require "middleman-core" require "middleman-core/renderers/redcarpet" require "nokogiri" require "active_support/core_ext/module/attribute_accessors" # Our custom Markdown parser - extends middleman's customer parser so we pick up # all the magic. class Middleman::HashiCorp::RedcarpetHTML < ::Middleman::Renderers::MiddlemanRedcarpetHTML # Custom RedCarpet options. REDCARPET_OPTIONS = { autolink: true, fenced_code_blocks: true, tables: true, no_intra_emphasis: true, with_toc_data: true, xhtml: true, strikethrough: true, superscript: true, }.freeze def initialize(options = {}) super(options.merge(REDCARPET_OPTIONS)) end # # Override headers to add custom links. # def header(title, level) anchor = anchor_link(title) return <<-EOH.gsub(/^ {6}/, "") » #{title} EOH end # # Override list_item to automatically add links for documentation # # @param [String] text # @param [String] list_type # def list_item(text, list_type) @anchors ||= {} md = text.match(/\A(?:

)?((.+?)<\/code>)/) linked = !text.match(/\A(

)?(.+?)<\/a>\s*?[-:]?/).nil? if !md.nil? && !linked container, name = md.captures anchor = anchor_link(name) replace = %|#{container}| text.sub!(container, replace) end "

  • #{text}
  • \n" end # # Override block_html to support parsing nested markdown blocks. # # @param [String] raw # def block_html(raw) raw = unindent(raw) if md = raw.match(/\<(.+?)\>(.*)\<(\/.+?)\>/m) open_tag, content, close_tag = md.captures rendered_content = recursive_render(content) # If the recursive render did more than simply wrap # the content within a paragraph, assume this is valid # markdown content and return the result if rendered_content != paragraph(content) return "<#{open_tag}>\n#{rendered_content}<#{close_tag}>" end end raw end # # Override paragraph to support custom alerts. # # @param [String] text # @return [String] # def paragraph(text) add_alerts("

    #{text.strip}

    \n") end private # # Generate an anchor link from the generated raw value. # # @return [String] # def anchor_link(raw) @anchors ||= {} name = raw .downcase .strip .gsub(/<\/?[^>]*>/, '') # Strip links .gsub(/\W+/, '-') # Whitespace to - .gsub(/\A\-/, '') # No leading - .squeeze('-') # Collapse -- i = 0 link = name while @anchors.key?(link) do i += 1 link = "#{name}-#{i}" end @anchors[link] = true return link end # # This is jank, but Redcarpet does not provide a way to access the # renderer from inside Redcarpet::Markdown. Since we know who we are, we # can cheat a bit. # # @param [String] markdown # @return [String] # def recursive_render(markdown) Redcarpet::Markdown.new(self.class, REDCARPET_OPTIONS).render(markdown) end # # Add alert text to the given markdown. # # @param [String] text # @return [String] # def add_alerts(text) map = { "=>" => "success", "->" => "info", "~>" => "warning", "!>" => "danger", } regexp = map.map { |k, _| Regexp.escape(k) }.join("|") if md = text.match(/^

    (#{regexp})/) key = md.captures[0] klass = map[key] text.gsub!(/#{Regexp.escape(key)}\s+?/, "") return <<-EOH.gsub(/^ {8}/, "")

    EOH else return text end end def unindent(string) string.gsub(/^#{string.scan(/^[[:blank:]]+/).min_by { |l| l.length }}/, "") end end