mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	Revise patterns for Vim modeline detection
The current expressions fail to match certain permutations of options:
    vim: noexpandtab: ft=javascript:
    vim: titlestring=foo\ ft=notperl ft=javascript:
Version-specific modelines are also unaccounted for:
    vim600: set foldmethod=marker ft=javascript:   # >= Vim 6.0
    vim<600: set ft=javascript:                    # <  Vim 6.0
See http://vimdoc.sourceforge.net/htmldoc/options.html#modeline
			
			
This commit is contained in:
		@@ -2,18 +2,67 @@ module Linguist
 | 
				
			|||||||
  module Strategy
 | 
					  module Strategy
 | 
				
			||||||
    class Modeline
 | 
					    class Modeline
 | 
				
			||||||
      EMACS_MODELINE = /-\*-\s*(?:(?!mode)[\w-]+\s*:\s*(?:[\w+-]+)\s*;?\s*)*(?:mode\s*:)?\s*([\w+-]+)\s*(?:;\s*(?!mode)[\w-]+\s*:\s*[\w+-]+\s*)*;?\s*-\*-/i
 | 
					      EMACS_MODELINE = /-\*-\s*(?:(?!mode)[\w-]+\s*:\s*(?:[\w+-]+)\s*;?\s*)*(?:mode\s*:)?\s*([\w+-]+)\s*(?:;\s*(?!mode)[\w-]+\s*:\s*[\w+-]+\s*)*;?\s*-\*-/i
 | 
				
			||||||
 | 
					      VIM_MODELINE   = /
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # First form vim modeline
 | 
					        # Start modeline. Could be `vim:`, `vi:` or `ex:`
 | 
				
			||||||
      # [text]{white}{vi:|vim:|ex:}[white]{options}
 | 
					        (?:
 | 
				
			||||||
      # ex: 'vim: syntax=ruby'
 | 
					          (?:\s|^)
 | 
				
			||||||
      VIM_MODELINE_1 = /(?:vim|vi|ex):\s*(?:ft|filetype|syntax)=(\w+)\s?/i
 | 
					          vi
 | 
				
			||||||
 | 
					          (?:m[<=>]?\d+|m)? # Version-specific modeline
 | 
				
			||||||
 | 
					          |
 | 
				
			||||||
 | 
					          (?!^)\s
 | 
				
			||||||
 | 
					          ex
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Second form vim modeline (compatible with some versions of Vi)
 | 
					        # If the option-list begins with `set ` or `se `, it indicates an alternative
 | 
				
			||||||
      # [text]{white}{vi:|vim:|Vim:|ex:}[white]se[t] {options}:[text]
 | 
					        # modeline syntax partly-compatible with older versions of Vi. Here, the colon
 | 
				
			||||||
      # ex: 'vim set syntax=ruby:'
 | 
					        # serves as a terminator for an option sequence, delimited by whitespace.
 | 
				
			||||||
      VIM_MODELINE_2 = /(?:vim|vi|Vim|ex):\s*se(?:t)?.*\s(?:ft|filetype|syntax)=(\w+)\s?.*:/i
 | 
					        (?=
 | 
				
			||||||
 | 
					          # So we have to ensure the modeline ends with a colon
 | 
				
			||||||
 | 
					          : (?=\s* set? \s [^\n:]+ :) |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      MODELINES = [EMACS_MODELINE, VIM_MODELINE_1, VIM_MODELINE_2]
 | 
					          # Otherwise, it isn't valid syntax and should be ignored
 | 
				
			||||||
 | 
					          : (?!\s* set? \s)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Possible (unrelated) `option=value` pairs to skip past
 | 
				
			||||||
 | 
					        (?:
 | 
				
			||||||
 | 
					          # Option separator. Vim uses whitespace or colons to separate options (except if
 | 
				
			||||||
 | 
					          # the alternate "vim: set " form is used, where only whitespace is used)
 | 
				
			||||||
 | 
					          (?:
 | 
				
			||||||
 | 
					            \s
 | 
				
			||||||
 | 
					            |
 | 
				
			||||||
 | 
					            \s* : \s* # Note that whitespace around colons is accepted too:
 | 
				
			||||||
 | 
					          )           # vim: noai :  ft=ruby:noexpandtab
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          # Option's name. All recognised Vim options have an alphanumeric form.
 | 
				
			||||||
 | 
					          \w*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          # Possible value. Not every option takes an argument.
 | 
				
			||||||
 | 
					          (?:
 | 
				
			||||||
 | 
					            # Whitespace between name and value is allowed: `vim: ft   =ruby`
 | 
				
			||||||
 | 
					            \s*=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Option's value. Might be blank; `vim: ft= ` says "use no filetype".
 | 
				
			||||||
 | 
					            (?:
 | 
				
			||||||
 | 
					              [^\\\s] # Beware of escaped characters: titlestring=\ ft=ruby
 | 
				
			||||||
 | 
					              |       # will be read by Vim as { titlestring: " ft=ruby" }.
 | 
				
			||||||
 | 
					              \\.
 | 
				
			||||||
 | 
					            )*
 | 
				
			||||||
 | 
					          )?
 | 
				
			||||||
 | 
					        )*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # The actual filetype declaration
 | 
				
			||||||
 | 
					        [\s:] (?:filetype|ft|syntax) \s*=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Language's name
 | 
				
			||||||
 | 
					        (\w+)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Ensure it's followed by a legal separator
 | 
				
			||||||
 | 
					        (?=\s|:|$)
 | 
				
			||||||
 | 
					      /xi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      MODELINES = [EMACS_MODELINE, VIM_MODELINE]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Scope of the search for modelines
 | 
					      # Scope of the search for modelines
 | 
				
			||||||
      # Number of lines to check at the beginning and at the end of the file
 | 
					      # Number of lines to check at the beginning and at the end of the file
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								test/fixtures/Data/Modelines/iamjs.pl
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								test/fixtures/Data/Modelines/iamjs.pl
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					# vim: noexpandtab: ft=javascript
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"It's JavaScript, baby";
 | 
				
			||||||
							
								
								
									
										4
									
								
								test/fixtures/Data/Modelines/iamjs2.pl
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								test/fixtures/Data/Modelines/iamjs2.pl
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					# vim:noexpandtab titlestring=hi\|there\\\ ft=perl ts=4
 | 
				
			||||||
 | 
					# vim:noexpandtab titlestring=hi|there\\ ft=javascript ts=4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"Still JavaScript, bruh";
 | 
				
			||||||
@@ -30,6 +30,8 @@ class TestModelines < Minitest::Test
 | 
				
			|||||||
    assert_modeline Language["Text"], fixture_blob("Data/Modelines/fundamentalEmacs.c")
 | 
					    assert_modeline Language["Text"], fixture_blob("Data/Modelines/fundamentalEmacs.c")
 | 
				
			||||||
    assert_modeline Language["Prolog"], fixture_blob("Data/Modelines/not_perl.pl")
 | 
					    assert_modeline Language["Prolog"], fixture_blob("Data/Modelines/not_perl.pl")
 | 
				
			||||||
    assert_modeline Language["Smalltalk"], fixture_blob("Data/Modelines/example_smalltalk.md")
 | 
					    assert_modeline Language["Smalltalk"], fixture_blob("Data/Modelines/example_smalltalk.md")
 | 
				
			||||||
 | 
					    assert_modeline Language["JavaScript"], fixture_blob("Data/Modelines/iamjs.pl")
 | 
				
			||||||
 | 
					    assert_modeline Language["JavaScript"], fixture_blob("Data/Modelines/iamjs2.pl")
 | 
				
			||||||
    assert_modeline Language["PHP"], fixture_blob("Data/Modelines/iamphp.inc")
 | 
					    assert_modeline Language["PHP"], fixture_blob("Data/Modelines/iamphp.inc")
 | 
				
			||||||
    assert_modeline nil, sample_blob("C/main.c")
 | 
					    assert_modeline nil, sample_blob("C/main.c")
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -51,6 +53,8 @@ class TestModelines < Minitest::Test
 | 
				
			|||||||
    assert_equal Language["Text"], fixture_blob("Data/Modelines/fundamentalEmacs.c").language
 | 
					    assert_equal Language["Text"], fixture_blob("Data/Modelines/fundamentalEmacs.c").language
 | 
				
			||||||
    assert_equal Language["Prolog"], fixture_blob("Data/Modelines/not_perl.pl").language
 | 
					    assert_equal Language["Prolog"], fixture_blob("Data/Modelines/not_perl.pl").language
 | 
				
			||||||
    assert_equal Language["Smalltalk"], fixture_blob("Data/Modelines/example_smalltalk.md").language
 | 
					    assert_equal Language["Smalltalk"], fixture_blob("Data/Modelines/example_smalltalk.md").language
 | 
				
			||||||
 | 
					    assert_equal Language["JavaScript"], fixture_blob("Data/Modelines/iamjs.pl").language
 | 
				
			||||||
 | 
					    assert_equal Language["JavaScript"], fixture_blob("Data/Modelines/iamjs2.pl").language
 | 
				
			||||||
    assert_equal Language["PHP"], fixture_blob("Data/Modelines/iamphp.inc").language
 | 
					    assert_equal Language["PHP"], fixture_blob("Data/Modelines/iamphp.inc").language
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user