lib/rouge/lexers/shell.rb in rouge-0.0.2 vs lib/rouge/lexers/shell.rb in rouge-0.0.3

- old
+ new

@@ -1,14 +1,15 @@ module Rouge module Lexers - ShellLexer = RegexLexer.create do - name 'shell' + class ShellLexer < RegexLexer + tag 'shell' aliases 'bash', 'zsh', 'ksh', 'sh' + extensions 'sh', 'bash', 'zsh', 'ksh' KEYWORDS = %w( - if fi else while do done for then return function case - select continue until esac elif + if fi else while do done for then return function + select continue until esac elif in ).join('|') BUILTINS = %w( alias bg bind break builtin caller cd command compgen complete declare dirs disown echo enable eval exec exit @@ -16,94 +17,109 @@ local logout popd printf pushd pwd read readonly set shift shopt source suspend test time times trap true type typeset ulimit umask unalias unset wait ).join('|') - lexer :basic do + state :basic do rule /#.*\n/, 'Comment' rule /\b(#{KEYWORDS})\s*\b/, 'Keyword' + rule /\bcase\b/, 'Keyword', :case rule /\b(#{BUILTINS})\s*\b(?!\.)/, 'Name.Builtin' - rule /(\b\w+)(=)/ do |_, var, eq, &out| - out.call 'Name.Variable', var - out.call 'Operator', eq + rule /(\b\w+)(=)/ do |m| + group 'Name.Variable' + group 'Operator' end rule /[\[\]{}()=]/, 'Operator' rule /&&|\|\|/, 'Operator' # rule /\|\|/, 'Operator' rule /<<</, 'Operator' # here-string rule /<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2/, 'Literal.String' end - lexer :double_quotes do - rule /"/, 'Literal.String.Double', :pop! - rule /\\./, 'Literal.String.Escape' + state :double_quotes do + # NB: "abc$" is literally the string abc$. + # Here we prevent :interp from interpreting $" as a variable. + rule /(?:\$#?)?"/, 'Literal.String.Double', :pop! mixin :interp rule /[^"`\\$]+/, 'Literal.String.Double' end - lexer :data do - # TODO: this should be its own sublexer so we can capture - # interpolation and such - rule /$?"/, 'Literal.String.Double', :double_quotes + state :data do + rule /\\./, 'Literal.String.Escape' + rule /\$?"/, 'Literal.String.Double', :double_quotes # single quotes are much easier than double quotes - we can # literally just scan until the next single quote. # POSIX: Enclosing characters in single-quotes ( '' ) # shall preserve the literal value of each character within the # single-quotes. A single-quote cannot occur within single-quotes. rule /$?'[^']*'/, 'Literal.String.Single' + rule /\*/, 'Keyword' + rule /;/, 'Text' rule /\s+/, 'Text' - rule /[^=\s\[\]{}()$"\'`\\<]+/, 'Text' + rule /[^=\s{}()$"\'`\\<]+/, 'Text' rule /\d+(?= |\Z)/, 'Number' rule /</, 'Text' mixin :interp end - lexer :curly do + state :curly do rule /}/, 'Keyword', :pop! rule /:-/, 'Keyword' rule /[a-zA-Z0-9_]+/, 'Name.Variable' rule /[^}:"'`$]+/, 'Punctuation' mixin :root end - lexer :paren do + state :paren do rule /\)/, 'Keyword', :pop! mixin :root end - lexer :math do + state :math do rule /\)\)/, 'Keyword', :pop! rule %r([-+*/%^|&]|\*\*|\|\|), 'Operator' rule /\d+/, 'Number' mixin :root end - lexer :backticks do + state :case do + rule /\besac\b/, 'Keyword', :pop! + rule /\|/, 'Punctuation' + rule /\)/, 'Punctuation', :case_stanza + mixin :root + end + + state :case_stanza do + rule /;;/, 'Punctuation', :pop! + mixin :root + end + + state :backticks do rule /`/, 'Literal.String.Backtick', :pop! mixin :root end - lexer :interp do + state :interp do + rule /\\$/, 'Literal.String.Escape' # line continuation + rule /\\./, 'Literal.String.Escape' rule /\$\(\(/, 'Keyword', :math rule /\$\(/, 'Keyword', :paren rule /\${#?/, 'Keyword', :curly rule /`/, 'Literal.String.Backtick', :backticks rule /\$#?(\w+|.)/, 'Name.Variable' end - lexer :root do + state :root do mixin :basic mixin :data end - - mixin :root end end end