diff --git a/lib/linguist/language.rb b/lib/linguist/language.rb index 6a44e2a2..9eb4df4d 100644 --- a/lib/linguist/language.rb +++ b/lib/linguist/language.rb @@ -9,12 +9,27 @@ module Linguist # Languages are defined in `lib/linguist/languages.yml`. class Language @languages = [] + @overrides = {} @index = {} @name_index = {} @alias_index = {} @extension_index = {} @filename_index = {} + # Internal: Test if extension maps to multiple Languages. + # + # Returns true or false. + def self.ambiguous?(extension) + @overrides.include?(extension) + end + + # Include?: Return overridden extensions. + # + # Returns extensions Array. + def self.overridden_extensions + @overrides.keys + end + # Internal: Create a new Language object # # attributes - A hash of attributes @@ -47,17 +62,21 @@ module Linguist warn "Extension is missing a '.': #{extension.inspect}" end - # All Language extensions should be unique. Warn if there is a - # duplicate. - if @extension_index.key?(extension) - warn "Duplicate extension: #{extension}" + unless ambiguous?(extension) + # Index the extension with a leading ".": ".rb" + @extension_index[extension] = language + + # Index the extension without a leading ".": "rb" + @extension_index[extension.sub(/^\./, '')] = language + end + end + + language.overrides.each do |extension| + if extension !~ /^\./ + warn "Extension is missing a '.': #{extension.inspect}" end - # Index the extension with a leading ".": ".rb" - @extension_index[extension] = language - - # Index the extension without a leading ".": "rb" - @extension_index[extension.sub(/^\./, '')] = language + @overrides[extension] = language end language.filenames.each do |filename| @@ -191,6 +210,7 @@ module Linguist # Set extensions or default to []. @extensions = attributes[:extensions] || [] + @overrides = attributes[:overrides] || [] @filenames = attributes[:filenames] || [] # Set popular, major, and searchable flags @@ -260,6 +280,11 @@ module Linguist # Returns the extensions Array attr_reader :extensions + # Internal: Get overridden extensions. + # + # Returns the extensions Array. + attr_reader :overrides + # Public: Get filenames # # Examples @@ -381,6 +406,7 @@ module Linguist :searchable => options.key?('searchable') ? options['searchable'] : true, :search_term => options['search_term'], :extensions => options['extensions'], + :overrides => options['overrides'], :filenames => options['filenames'], :major => options['major'], :popular => popular.include?(name) diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml index ffae58e5..7e158b94 100644 --- a/lib/linguist/languages.yml +++ b/lib/linguist/languages.yml @@ -9,6 +9,7 @@ # aliases - An Array of additional aliases (implicitly # includes name.downcase) # extension - An Array of associated extensions +# overrides - An Array of extensions that takes precedence over conflicts # major - Boolean flag major programming languages. Please leave # this option to GitHub staff to decide. # searchable - Boolean flag to enable searching (defaults to true) @@ -477,6 +478,7 @@ Markdown: Matlab: extensions: + - .m - .matlab Max/MSP: @@ -542,6 +544,8 @@ ObjDump: Objective-C: major: true + overrides: + - .m extensions: - .m - .mm @@ -580,6 +584,8 @@ Parrot Internal Representation: Perl: major: true + overrides: + - .pl extensions: - .pl - .ph @@ -593,6 +599,7 @@ Perl: Prolog: major: true extensions: + - .pl - .pro - .prolog @@ -620,6 +627,8 @@ Python traceback: R: major: true lexer: S + overrides: + - .r extensions: - .r - .R @@ -648,9 +657,10 @@ Raw token data: Rebol: lexer: REBOL extensions: - - .rebol + - .r - .r2 - .r3 + - .rebol Redcode: extensions: diff --git a/test/test_language.rb b/test/test_language.rb index 6a83dbba..8fc881ec 100644 --- a/test/test_language.rb +++ b/test/test_language.rb @@ -5,6 +5,17 @@ require 'test/unit' class TestLanguage < Test::Unit::TestCase include Linguist + def test_ambiguous_extensions + assert Language.ambiguous?('.m') + assert_equal Language['Objective-C'], Language.find_by_extension('m') + + assert Language.ambiguous?('.pl') + assert_equal Language['Perl'], Language.find_by_extension('pl') + + assert Language.ambiguous?('.r') + assert_equal Language['R'], Language.find_by_extension('r') + end + def test_lexer # Add an assertion to this list if you add/change any lexers # in languages.yml. Please keep this list alphabetized. @@ -383,7 +394,9 @@ class TestLanguage < Test::Unit::TestCase def test_find_all_by_extension Language.all.each do |language| language.extensions.each do |extension| - assert_equal language, Language.find_by_extension(extension) + unless Language.ambiguous?(extension) + assert_equal language, Language.find_by_extension(extension) + end end end end