class Kramdown::Parser::GFM

This class provides a parser implementation for the GFM dialect of Markdown.

Constants

ATX_HEADER_START
ESCAPED_CHARS_GFM
FENCED_CODEBLOCK_MATCH
FENCED_CODEBLOCK_START
LIST_TYPES
NON_WORD_RE
PARAGRAPH_END_GFM
STRIKETHROUGH_DELIM
STRIKETHROUGH_MATCH
VERSION

Attributes

paragraph_end[R]

Public Class Methods

new(source, options) click to toggle source
Calls superclass method
# File lib/kramdown/parser/gfm.rb, line 24
def initialize(source, options)
  super
  @options[:auto_id_stripping] = true
  @id_counter = Hash.new(-1)

  @span_parsers.delete(:line_break)       if @options[:hard_wrap]
  @span_parsers.delete(:typographic_syms) if @options[:gfm_quirks].include?(:no_auto_typographic)

  if @options[:gfm_quirks].include?(:paragraph_end)
    atx_header_parser = :atx_header_gfm_quirk
    @paragraph_end    = self.class::PARAGRAPH_END_GFM
  else
    atx_header_parser = :atx_header_gfm
    @paragraph_end    = self.class::PARAGRAPH_END
  end

  {codeblock_fenced: :codeblock_fenced_gfm,
   atx_header: atx_header_parser}.each do |current, replacement|
    i = @block_parsers.index(current)
    @block_parsers.delete(current)
    @block_parsers.insert(i, replacement)
  end

  i = @span_parsers.index(:escaped_chars)
  @span_parsers[i] = :escaped_chars_gfm if i
  @span_parsers << :strikethrough_gfm

  @hard_line_break = "#{@options[:hard_wrap] ? '' : '\\'}\n"
end

Public Instance Methods

generate_gfm_header_id(text) click to toggle source
# File lib/kramdown/parser/gfm.rb, line 107
def generate_gfm_header_id(text)
  result = text.downcase
  result.gsub!(NON_WORD_RE, '')
  result.tr!(" \t", '-')

  @id_counter[result] += 1
  counter_result = @id_counter[result]
  result << "-#{counter_result}" if counter_result > 0

  @options[:auto_id_prefix] + result
end
parse() click to toggle source
Calls superclass method
# File lib/kramdown/parser/gfm.rb, line 54
def parse
  super
  update_elements(@root)
end
parse_atx_header_gfm_quirk() click to toggle source

Copied from kramdown/parser/kramdown/header.rb, removed the first line

# File lib/kramdown/parser/gfm.rb, line 124
def parse_atx_header_gfm_quirk
  text, id = parse_header_contents
  text.sub!(/[\t ]#+\z/, '') && text.rstrip!
  return false if text.empty?

  add_header(@src["level"].length, text, id)
  true
end
parse_list() click to toggle source

To handle task-lists we override the parse method for lists, converting matching text into checkbox input elements where necessary (as well as applying classes to the ul/ol and li elements).

Calls superclass method
# File lib/kramdown/parser/gfm.rb, line 162
def parse_list
  super
  current_list = @tree.children.select { |element| LIST_TYPES.include?(element.type) }.last

  is_tasklist   = false
  box_unchecked = '<input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />'
  box_checked   = '<input type="checkbox" class="task-list-item-checkbox" ' \
    'disabled="disabled" checked="checked" />'

  current_list.children.each do |li|
    list_items = li.children
    next unless !list_items.empty? && list_items[0].type == :p

    # li -> p -> raw_text
    descendant = list_items[0].children[0].value
    checked    = descendant.gsub!(/\A\s*\[ \]\s+/,  box_unchecked)
    unchecked  = descendant.gsub!(/\A\s*\[x\]\s+/i, box_checked)
    is_tasklist ||= checked || unchecked

    li.attr['class'] = 'task-list-item' if is_tasklist
  end

  current_list.attr['class'] = 'task-list' if is_tasklist

  true
end
parse_strikethrough_gfm() click to toggle source
# File lib/kramdown/parser/gfm.rb, line 141
def parse_strikethrough_gfm
  line_number = @src.current_line_number

  @src.pos += @src.matched_size
  el = Element.new(:html_element, 'del', {}, category: :span, line: line_number)
  @tree.children << el

  env = save_env
  reset_env(src: Kramdown::Utils::StringScanner.new(@src.matched[2..-3], line_number),
            text_type: :text)
  parse_spans(el)
  restore_env(env)

  el
end
update_elements(element) click to toggle source
# File lib/kramdown/parser/gfm.rb, line 59
def update_elements(element)
  element.children.map! do |child|
    if child.type == :text && child.value.include?(@hard_line_break)
      update_text_type(element, child)
    elsif child.type == :html_element
      child
    elsif child.type == :header && @options[:auto_ids] && !child.attr.key?('id')
      child.attr['id'] = generate_gfm_header_id(child.options[:raw_text])
      child
    else
      update_elements(child)
      child
    end
  end.flatten!
end
update_raw_text(item) click to toggle source

Update the raw text for automatic ID generation.

# File lib/kramdown/parser/gfm.rb, line 76
def update_raw_text(item)
  raw_text = +''

  append_text = lambda do |child|
    case child.type
    when :text, :codespan, :math
      raw_text << child.value
    when :entity
      raw_text << child.value.char
    when :smart_quote
      raw_text << ::Kramdown::Utils::Entities.entity(child.value.to_s).char
    when :typographic_sym
      raw_text << case child.value
                  when :laquo_space
                    "« "
                  when :raquo_space
                    " »"
                  else
                    ::Kramdown::Utils::Entities.entity(child.value.to_s).char
                  end
    else
      child.children.each { |c| append_text.call(c) }
    end
  end

  append_text.call(item)
  item.options[:raw_text] = raw_text
end

Private Instance Methods

update_text_type(element, child) click to toggle source
# File lib/kramdown/parser/gfm.rb, line 199
def update_text_type(element, child)
  children = []
  lines = child.value.split(@hard_line_break, -1)
  omit_trailing_br = (lines[-1].empty? && Kramdown::Element.category(element) == :block &&
                      element.children[-1] == child)

  lines.each_with_index do |line, index|
    new_element_options = {location: child.options[:location] + index}
    children << Element.new(:text, (index > 0 ? "\n#{line}" : line), nil, new_element_options)

    if index < lines.size - 2 || (index == lines.size - 2 && !omit_trailing_br)
      children << Element.new(:br, nil, nil, new_element_options)
    end
  end

  children
end