# -*- coding: utf-8 -*- # module Rouge module Lexers class EmbeddedJSX < RegexLexer tag :xml_inside_jsx def self.analyze_text(text) 0 end def jsx @jsx ||= JSX.new end state :assignment_expression do # actually a JS expr in {...} rule /{(.*?)}/m do |match| inside = match.matched[1...-1] delegate jsx, inside end end state :root do mixin :assignment_expression rule /[^<&]+/, Text rule /&\S*?;/, Name::Entity rule /<!\[CDATA\[.*?\]\]\>/, Comment::Preproc rule /<!--/, Comment, :comment rule /<\?.*?\?>/, Comment::Preproc rule /<![^>]*>/, Comment::Preproc rule %r(<\s*[\w:.-]+)m, Name::Tag, :tag # self-closing tags rule %r(<\s*/\s*[\w:.-]+\s*>)m, Name::Tag end state :comment do rule /[^-]+/m, Comment rule /-->/, Comment, :pop! rule /-/, Comment end state :tag do rule /\s+/m, Text rule /[\w.:-]+\s*=/m, Name::Attribute, :attr mixin :assignment_expression rule %r(/?\s*>), Name::Tag, :pop! end state :attr do rule /\s+/m, Text rule /{.*?}/m, Comment, :pop! rule /\".*?\"|\'.*?'|[^\s>]+/, Str, :pop! end end class JSX < RegexLexer title "JavaScript (JSX)" desc "JSX variant of JavaScript" tag 'jsx' filenames '*.jsx' mimetypes 'text/jsx' def self.analyze_text(text) case text when %r{</\w} 0.3 else 0 end end def initialize(opts={}) @xml_lexer = EmbeddedJSX.new(opts) super(opts) end state :comments_and_whitespace do rule /\s+/, Text rule /<!--/, Comment # really...? rule %r(//.*?$), Comment::Single rule %r(/\*.*?\*/)m, Comment::Multiline end state :embedded_xml do # open tags rule %r{<\s*([\w:.-]+).*?<\s*/\1\s*>}m do delegate @xml_lexer end # self-closing tags rule %r(<\s*/\s*[\w:.-]+\s*>)m do delegate @xml_lexer end end state :expr_start do mixin :comments_and_whitespace mixin :embedded_xml rule %r(/) do token Str::Regex goto :regex end rule /[{]/, Punctuation, :object rule //, Text, :pop! end state :comment do rule /[^-]+/m, Comment rule /-->/, Comment, :pop! rule /-/, Comment end state :tag do rule /\s+/m, Text rule /[\w.:-]+\s*=/m, Name::Attribute, :attr rule %r(/?\s*>), Name::Tag, :pop! end state :attr do rule /\s+/m, Text rule /\".*?\"|\'.*?'|[^\s>]+/, Str, :pop! end state :regex do rule %r(/) do token Str::Regex goto :regex_end end rule %r([^/]\n), Error, :pop! rule /\n/, Error, :pop! rule /\[\^/, Str::Escape, :regex_group rule /\[/, Str::Escape, :regex_group rule /\\./, Str::Escape rule %r{[(][?][:=<!]}, Str::Escape rule /[{][\d,]+[}]/, Str::Escape rule /[()?]/, Str::Escape rule /./, Str::Regex end state :regex_end do rule /[gim]+/, Str::Regex, :pop! rule(//) { pop! } end state :regex_group do # specially highlight / in a group to indicate that it doesn't # close the regex rule /\//, Str::Escape rule %r([^/]\n) do token Error pop! 2 end rule /\]/, Str::Escape, :pop! rule /\\./, Str::Escape rule /./, Str::Regex end state :bad_regex do rule /[^\n]+/, Error, :pop! end def self.keywords @keywords ||= Set.new %w( for in while do break return continue switch case default if else throw try catch finally new delete typeof instanceof void this yield ) end def self.declarations @declarations ||= Set.new %w(var let with function) end def self.reserved @reserved ||= Set.new %w( abstract boolean byte char class const debugger double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile ) end def self.constants @constants ||= Set.new %w(true false null NaN Infinity undefined) end def self.builtins @builtins ||= %w( Array Boolean Date Error Function Math netscape Number Object Packages RegExp String sun decodeURI decodeURIComponent encodeURI encodeURIComponent Error eval isFinite isNaN parseFloat parseInt document this window ) end id = /[$a-zA-Z_][a-zA-Z0-9_]*/ state :root do rule /\A\s*#!.*?\n/m, Comment::Preproc, :statement rule /\n/, Text, :statement rule %r((?<=\n)(?=\s|/|<!--)), Text, :expr_start mixin :embedded_xml mixin :comments_and_whitespace rule %r(\+\+ | -- | ~ | && | \|\| | \\(?=\n) | << | >>>? | === | !== )x, Operator, :expr_start rule %r([-<>+*%&|\^/!=]=?), Operator, :expr_start rule /[(\[,]/, Punctuation, :expr_start rule /;/, Punctuation, :statement rule /[)\].]/, Punctuation rule /[?]/ do token Punctuation push :ternary push :expr_start end rule /[{}]/, Punctuation, :statement rule id do |m| if self.class.keywords.include? m[0] token Keyword push :expr_start elsif self.class.declarations.include? m[0] token Keyword::Declaration push :expr_start elsif self.class.reserved.include? m[0] token Keyword::Reserved elsif self.class.constants.include? m[0] token Keyword::Constant elsif self.class.builtins.include? m[0] token Name::Builtin else token Name::Other end end rule /[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?/, Num::Float rule /0x[0-9a-fA-F]+/, Num::Hex rule /[0-9]+/, Num::Integer rule /"(\\\\|\\"|[^"])*"/, Str::Double rule /'(\\\\|\\'|[^'])*'/, Str::Single end # braced parts that aren't object literals state :statement do rule /(#{id})(\s*)(:)/ do groups Name::Label, Text, Punctuation end rule /[{}]/, Punctuation mixin :expr_start end # object literals state :object do mixin :comments_and_whitespace rule /[}]/ do token Punctuation goto :statement end rule /(#{id})(\s*)(:)/ do groups Name::Attribute, Text, Punctuation push :expr_start end rule /:/, Punctuation mixin :root end # ternary expressions, where <id>: is not a label! state :ternary do rule /:/ do token Punctuation goto :expr_start end mixin :root end end end end