module Rouge module Lexers class Markdown < RegexLexer tag 'markdown' aliases 'md', 'mkd' filenames '*.markdown', '*.md', '*.mkd' mimetypes 'text/x-markdown' def html @html ||= HTML.new(options) end start { html.reset! } edot = /\\.|[^\\\n]/ state :root do # YAML frontmatter rule(/\A(---\s*\n.*?\n?)^(---\s*$\n?)/m) { delegate YAML } rule /\\./, 'Literal.String.Escape' rule /^[\S ]+\n(?:---*)\n/, 'Generic.Heading' rule /^[\S ]+\n(?:===*)\n/, 'Generic.Subheading' rule /^#(?=[^#]).*?$/, 'Generic.Heading' rule /^##*.*?$/, 'Generic.Subheading' # TODO: syntax highlight the code block, github style rule /(\n[ \t]*)(```|~~~)(.*?)(\n.*?)(\2)/m do |m| sublexer, opts = m[3].strip.split('?', 2) if sublexer sublexer = Lexer.find(sublexer) # parse the options hash from a cgi-style string opts = CGI.parse(opts || '').map do |k, vals| [ k.to_sym, vals.empty? ? true : vals[0] ] end opts = Hash[opts] sublexer &&= sublexer.new(opts) end token 'Text', m[1] token 'Punctuation', m[2] token 'Name.Label', m[3] if sublexer delegate sublexer, m[4] else token 'Literal.String.Backtick', m[4] end token 'Punctuation', m[5] end rule /\n\n(( |\t).*?\n|\n)+/, 'Literal.String.Backtick' rule /(`+)#{edot}*\1/, 'Literal.String.Backtick' # various uses of * are in order of precedence # line breaks rule /^(\s*[*]){3,}\s*$/, 'Punctuation' rule /^(\s*[-]){3,}\s*$/, 'Punctuation' # bulleted lists rule /^\s*[*+-](?=\s)/, 'Punctuation' # numbered lists rule /^\s*\d+\./, 'Punctuation' # blockquotes rule /^\s*>.*?$/, 'Generic.Traceback' # link references # [foo]: bar "baz" rule %r(^ (\s*) # leading whitespace (\[) (#{edot}+?) (\]) # the reference (\s*) (:) # colon )x do group 'Text' group 'Punctuation'; group 'Literal.String.Symbol'; group 'Punctuation' group 'Text'; group 'Punctuation' push :title push :url end # links and images rule /(!?\[)(#{edot}+?)(\])/ do group 'Punctuation' group 'Name.Variable' group 'Punctuation' push :link end rule /[*][*]#{edot}*?[*][*]/, 'Generic.Strong' rule /__#{edot}*?__/, 'Generic.Strong' rule /[*]#{edot}*?[*]/, 'Generic.Emph' rule /_#{edot}*?_/, 'Generic.Emph' # Automatic links rule /<.*?@.+[.].+>/, 'Name.Variable' rule %r[<(https?|mailto|ftp)://#{edot}*?>], 'Name.Variable' rule /[^\\`\[*\n&<]+/, 'Text' # inline html rule(/&\S*;/) { delegate html } rule(/<#{edot}*?>/) { delegate html } rule /[&<]/, 'Text' rule /\n/, 'Text' end state :link do rule /(\[)(#{edot}*?)(\])/ do group 'Punctuation' group 'Literal.String.Symbol' group 'Punctuation' pop! end rule /[(]/ do token 'Punctuation' push :inline_title push :inline_url end rule /[ \t]+/, 'Text' rule(//) { pop! } end state :url do rule /[ \t]+/, 'Text' # the url rule /(<)(#{edot}*?)(>)/ do group 'Name.Tag' group 'Literal.String.Other' group 'Name.Tag' pop! end rule /\S+/, 'Literal.String.Other', :pop! end state :title do rule /"#{edot}*?"/, 'Name.Namespace' rule /'#{edot}*?'/, 'Name.Namespace' rule /[(]#{edot}*?[)]/, 'Name.Namespace' rule /\s*(?=["'()])/, 'Text' rule(//) { pop! } end state :inline_title do rule /[)]/, 'Punctuation', :pop! mixin :title end state :inline_url do rule /[^<\s)]+/, 'Literal.String.Other', :pop! rule /\s+/m, 'Text' mixin :url end end end end