diff --git a/bin/git-linguist b/bin/git-linguist index 37d46b64..9e525bb5 100755 --- a/bin/git-linguist +++ b/bin/git-linguist @@ -111,7 +111,7 @@ def git_linguist(args) parser.parse!(args) git_dir = `git rev-parse --git-dir`.strip - raise "git-linguist must be ran in a Git repository" unless $?.success? + raise "git-linguist must be ran in a Git repository (#{Dir.pwd})" unless $?.success? wrapper = GitLinguist.new(git_dir, commit, incremental) case args.pop diff --git a/lib/linguist.rb b/lib/linguist.rb index 23553af9..544bb250 100644 --- a/lib/linguist.rb +++ b/lib/linguist.rb @@ -9,8 +9,85 @@ require 'linguist/shebang' require 'linguist/version' class << Linguist + # Public: Detects the Language of the blob. + # + # blob - an object that includes the Linguist `BlobHelper` interface; + # see Linguist::LazyBlob and Linguist::FileBlob for examples + # + # Returns Language or nil. + def detect(blob) + # Bail early if the blob is binary or empty. + return nil if blob.likely_binary? || blob.binary? || blob.empty? + + Linguist.instrument("linguist.detection", :blob => blob) do + # Call each strategy until one candidate is returned. + languages = [] + returning_strategy = nil + + STRATEGIES.each do |strategy| + returning_strategy = strategy + candidates = Linguist.instrument("linguist.strategy", :blob => blob, :strategy => strategy, :candidates => languages) do + strategy.call(blob, languages) + end + if candidates.size == 1 + languages = candidates + break + elsif candidates.size > 1 + # More than one candidate was found, pass them to the next strategy. + languages = candidates + else + # No candidates, try the next strategy + end + end + + Linguist.instrument("linguist.detected", :blob => blob, :strategy => returning_strategy, :language => languages.first) + + languages.first + end + end + + # Internal: The strategies used to detect the language of a file. + # + # A strategy is an object that has a `.call` method that takes two arguments: + # + # blob - An object that quacks like a blob. + # languages - An Array of candidate Language objects that were returned by the + #     previous strategy. + # + # A strategy should return an Array of Language candidates. + # + # Strategies are called in turn until a single Language is returned. + STRATEGIES = [ + Linguist::Strategy::Modeline, + Linguist::Shebang, + Linguist::Strategy::Filename, + Linguist::Heuristics, + Linguist::Classifier + ] + + # Public: Set an instrumenter. + # + # class CustomInstrumenter + # def instrument(name, payload = {}) + # warn "Instrumenting #{name}: #{payload[:blob]}" + # end + # end + # + # Linguist.instrumenter = CustomInstrumenter + # + # The instrumenter must conform to the `ActiveSupport::Notifications` + # interface, which defines `#instrument` and accepts: + # + # name - the String name of the event (e.g. "linguist.detected") + # payload - a Hash of the exception context. attr_accessor :instrumenter + # Internal: Perform instrumentation on a block + # + # Linguist.instrument("linguist.dosomething", :blob => blob) do + # # logic to instrument here. + # end + # def instrument(*args, &bk) if instrumenter instrumenter.instrument(*args, &bk) @@ -18,4 +95,5 @@ class << Linguist yield end end + end diff --git a/lib/linguist/blob_helper.rb b/lib/linguist/blob_helper.rb index 01a567e0..a1565646 100644 --- a/lib/linguist/blob_helper.rb +++ b/lib/linguist/blob_helper.rb @@ -6,7 +6,7 @@ require 'yaml' module Linguist # DEPRECATED Avoid mixing into Blob classes. Prefer functional interfaces - # like `Language.detect` over `Blob#language`. Functions are much easier to + # like `Linguist.detect` over `Blob#language`. Functions are much easier to # cache and compose. # # Avoid adding additional bloat to this module. @@ -325,7 +325,7 @@ module Linguist # # Returns a Language or nil if none is detected def language - @language ||= Language.detect(self) + @language ||= Linguist.detect(self) end # Internal: Get the TextMate compatible scope for the blob diff --git a/lib/linguist/heuristics.rb b/lib/linguist/heuristics.rb index 5c27ca03..cbbcd03a 100644 --- a/lib/linguist/heuristics.rb +++ b/lib/linguist/heuristics.rb @@ -313,6 +313,14 @@ module Linguist end end + disambiguate ".props" do |data| + if /^(\s*)( blob) do - # Call each strategy until one candidate is returned. - languages = [] - returning_strategy = nil - - STRATEGIES.each do |strategy| - returning_strategy = strategy - candidates = Linguist.instrument("linguist.strategy", :blob => blob, :strategy => strategy, :candidates => languages) do - strategy.call(blob, languages) - end - if candidates.size == 1 - languages = candidates - break - elsif candidates.size > 1 - # More than one candidate was found, pass them to the next strategy. - languages = candidates - else - # No candidates, try the next strategy - end - end - - Linguist.instrument("linguist.detected", :blob => blob, :strategy => returning_strategy, :language => languages.first) - - languages.first - end + warn "[DEPRECATED] `Linguist::Language.detect` is deprecated. Use `Linguist.detect`. #{caller[0]}" + Linguist.detect(blob) end # Public: Get all Languages diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml index 1163fbf8..624427cb 100755 --- a/lib/linguist/languages.yml +++ b/lib/linguist/languages.yml @@ -3863,6 +3863,7 @@ XML: - .osm - .plist - .pluginspec + - .props - .ps1xml - .psc1 - .pt diff --git a/lib/linguist/version.rb b/lib/linguist/version.rb index 7bb4af7b..2e14f12f 100644 --- a/lib/linguist/version.rb +++ b/lib/linguist/version.rb @@ -1,3 +1,3 @@ module Linguist - VERSION = "4.7.6" + VERSION = "4.8.0" end diff --git a/samples/PLpgSQL/procedures.sql b/samples/PLpgSQL/procedures.sql new file mode 100644 index 00000000..51b6bc94 --- /dev/null +++ b/samples/PLpgSQL/procedures.sql @@ -0,0 +1,31 @@ +load 'plpgsql'; +load 'plpgsql_lint'; + +DROP FUNCTION IF EXISTS list_sites(); +CREATE OR REPLACE FUNCTION list_sites() RETURNS TABLE (fc json) AS +$func$ +BEGIN +RETURN QUERY SELECT row_to_json(feat_col) FROM ( + SELECT 'FeatureCollection' AS type, array_to_json(array_agg(feat)) AS features FROM ( + SELECT DISTINCT ON (new_id) 'Feature' AS type, ST_ASGeoJSON(loc.geom)::json AS geometry, row_to_json( + (SELECT prop FROM (SELECT new_id) AS prop)) AS properties FROM location loc) AS feat) AS feat_col; +END; +$func$ LANGUAGE plpgsql; + + +DROP FUNCTION IF EXISTS get_observations(character varying, integer); +CREATE OR REPLACE FUNCTION get_observations(kind varchar, site_id integer) RETURNS TABLE (fc json) AS +$func$ +BEGIN + IF kind = 'o2_abs' THEN + RETURN QUERY SELECT array_to_json(array_agg(row_to_json(obs))) FROM ( + SELECT observation_date AS date, o2_abs AS value FROM oxygen WHERE new_id = site_id) AS obs; + ELSIF kind = 'o2_rel' THEN + RETURN QUERY SELECT array_to_json(array_agg(row_to_json(obs))) FROM ( + SELECT observation_date AS date, o2_rel AS value FROM oxygen WHERE new_id = site_id) AS obs; + ELSIF kind = 'temp' THEN + RETURN QUERY SELECT array_to_json(array_agg(row_to_json(obs))) FROM ( + SELECT observation_date AS date, temp AS value FROM oxygen WHERE new_id = site_id) AS obs; + END IF; +END; +$func$ LANGUAGE plpgsql; diff --git a/samples/XML/Default.props b/samples/XML/Default.props new file mode 100644 index 00000000..f6190b63 --- /dev/null +++ b/samples/XML/Default.props @@ -0,0 +1,29 @@ + + + + + + + + + Windows Phone Silverlight 8.1 + 6.3 + v120 + + <_PlatformToolsetFriendlyNameFor_v120>Windows Phone Silverlight 8.1 (v120) + <_PlatformToolsetShortNameFor_v120>Windows Phone Silverlight 8.1 + + + + + diff --git a/test/test_heuristics.rb b/test/test_heuristics.rb index 7021e20b..301ca9d7 100644 --- a/test/test_heuristics.rb +++ b/test/test_heuristics.rb @@ -36,7 +36,7 @@ class TestHeuristcs < Minitest::Test def test_detect_still_works_if_nothing_matches blob = Linguist::FileBlob.new(File.join(samples_path, "Objective-C/hello.m")) - match = Language.detect(blob) + match = Linguist.detect(blob) assert_equal Language["Objective-C"], match end @@ -189,6 +189,16 @@ class TestHeuristcs < Minitest::Test }) end + # Candidate languages = ["SQL", "PLpgSQL", "SQLPL", "PLSQL"] + def test_sql_by_heuristics + assert_heuristics({ + "SQL" => ["SQL/create_stuff.sql", "SQL/db.sql", "SQL/dual.sql"], + "PLpgSQL" => all_fixtures("PLpgSQL", "*.sql"), + "SQLPL" => ["SQLPL/trigger.sql"], + "PLSQL" => all_fixtures("PLSQL", "*.sql") + }) + end + # Candidate languages = ["Perl", "Perl6"] def test_t_perl_by_heuristics assert_heuristics({ diff --git a/test/test_instrumentation.rb b/test/test_instrumentation.rb index ab0615e5..d2eedad8 100644 --- a/test/test_instrumentation.rb +++ b/test/test_instrumentation.rb @@ -28,7 +28,7 @@ class TestInstrumentation < Minitest::Test def test_detection_instrumentation_with_binary_blob binary_blob = fixture_blob("Binary/octocat.ai") - Language.detect(binary_blob) + Linguist.detect(binary_blob) # Shouldn't instrument this (as it's binary) assert_equal 0, Linguist.instrumenter.events.size @@ -36,7 +36,7 @@ class TestInstrumentation < Minitest::Test def test_modeline_instrumentation blob = fixture_blob("Data/Modelines/ruby") - Language.detect(blob) + Linguist.detect(blob) detect_event = Linguist.instrumenter.events.last detect_event_payload = detect_event[:args].first diff --git a/vendor/grammars/AutoHotkey b/vendor/grammars/AutoHotkey index 4da62de2..d31adb91 160000 --- a/vendor/grammars/AutoHotkey +++ b/vendor/grammars/AutoHotkey @@ -1 +1 @@ -Subproject commit 4da62de23dc705bf9b95e76cf5e8e51aa1e68fea +Subproject commit d31adb9184fb14f970995c12ad3a29f288c9bc0f diff --git a/vendor/grammars/Elm.tmLanguage b/vendor/grammars/Elm.tmLanguage index 437033bd..155ce91c 160000 --- a/vendor/grammars/Elm.tmLanguage +++ b/vendor/grammars/Elm.tmLanguage @@ -1 +1 @@ -Subproject commit 437033bd48350b49bc0dfa734206bfa0ba5de337 +Subproject commit 155ce91c81a3b98fdf2785fe69b5460a3075d9f0 diff --git a/vendor/grammars/Lean.tmbundle b/vendor/grammars/Lean.tmbundle index 943ac84b..a1a3818e 160000 --- a/vendor/grammars/Lean.tmbundle +++ b/vendor/grammars/Lean.tmbundle @@ -1 +1 @@ -Subproject commit 943ac84bf69bfbcab54eeeebba6d66c90ed700ff +Subproject commit a1a3818ecfdd1365ec01ac3894106b5a70b455ac diff --git a/vendor/grammars/MagicPython b/vendor/grammars/MagicPython index 82c76aff..7d07d6f5 160000 --- a/vendor/grammars/MagicPython +++ b/vendor/grammars/MagicPython @@ -1 +1 @@ -Subproject commit 82c76aff704192fb9ed1f505360635f575f13b5a +Subproject commit 7d07d6f5b052785603e1e8cfbf771ccd442d2975 diff --git a/vendor/grammars/NimLime b/vendor/grammars/NimLime index 4db349dd..0c6c6207 160000 --- a/vendor/grammars/NimLime +++ b/vendor/grammars/NimLime @@ -1 +1 @@ -Subproject commit 4db349dda5219a37e99a0375e2a5d8a001fbf20e +Subproject commit 0c6c6207a62929054d50f242c4f0b3e32084e2f6 diff --git a/vendor/grammars/Vala-TMBundle b/vendor/grammars/Vala-TMBundle index 1c5ebb62..935bd21c 160000 --- a/vendor/grammars/Vala-TMBundle +++ b/vendor/grammars/Vala-TMBundle @@ -1 +1 @@ -Subproject commit 1c5ebb62d11e01d145b414355de72d4de8758ed8 +Subproject commit 935bd21c13068158435f64b3dbd8423c8d812c92 diff --git a/vendor/grammars/apache.tmbundle b/vendor/grammars/apache.tmbundle index a4a494e8..a12e8955 160000 --- a/vendor/grammars/apache.tmbundle +++ b/vendor/grammars/apache.tmbundle @@ -1 +1 @@ -Subproject commit a4a494e8455ca98bde8591a7f068c3384babb09a +Subproject commit a12e895533700b0200cca20f169be63727efff8c diff --git a/vendor/grammars/api-blueprint-sublime-plugin b/vendor/grammars/api-blueprint-sublime-plugin index 550417b9..076ee9bd 160000 --- a/vendor/grammars/api-blueprint-sublime-plugin +++ b/vendor/grammars/api-blueprint-sublime-plugin @@ -1 +1 @@ -Subproject commit 550417b9bbbd833b57b5917b48ca0fef9ed52190 +Subproject commit 076ee9bd62d68ee07831c87f2e7c7eeec732dd0f diff --git a/vendor/grammars/atom-language-stan b/vendor/grammars/atom-language-stan index f8d855ea..2fa2745d 160000 --- a/vendor/grammars/atom-language-stan +++ b/vendor/grammars/atom-language-stan @@ -1 +1 @@ -Subproject commit f8d855eab960b4dd140c0f469a809401544850b8 +Subproject commit 2fa2745da7ab9de0ba42a9743d1942becb5e4c32 diff --git a/vendor/grammars/ats.sublime b/vendor/grammars/ats.sublime index a3f24abb..fd6bd223 160000 --- a/vendor/grammars/ats.sublime +++ b/vendor/grammars/ats.sublime @@ -1 +1 @@ -Subproject commit a3f24abbe7043adc0ad798711467edae33cf89f0 +Subproject commit fd6bd223d17e384f2a7dc93ce01c6e786fd452c2 diff --git a/vendor/grammars/chapel-tmbundle b/vendor/grammars/chapel-tmbundle index 469476b2..d6c9e926 160000 --- a/vendor/grammars/chapel-tmbundle +++ b/vendor/grammars/chapel-tmbundle @@ -1 +1 @@ -Subproject commit 469476b285adf6c4a09973fd12e97ec831afd050 +Subproject commit d6c9e926e78b4b8fd1631fc059307a87fb73d33e diff --git a/vendor/grammars/factor b/vendor/grammars/factor index 97d1ec75..d99c9e16 160000 --- a/vendor/grammars/factor +++ b/vendor/grammars/factor @@ -1 +1 @@ -Subproject commit 97d1ec759eb9fa2ace83c62685b6b36faec05981 +Subproject commit d99c9e16632d2eb1e5a8862182529d3dcc6b7c56 diff --git a/vendor/grammars/gap-tmbundle b/vendor/grammars/gap-tmbundle index 52c8fafb..cf05fa8d 160000 --- a/vendor/grammars/gap-tmbundle +++ b/vendor/grammars/gap-tmbundle @@ -1 +1 @@ -Subproject commit 52c8fafb664fb7909223f92403e26fe3bfde0cdc +Subproject commit cf05fa8df13f8b1f46fc07e6c17270d899a69926 diff --git a/vendor/grammars/haxe-sublime-bundle b/vendor/grammars/haxe-sublime-bundle index 94cc8eea..e9559a2c 160000 --- a/vendor/grammars/haxe-sublime-bundle +++ b/vendor/grammars/haxe-sublime-bundle @@ -1 +1 @@ -Subproject commit 94cc8eea31127365556ef9bbb1db78ef6bfdd2e5 +Subproject commit e9559a2c538c83d5dca09e410915834e9bdcd3cb diff --git a/vendor/grammars/jade-tmbundle b/vendor/grammars/jade-tmbundle index d27b61d1..81093433 160000 --- a/vendor/grammars/jade-tmbundle +++ b/vendor/grammars/jade-tmbundle @@ -1 +1 @@ -Subproject commit d27b61d1780ff594b43d9ad04a9e896ea92b393b +Subproject commit 81093433d6c371657c237ed8be3c47f3e98e3a66 diff --git a/vendor/grammars/kotlin-sublime-package b/vendor/grammars/kotlin-sublime-package index 3ddc52e8..535967fd 160000 --- a/vendor/grammars/kotlin-sublime-package +++ b/vendor/grammars/kotlin-sublime-package @@ -1 +1 @@ -Subproject commit 3ddc52e8dbc8bef1f1a534b94bd0f688bc2a4422 +Subproject commit 535967fd2c53d299289d3b045097ea08dbe9764f diff --git a/vendor/grammars/language-babel b/vendor/grammars/language-babel index e2fd09d7..5f63df46 160000 --- a/vendor/grammars/language-babel +++ b/vendor/grammars/language-babel @@ -1 +1 @@ -Subproject commit e2fd09d7d9caf1f336b3a35581e55387723dcbf3 +Subproject commit 5f63df46ccae53d3aa07e41e647e8e91f18177b6 diff --git a/vendor/grammars/language-clojure b/vendor/grammars/language-clojure index a0193ad2..fa482c39 160000 --- a/vendor/grammars/language-clojure +++ b/vendor/grammars/language-clojure @@ -1 +1 @@ -Subproject commit a0193ad2a9797033649e665083f09249d2d098fc +Subproject commit fa482c39a323624189240910cd7eb81dfd97e445 diff --git a/vendor/grammars/language-csharp b/vendor/grammars/language-csharp index f635e67e..96ab79f4 160000 --- a/vendor/grammars/language-csharp +++ b/vendor/grammars/language-csharp @@ -1 +1 @@ -Subproject commit f635e67edef4e2fe68c3526e1ad26ed66f01aa62 +Subproject commit 96ab79f45bfb6875c9aa2ac702934a385df35de3 diff --git a/vendor/grammars/language-gfm b/vendor/grammars/language-gfm index 298a8a3e..1472c976 160000 --- a/vendor/grammars/language-gfm +++ b/vendor/grammars/language-gfm @@ -1 +1 @@ -Subproject commit 298a8a3eb180f1fa6b8a8bc77c2147e355c8cafd +Subproject commit 1472c976c5c5cbc842bb7cce7fc6e19de9b926a6 diff --git a/vendor/grammars/language-javascript b/vendor/grammars/language-javascript index f68e4bfe..b28af094 160000 --- a/vendor/grammars/language-javascript +++ b/vendor/grammars/language-javascript @@ -1 +1 @@ -Subproject commit f68e4bfe54a3b9d16450223f401d2fb16453897f +Subproject commit b28af094cc31a7e2423c474845e506ce33aa3a57 diff --git a/vendor/grammars/language-renpy b/vendor/grammars/language-renpy index cc2f1c69..c259c5d3 160000 --- a/vendor/grammars/language-renpy +++ b/vendor/grammars/language-renpy @@ -1 +1 @@ -Subproject commit cc2f1c69f0b1c1d121aa5648422fc70d86dca7cf +Subproject commit c259c5d3acc0a27dc2a28abc2a2229acf9adde1b diff --git a/vendor/grammars/latex.tmbundle b/vendor/grammars/latex.tmbundle index 82986b93..bec21c8b 160000 --- a/vendor/grammars/latex.tmbundle +++ b/vendor/grammars/latex.tmbundle @@ -1 +1 @@ -Subproject commit 82986b93a4f4ae7aab52445d8b7742b9af635d05 +Subproject commit bec21c8bcd3e977c157e41f8e4d3f6c53862dbe8 diff --git a/vendor/grammars/mathematica-tmbundle b/vendor/grammars/mathematica-tmbundle index 3b4b826d..5067a25b 160000 --- a/vendor/grammars/mathematica-tmbundle +++ b/vendor/grammars/mathematica-tmbundle @@ -1 +1 @@ -Subproject commit 3b4b826dbe60f1dba4484fdeacf0047681b9b82e +Subproject commit 5067a25b9b1c2b9a8bb0ed9fcfd63c73ff11277e diff --git a/vendor/grammars/sas.tmbundle b/vendor/grammars/sas.tmbundle index 3759a197..ba5c7462 160000 --- a/vendor/grammars/sas.tmbundle +++ b/vendor/grammars/sas.tmbundle @@ -1 +1 @@ -Subproject commit 3759a19719d3c4c4979087be12adbcaa02a7bca3 +Subproject commit ba5c74624c477d657698ffd515fa6f670ee147b9 diff --git a/vendor/grammars/smali-sublime b/vendor/grammars/smali-sublime index 28a43364..60a1fdb3 160000 --- a/vendor/grammars/smali-sublime +++ b/vendor/grammars/smali-sublime @@ -1 +1 @@ -Subproject commit 28a4336421bff627d76c8dc27b991a4ed53a6747 +Subproject commit 60a1fdb3442cd7082036f1a02e403a5f725ed837 diff --git a/vendor/grammars/sublime-nginx b/vendor/grammars/sublime-nginx index fcf644ec..e72eb758 160000 --- a/vendor/grammars/sublime-nginx +++ b/vendor/grammars/sublime-nginx @@ -1 +1 @@ -Subproject commit fcf644ecea021ab8a6bc171f415f8df0b005b31e +Subproject commit e72eb758149317de9b79a14aee91e778b3dbd929 diff --git a/vendor/grammars/sublime-robot-plugin b/vendor/grammars/sublime-robot-plugin index bf5dc7fa..07069ebf 160000 --- a/vendor/grammars/sublime-robot-plugin +++ b/vendor/grammars/sublime-robot-plugin @@ -1 +1 @@ -Subproject commit bf5dc7fa9f431f4990a02518f89fe45a2beaa5e8 +Subproject commit 07069ebf20c82663eee24e4c7d82cccca020e8e9 diff --git a/vendor/grammars/sublime-rust b/vendor/grammars/sublime-rust index 621e4f61..f75f2b10 160000 --- a/vendor/grammars/sublime-rust +++ b/vendor/grammars/sublime-rust @@ -1 +1 @@ -Subproject commit 621e4f6117531d8fe299eb5584a6be766df1822e +Subproject commit f75f2b102632eb2f29124c055cdcc9fa7776886d diff --git a/vendor/grammars/sublime-typescript b/vendor/grammars/sublime-typescript index 26fd717a..2da61f59 160000 --- a/vendor/grammars/sublime-typescript +++ b/vendor/grammars/sublime-typescript @@ -1 +1 @@ -Subproject commit 26fd717a79d1984e76bbe6d958c5c4bbf0179049 +Subproject commit 2da61f59d231b5442e1a7136aedcb9705ad099e2 diff --git a/vendor/grammars/sublime_cobol b/vendor/grammars/sublime_cobol index 3d2b6dbc..7c60c108 160000 --- a/vendor/grammars/sublime_cobol +++ b/vendor/grammars/sublime_cobol @@ -1 +1 @@ -Subproject commit 3d2b6dbcd1b27023150ff9d8ab47953706d070b8 +Subproject commit 7c60c10849c45a55203b352dcf0b212113a61caf diff --git a/vendor/grammars/vue-syntax-highlight b/vendor/grammars/vue-syntax-highlight index f20c9bab..fbcccaee 160000 --- a/vendor/grammars/vue-syntax-highlight +++ b/vendor/grammars/vue-syntax-highlight @@ -1 +1 @@ -Subproject commit f20c9bab7e71738f421e6edc1aab8839ee05d85a +Subproject commit fbcccaee1043af3e6269a1e674fc89ede2cc941b