lib/rouge/lexers/markdown.rb



module Rouge
  module Lexers
    class Markdown < RegexLexer
      desc "Markdown, a light-weight markup language for authors"

      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 /\\./, Str::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 = Lexer.find_fancy(m[3].strip, m[4])
          sublexer ||= PlainText.new(:token => Str::Backtick)

          token Text, m[1]
          token Punctuation, m[2]
          token Name::Label, m[3]
          delegate sublexer, m[4]
          token Punctuation, m[5]
        end

        rule /\n\n((    |\t).*?\n|\n)+/, Str::Backtick

        rule /(`+)#{edot}*\1/, Str::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
          groups Text, Punctuation, Str::Symbol, Punctuation, Text, Punctuation

          push :title
          push :url
        end

        # links and images
        rule /(!?\[)(#{edot}+?)(\])/ do
          groups Punctuation, Name::Variable, 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
          groups Punctuation, Str::Symbol, 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
          groups Name::Tag, Str::Other, Name::Tag
          pop!
        end

        rule /\S+/, Str::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)]+/, Str::Other, :pop!
        rule /\s+/m, Text
        mixin :url
      end
    end
  end
end