PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/language_sniffer/language.rb

https://github.com/grosser/language_sniffer
Ruby | 345 lines | 135 code | 50 blank | 160 comment | 17 complexity | 62f8ea2aa91a68ed5ea48e6b5e1e4faf MD5 | raw file
  1. require 'yaml'
  2. module LanguageSniffer
  3. # Language names that are recognizable by GitHub. Defined languages
  4. # can be highlighted, searched and listed under the Top Languages page.
  5. #
  6. # Languages are defined in `lib/language_sniffer/languages.yml`.
  7. class Language
  8. @languages = []
  9. @overrides = {}
  10. @index = {}
  11. @name_index = {}
  12. @alias_index = {}
  13. @extension_index = {}
  14. @filename_index = {}
  15. # Valid Languages types
  16. TYPES = [:data, :markup, :programming]
  17. # Internal: Test if extension maps to multiple Languages.
  18. #
  19. # Returns true or false.
  20. def self.ambiguous?(extension)
  21. @overrides.include?(extension)
  22. end
  23. # Include?: Return overridden extensions.
  24. #
  25. # Returns extensions Array.
  26. def self.overridden_extensions
  27. @overrides.keys
  28. end
  29. # Internal: Create a new Language object
  30. #
  31. # attributes - A hash of attributes
  32. #
  33. # Returns a Language object
  34. def self.create(attributes = {})
  35. language = new(attributes)
  36. @languages << language
  37. # All Language names should be unique. Warn if there is a duplicate.
  38. if @name_index.key?(language.name)
  39. warn "Duplicate language name: #{language.name}"
  40. end
  41. # Language name index
  42. @index[language.name] = @name_index[language.name] = language
  43. language.aliases.each do |name|
  44. # All Language aliases should be unique. Warn if there is a duplicate.
  45. if @alias_index.key?(name)
  46. warn "Duplicate alias: #{name}"
  47. end
  48. @index[name] = @alias_index[name] = language
  49. end
  50. language.extensions.each do |extension|
  51. if extension !~ /^\./
  52. warn "Extension is missing a '.': #{extension.inspect}"
  53. end
  54. unless ambiguous?(extension)
  55. # Index the extension with a leading ".": ".rb"
  56. @extension_index[extension] = language
  57. # Index the extension without a leading ".": "rb"
  58. @extension_index[extension.sub(/^\./, '')] = language
  59. end
  60. end
  61. language.overrides.each do |extension|
  62. if extension !~ /^\./
  63. warn "Extension is missing a '.': #{extension.inspect}"
  64. end
  65. @overrides[extension] = language
  66. end
  67. language.filenames.each do |filename|
  68. @filename_index[filename] = language
  69. end
  70. language
  71. end
  72. # Public: Get all Languages
  73. #
  74. # Returns an Array of Languages
  75. def self.all
  76. @languages
  77. end
  78. # Public: Look up Language by its proper name.
  79. #
  80. # name - The String name of the Language
  81. #
  82. # Examples
  83. #
  84. # Language.find_by_name('Ruby')
  85. # # => #<Language name="Ruby">
  86. #
  87. # Returns the Language or nil if none was found.
  88. def self.find_by_name(name)
  89. @name_index[name]
  90. end
  91. # Public: Look up Language by one of its aliases.
  92. #
  93. # name - A String alias of the Language
  94. #
  95. # Examples
  96. #
  97. # Language.find_by_alias('cpp')
  98. # # => #<Language name="C++">
  99. #
  100. # Returns the Language or nil if none was found.
  101. def self.find_by_alias(name)
  102. @alias_index[name]
  103. end
  104. # Public: Look up Language by extension.
  105. #
  106. # extension - The extension String. May include leading "."
  107. #
  108. # Examples
  109. #
  110. # Language.find_by_extension('.rb')
  111. # # => #<Language name="Ruby">
  112. #
  113. # Returns the Language or nil if none was found.
  114. def self.find_by_extension(extension)
  115. @extension_index[extension]
  116. end
  117. # Public: Look up Language by filename.
  118. #
  119. # filename - The path String.
  120. #
  121. # Examples
  122. #
  123. # Language.find_by_filename('foo.rb')
  124. # # => #<Language name="Ruby">
  125. #
  126. # Returns the Language or nil if none was found.
  127. def self.find_by_filename(filename)
  128. basename, extname = File.basename(filename), File.extname(filename)
  129. @filename_index[basename] || @extension_index[extname]
  130. end
  131. # Public: Look up Language by its name.
  132. #
  133. # name - The String name of the Language
  134. #
  135. # Examples
  136. #
  137. # Language['Ruby']
  138. # # => #<Language name="Ruby">
  139. #
  140. # Language['ruby']
  141. # # => #<Language name="Ruby">
  142. #
  143. # Returns the Language or nil if none was found.
  144. def self.[](name)
  145. @index[name]
  146. end
  147. # Internal: Initialize a new Language
  148. #
  149. # attributes - A hash of attributes
  150. def initialize(attributes = {})
  151. # @name is required
  152. @name = attributes[:name] || raise(ArgumentError, "missing name")
  153. # Set type
  154. @type = attributes[:type] ? attributes[:type].to_sym : nil
  155. # Set aliases
  156. @aliases = [default_alias_name] + (attributes[:aliases] || [])
  157. # Set pygments lexer
  158. @lexer = attributes[:lexer] || name
  159. # Set legacy search term
  160. @search_term = attributes[:search_term] || default_alias_name
  161. # Set extensions or default to [].
  162. @extensions = attributes[:extensions] || []
  163. @overrides = attributes[:overrides] || []
  164. @filenames = attributes[:filenames] || []
  165. @primary_extension = attributes[:primary_extension] || default_primary_extension || extensions.first
  166. # Prepend primary extension unless its already included
  167. if primary_extension && !extensions.include?(primary_extension)
  168. @extensions = [primary_extension] + extensions
  169. end
  170. # If group name is set, save the name so we can lazy load it later
  171. if attributes[:group_name]
  172. @group = nil
  173. @group_name = attributes[:group_name]
  174. # Otherwise we can set it to self now
  175. else
  176. @group = self
  177. end
  178. end
  179. # Public: Get proper name
  180. #
  181. # Examples
  182. #
  183. # # => "Ruby"
  184. # # => "Python"
  185. # # => "Perl"
  186. #
  187. # Returns the name String
  188. attr_reader :name
  189. # Public: Get type.
  190. #
  191. # Returns a type Symbol or nil.
  192. attr_reader :type
  193. # Public: Get pygments lexer name.
  194. #
  195. # Returns a lexer name or nil.
  196. attr_reader :lexer
  197. # Public: Get aliases
  198. #
  199. # Examples
  200. #
  201. # Language['C++'].aliases
  202. # # => ["cpp"]
  203. #
  204. # Returns an Array of String names
  205. attr_reader :aliases
  206. # Deprecated: Get code search term
  207. #
  208. # Examples
  209. #
  210. # # => "ruby"
  211. # # => "python"
  212. # # => "perl"
  213. #
  214. # Returns the name String
  215. attr_reader :search_term
  216. # Public: Get extensions
  217. #
  218. # Examples
  219. #
  220. # # => ['.rb', '.rake', ...]
  221. #
  222. # Returns the extensions Array
  223. attr_reader :extensions
  224. # Deprecated: Get primary extension
  225. #
  226. # Defaults to the first extension but can be overriden
  227. # in the languages.yml.
  228. #
  229. # The primary extension can not be nil. Tests should verify this.
  230. #
  231. # This attribute is only used by app/helpers/gists_helper.rb for
  232. # creating the language dropdown. It really should be using `name`
  233. # instead. Would like to drop primary extension.
  234. #
  235. # Returns the extension String.
  236. attr_reader :primary_extension
  237. # Internal: Get overridden extensions.
  238. #
  239. # Returns the extensions Array.
  240. attr_reader :overrides
  241. # Public: Get filenames
  242. #
  243. # Examples
  244. #
  245. # # => ['Rakefile', ...]
  246. #
  247. # Returns the extensions Array
  248. attr_reader :filenames
  249. # Internal: Get default alias name
  250. #
  251. # Returns the alias name String
  252. def default_alias_name
  253. name.downcase.gsub(/\s/, '-')
  254. end
  255. # Internal: Get default primary extension.
  256. #
  257. # Returns the extension String.
  258. def default_primary_extension
  259. extensions.first
  260. end
  261. # Public: Get Language group
  262. #
  263. # Returns a Language
  264. def group
  265. @group ||= Language.find_by_name(@group_name)
  266. end
  267. # Public: Return name as String representation
  268. def to_s
  269. name
  270. end
  271. def ==(other)
  272. eql?(other)
  273. end
  274. def eql?(other)
  275. equal?(other)
  276. end
  277. def hash
  278. name.hash
  279. end
  280. end
  281. YAML.load_file(File.expand_path("../languages.yml", __FILE__)).each do |name, options|
  282. Language.create(
  283. :name => name,
  284. :type => options['type'],
  285. :aliases => options['aliases'],
  286. :lexer => options['lexer'],
  287. :group_name => options['group'],
  288. :search_term => options['search_term'],
  289. :extensions => options['extensions'],
  290. :primary_extension => options['primary_extension'],
  291. :overrides => options['overrides'],
  292. :filenames => options['filenames']
  293. )
  294. end
  295. end