require "kramdown" require "kramdown-parser-gfm" require "rabbit/parser/base" module Kramdown module Parser class Kramdown alias_method :handle_extension_raw, :handle_extension def handle_extension(name, opts, body, type, line_no=nil) return true if handle_extension_raw(name, opts, body, type, line_no) element = Element.new(name.to_sym, nil, opts, :category => type, :location => line_no) if body root, warnings = self.class.parse(body, @options) fix_location(root, line_no) if type == :span p_element = root.children.first p_element.children.each do |sub_element| element.children << sub_element end else root.children.each do |sub_element| element.children << sub_element end end end @tree.children << element true end alias_method :configure_parser_raw, :configure_parser def configure_parser @span_parsers.unshift(:strikethrough) position = @block_parsers.index(:codeblock_fenced) @block_parsers.insert(position, :codeblock_fenced_gfm) configure_parser_raw end STRIKETHROUGH_DELIMITER = "~~" STRIKETHROUGH_DELIMITER_PATTERN = /#{Regexp.escape(STRIKETHROUGH_DELIMITER)}/ def parse_strikethrough start_line_number = @src.current_line_number delimiter = @src.scan(STRIKETHROUGH_DELIMITER_PATTERN) saved_pos = @src.save_pos text = @src.scan_until(STRIKETHROUGH_DELIMITER_PATTERN) if text text = text.sub(/#{STRIKETHROUGH_DELIMITER_PATTERN}\Z/, "") @tree.children << Element.new(:strikethrough, text, nil, :location => start_line_number) else @src.revert_pos(saved_pos) add_text(delimiter) end end define_parser(:strikethrough, STRIKETHROUGH_DELIMITER_PATTERN, STRIKETHROUGH_DELIMITER) escaped_chars_start_re = parser(:escaped_chars).start_re.to_s.sub(/(\]\)\))\z/, "~\\1") parser(:escaped_chars).start_re = /#{escaped_chars_start_re}/ parser(:codeblock_fenced_gfm).method = "parse_codeblock_fenced_gfm" def parse_codeblock_fenced_gfm original_match = self.class::FENCED_CODEBLOCK_MATCH begin self.class.send(:remove_const, :FENCED_CODEBLOCK_MATCH) self.class.const_set(:FENCED_CODEBLOCK_MATCH, GFM::FENCED_CODEBLOCK_MATCH) parse_codeblock_fenced ensure self.class.send(:remove_const, :FENCED_CODEBLOCK_MATCH) self.class.const_set(:FENCED_CODEBLOCK_MATCH, original_match) end end private def fix_location(element, base_location) return unless element.options.key?(:location) element.options[:location] += base_location - 1 element.children.each do |sub_element| fix_location(sub_element, base_location) end end end end end module Rabbit module Parser class Markdown < Base end end end require "rabbit/parser/markdown/converter" module Rabbit module Parser class Markdown unshift_loader(self) class << self def format_name "Makrdown" end def match?(source) /\A(?:md|markdown)\z/i =~ source.extension.to_s end end def parse document = Kramdown::Document.new(@source.read) converter = Converter.new(@canvas) converter.convert(document.root) end end end end