Class: Kramdown::Parser::Kramdown

Inherits:
Object
  • Object
show all
Defined in:
lib/monkey_patches/kramdown/parser/kramdown/html.rb,
lib/monkey_patches/kramdown/parser/kramdown/link.rb,
lib/monkey_patches/kramdown/parser/kramdown/list.rb,
lib/monkey_patches/kramdown/parser/kramdown/header.rb,
lib/monkey_patches/kramdown/parser/kramdown/autolink.rb,
lib/monkey_patches/kramdown/parser/kramdown/emphasis.rb,
lib/monkey_patches/kramdown/parser/kramdown/footnote.rb,
lib/monkey_patches/kramdown/parser/kramdown/code_span.rb,
lib/monkey_patches/kramdown/parser/kramdown/paragraph.rb,
lib/monkey_patches/kramdown/parser/kramdown/blockquote.rb,
lib/monkey_patches/kramdown/parser/kramdown/code_block.rb,
lib/monkey_patches/kramdown/parser/kramdown/escaped_chars.rb,
lib/monkey_patches/kramdown/parser/kramdown/horizontal_rule.rb,
lib/monkey_patches/kramdown/parser/kramdown/typographic_symbol.rb

Direct Known Subclasses

GFM

Instance Method Summary collapse

Instance Method Details

This helper methods adds the approriate attributes to the element el of type a or img and the element itself to the @tree.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/monkey_patches/kramdown/parser/kramdown/link.rb', line 6

def add_link(el, href, title, alt_text = nil, ial = nil, link_id = nil)
  el.options[:ial] = ial
  el.options[:link_id] = link_id

  update_attr_with_ial(el.attr, ial) if ial
  if el.type == :a
    el.attr['href'] = href
    el.options[:link_text] = alt_text
  else
    el.attr['src'] = href
    el.attr['alt'] = alt_text
    el.children.clear
  end
  el.attr['title'] = title if title
  @tree.children << el
end

#parse_atx_headerObject

Parse the Atx header at the current location.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/monkey_patches/kramdown/parser/kramdown/header.rb', line 21

def parse_atx_header
  return false if !after_block_boundary?

  start_line_number = @src.current_line_number
  @src.check(ATX_HEADER_MATCH)
  level, text, id = @src[1], @src[2].to_s.strip, @src[3]
  return false if text.empty?

  @src.pos += @src.matched_size
  el = new_block_el(:header, nil, nil, :level => level.length, :raw_text => text, :location => start_line_number, :original_text => @src[0].chomp)
  add_text(text, el)
  el.attr['id'] = id if id
  @tree.children << el
  true
end

Parse the autolink at the current location.



6
7
8
9
10
11
12
13
# File 'lib/monkey_patches/kramdown/parser/kramdown/autolink.rb', line 6

def parse_autolink
  start_line_number = @src.current_line_number
  @src.pos += @src.matched_size
  href = (@src[2].nil? ? "mailto:#{@src[1]}" : @src[1])
  el = Element.new(:a, nil, {'href' => href}, :location => start_line_number, :raw_text => @src[0].dup)
  add_text(@src[1].sub(/^mailto:/, ''), el)
  @tree.children << el
end

#parse_blockquoteObject

Parse the blockquote at the current location.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/monkey_patches/kramdown/parser/kramdown/blockquote.rb', line 6

def parse_blockquote
  start_line_number = @src.current_line_number
  result = @src.scan(PARAGRAPH_MATCH)
  while !@src.match?(self.class::LAZY_END)
    result << @src.scan(PARAGRAPH_MATCH)
  end
  raw_text = result.dup
  result.gsub!(BLOCKQUOTE_START, '')

  el = new_block_el(:blockquote, nil, nil, :location => start_line_number, :raw_text => raw_text)
  @tree.children << el
  parse_blocks(el, result)
  true
end

#parse_codeblockObject

Parse the indented codeblock at the current location.



6
7
8
9
10
11
12
13
# File 'lib/monkey_patches/kramdown/parser/kramdown/code_block.rb', line 6

def parse_codeblock
  start_line_number = @src.current_line_number
  data = @src.scan(self.class::CODEBLOCK_MATCH)
  data.gsub!(/\n( {0,3}\S)/, ' \\1')
  data.gsub!(INDENT, '')
  @tree.children << new_block_el(:codeblock, data, nil, :location => start_line_number, :raw_text => @src.matched.dup)
  true
end

#parse_codeblock_fencedObject

Parse the fenced codeblock at the current location.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/monkey_patches/kramdown/parser/kramdown/code_block.rb', line 16

def parse_codeblock_fenced
  if @src.check(self.class::FENCED_CODEBLOCK_MATCH)
    start_line_number = @src.current_line_number
    @src.pos += @src.matched_size
    el = new_block_el(:codeblock, @src[5], nil, :location => start_line_number, :raw_text => @src.matched.dup)
    lang = @src[3].to_s.strip
    unless lang.empty?
      el.options[:lang] = lang
      el.attr['class'] = "language-#{@src[4]}"
    end
    @tree.children << el
    true
  else
    false
  end
end

#parse_codespanObject

Parse the codespan at the current scanner location.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/monkey_patches/kramdown/parser/kramdown/code_span.rb', line 5

def parse_codespan
  start_line_number = @src.current_line_number
  result = @src.scan(CODESPAN_DELIMITER)
  simple = (result.length == 1)
  saved_pos = @src.save_pos

  if simple && @src.pre_match =~ /\s\Z/ && @src.match?(/\s/)
    add_text(result)
    return
  end

  if text = @src.scan_until(/#{result}/)
    raw_text = result + text
    text.sub!(/#{result}\Z/, '')
    if !simple
      text = text[1..-1] if text[0..0] == ' '
      text = text[0..-2] if text[-1..-1] == ' '
    end
    @tree.children << Element.new(:codespan, text, nil, :location => start_line_number, :raw_text => raw_text)
  else
    @src.revert_pos(saved_pos)
    add_text(result)
  end
end

#parse_emphasisObject

Parse the emphasis at the current location.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/monkey_patches/kramdown/parser/kramdown/emphasis.rb', line 6

def parse_emphasis
  start_line_number = @src.current_line_number
  saved_pos = @src.save_pos

  result = @src.scan(EMPHASIS_START)
  element = (result.length == 2 ? :strong : :em)
  type = result[0..0]

  if (type == '_' && @src.pre_match =~ /[[:alpha:]-]\z/) || @src.check(/\s/) ||
    @tree.type == element || @stack.any? { |el, _| el.type == element }
    add_text(result)
    return
  end

  sub_parse = lambda do |delim, elem|
    el = Element.new(elem, nil, nil, :location => start_line_number, :delim => delim)
    stop_re = /#{Regexp.escape(delim)}/
    found = parse_spans(el, stop_re) do
      (@src.pre_match[-1, 1] !~ /\s/) &&
        (elem != :em || !@src.match?(/#{Regexp.escape(delim*2)}(?!#{Regexp.escape(delim)})/)) &&
        (type != '_' || !@src.match?(/#{Regexp.escape(delim)}[[:alnum:]]/)) && el.children.size > 0
    end
    [found, el, stop_re]
  end

  found, el, stop_re = sub_parse.call(result, element)
  if !found && element == :strong && @tree.type != :em
    @src.revert_pos(saved_pos)
    @src.pos += 1
    found, el, stop_re = sub_parse.call(type, :em)
  end
  if found
    @src.scan(stop_re)
    @tree.children << el
  else
    @src.revert_pos(saved_pos)
    @src.pos += result.length
    add_text(result)
  end
end

#parse_escaped_charsObject

Parse the backslash-escaped character at the current location.



6
7
8
9
# File 'lib/monkey_patches/kramdown/parser/kramdown/escaped_chars.rb', line 6

def parse_escaped_chars
  @src.pos += @src.matched_size
  add_text(@src[0])
end

#parse_footnote_markerObject

Parse the footnote marker at the current location.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/monkey_patches/kramdown/parser/kramdown/footnote.rb', line 5

def parse_footnote_marker
  start_line_number = @src.current_line_number
  @src.pos += @src.matched_size
  fn_def = @footnotes[@src[1]]
  if fn_def
    if fn_def[:eob]
      update_attr_with_ial(fn_def[:eob].attr, fn_def[:eob].options[:ial] || {})
      fn_def[:attr] = fn_def[:eob].attr
      fn_def[:options] = fn_def[:eob].options
      fn_def.delete(:eob)
    end
    fn_def[:marker] ||= []
    fn_def[:marker].push(Element.new(:footnote, fn_def[:content], fn_def[:attr],
                                     fn_def[:options].merge(:name => @src[1], :location => start_line_number, :raw_text => @src[0].dup)))
    @tree.children << fn_def[:marker].last
  else
    warning("Footnote definition for '#{@src[1]}' not found on line #{start_line_number}")
    add_text(@src.matched)
  end
end

#parse_horizontal_ruleObject

Parse the horizontal rule at the current location.



6
7
8
9
10
11
# File 'lib/monkey_patches/kramdown/parser/kramdown/horizontal_rule.rb', line 6

def parse_horizontal_rule
  start_line_number = @src.current_line_number
  @src.pos += @src.matched_size
  @tree.children << new_block_el(:hr, nil, nil, :location => start_line_number, raw_text: @src[0].dup)
  true
end


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/monkey_patches/kramdown/parser/kramdown/link.rb', line 23

def parse_link
  start_line_number = @src.current_line_number
  result = @src.scan(LINK_START)
  raw_text = result.dup
  cur_pos = @src.pos
  saved_pos = @src.save_pos

  link_type = (result =~ /^!/ ? :img : :a)

  # no nested links allowed
  if link_type == :a && (@tree.type == :img || @tree.type == :a || @stack.any? { |t, s| t && (t.type == :img || t.type == :a) })
    add_text(result)
    return
  end
  el = Element.new(link_type, nil, nil, :location => start_line_number)

  count = 1
  found = parse_spans(el, LINK_BRACKET_STOP_RE) do
    count = count + (@src[1] ? -1 : 1)
    count - el.children.select { |c| c.type == :img }.size == 0
  end
  unless found
    @src.revert_pos(saved_pos)
    add_text(result)
    return
  end
  alt_text = extract_string(cur_pos...@src.pos, @src).gsub(ESCAPED_CHARS, '\1')
  raw_text << alt_text.dup
  @src.scan(LINK_BRACKET_STOP_RE)
  raw_text << @src[0].dup

  # reference style link or no link url
  if @src.scan(LINK_INLINE_ID_RE) || !@src.check(/\(/)
    link_id = normalize_link_id(@src[1] || alt_text)
    if @link_defs.has_key?(link_id)
      add_link(el, @link_defs[link_id][0], @link_defs[link_id][1], alt_text,
               @link_defs[link_id][2] && @link_defs[link_id][2].options[:ial], link_id)
    else
      warning("No link definition for link ID '#{link_id}' found on line #{start_line_number}")
      @src.revert_pos(saved_pos)
      add_text(result)
    end
    raw_text << @src[0].dup unless @src[0].nil?
    el.options[:raw_text] = raw_text
    return
  end

  # link url in parentheses
  if @src.scan(/\(<(.*?)>/)
    link_url = @src[1]
    if @src.scan(/\)/)
      raw_text += "(<#{link_url}>)"
      el.options[:raw_text] = raw_text
      add_link(el, link_url, nil, alt_text)
      return
    end
  else
    link_url = ''
    nr_of_brackets = 0
    while temp = @src.scan_until(LINK_PAREN_STOP_RE)
      link_url << temp
      if @src[2]
        nr_of_brackets -= 1
        break if nr_of_brackets == 0
      elsif @src[1]
        nr_of_brackets += 1
      else
        break
      end
    end
    raw_text << link_url.dup

    link_url = link_url[1..-2]
    link_url.strip!

    if nr_of_brackets == 0
      el.options[:raw_text] = raw_text
      add_link(el, link_url, nil, alt_text)
      return
    end
  end

  if @src.scan(LINK_INLINE_TITLE_RE)
    raw_text << @src[0].dup
    el.options[:raw_text] = raw_text
    add_link(el, link_url, @src[0][0..-2], alt_text)
  else
    @src.revert_pos(saved_pos)
    add_text(result)
  end
end

#parse_listObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/monkey_patches/kramdown/parser/kramdown/list.rb', line 4

def parse_list
  start_line_number = @src.current_line_number
  type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL])
  list = new_block_el(type, nil, nil, :location => start_line_number, :raw_text => "")

  item = nil
  content_re, lazy_re, indent_re = nil
  eob_found = false
  nested_list_found = false
  last_is_blank = false
  while !@src.eos?
    start_line_number = @src.current_line_number
    if last_is_blank && @src.check(HR_START)
      break
    elsif @src.scan(EOB_MARKER)
      eob_found = true
      break
    elsif @src.scan(list_start_re)
      item = Element.new(:li, nil, nil, :location => start_line_number, :raw_text => @src.matched.dup)
      list.options[:raw_text] += @src[0]
      item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
      list.children << item

      item.value.sub!(self.class::LIST_ITEM_IAL) do |match|
        parse_attribute_list($1, item.options[:ial] ||= {})
        ''
      end

      list_start_re = (type == :ul ? /^( {0,#{[3, indentation - 1].min}}[+*-])([\t| ].*?\n)/ :
                         /^( {0,#{[3, indentation - 1].min}}\d+\.)([\t| ].*?\n)/)
      nested_list_found = (item.value =~ LIST_START)
      last_is_blank = false
      item.value = [item.value]
    elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
      result.sub!(/^(\t+)/) { " " * 4 * $1.length }
      indentation_found = result.sub!(indent_re, '')
      if !nested_list_found && indentation_found && result =~ LIST_START
        item.value << ''
        nested_list_found = true
      elsif nested_list_found && !indentation_found && result =~ LIST_START
        result = " " * (indentation + 4) << result
      end
      item.options[:raw_text] += @src[0]
      list.options[:raw_text] += @src[0]
      item.value.last << result
      last_is_blank = false
    elsif result = @src.scan(BLANK_LINE)
      nested_list_found = true
      last_is_blank = true
      item.options[:raw_text] += @src.matched
      list.options[:raw_text] += @src.matched

      if list.children.last.type != :blank
        bl = new_block_el(:blank, [@src.matched.dup])
        bl.options[:raw_text] = @src.matched.dup
        list.children << bl
      end

      item.value.last << result
    else
      break
    end
  end

  list.options[:raw_text].gsub!(/\R{2,}\z/, "\n")
  list.children.pop if list.children.last.type == :blank
  @tree.children << list

  last = nil
  list.children.each do |it|
    temp = Element.new(:temp, nil, nil, :location => it.options[:location])

    env = save_env
    location = it.options[:location]
    it.value.each do |val|
      @src = ::Kramdown::Utils::StringScanner.new(val, location)
      parse_blocks(temp)
      location = @src.current_line_number
    end
    restore_env(env)

    it.children = temp.children
    it.value = nil
    next if it.children.size == 0

    # Handle the case where an EOB marker is inserted by a block IAL for the first paragraph
    it.children.delete_at(1) if it.children.first.type == :p &&
      it.children.length >= 2 && it.children[1].type == :eob && it.children.first.options[:ial]

    if it.children.first.type == :p &&
      (it.children.length < 2 || it.children[1].type != :blank ||
        (it == list.children.last && it.children.length == 2 && !eob_found)) &&
      (list.children.last != it || list.children.size == 1 ||
        list.children[0..-2].any? { |cit| !cit.children.first || cit.children.first.type != :p || cit.children.first.options[:transparent] })
      it.children.first.children.first.value << "\n" if it.children.size > 1 && it.children[1].type != :blank
      it.children.first.options[:transparent] = true
    end

    if it.children.last.type == :blank
      last = it.children.pop
    else
      last = nil
    end
  end

  @tree.children << last if !last.nil? && !eob_found

  true
end

#parse_paragraphObject

Parse the paragraph at the current location.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/monkey_patches/kramdown/parser/kramdown/paragraph.rb', line 6

def parse_paragraph
  start_line_number = @src.current_line_number
  result = @src.scan(PARAGRAPH_MATCH)
  while !@src.match?(paragraph_end)
    result << @src.scan(PARAGRAPH_MATCH)
  end
  raw_text = result.dup
  result.rstrip!
  if @tree.children.last && @tree.children.last.type == :p
    @tree.children.last.options[:raw_text] += result
    @tree.children.last.children.first.value << "\n" << result
  else
    @tree.children << new_block_el(:p, nil, nil, :location => start_line_number, raw_text: raw_text)
    result.lstrip!
    add_text(result, @tree.children.last)
  end
  true
end

#parse_setext_headerObject

Parse the Setext header at the current location.



6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/monkey_patches/kramdown/parser/kramdown/header.rb', line 6

def parse_setext_header
  return false if !after_block_boundary?

  start_line_number = @src.current_line_number
  @src.pos += @src.matched_size
  text, id, level = @src[1], @src[2], @src[3]
  text.strip!
  el = new_block_el(:header, nil, nil, :level => (level == '-' ? 2 : 1), :raw_text => text, :location => start_line_number, :original_text => @src[0].chomp)
  add_text(text, el)
  el.attr['id'] = id if id
  @tree.children << el
  true
end

#parse_span_htmlObject

Parse the HTML at the current position as span-level HTML.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/monkey_patches/kramdown/parser/kramdown/html.rb', line 5

def parse_span_html
  line = @src.current_line_number
  if result = @src.scan(HTML_COMMENT_RE)
    @tree.children << Element.new(:xml_comment, result, nil, :category => :span, :location => line)
  elsif result = @src.scan(HTML_INSTRUCTION_RE)
    @tree.children << Element.new(:xml_pi, result, nil, :category => :span, :location => line)
  elsif result = @src.scan(HTML_TAG_CLOSE_RE)
    warning("Found invalidly used HTML closing tag for '#{@src[1]}' on line #{line}")
    add_text(result)
  elsif result = @src.scan(HTML_TAG_RE)
    tag_name = @src[1]
    tag_name.downcase! if HTML_ELEMENT[tag_name.downcase]
    if HTML_BLOCK_ELEMENTS.include?(tag_name)
      warning("Found block HTML tag '#{tag_name}' in span-level text on line #{line}")
      add_text(result)
      return
    end

    attrs = parse_html_attributes(@src[2], line, HTML_ELEMENT[tag_name])
    attrs.each { |name, value| value.gsub!(/\n+/, ' ') }

    do_parsing = (HTML_CONTENT_MODEL[tag_name] == :raw || @tree.options[:content_model] == :raw ? false : @options[:parse_span_html])
    if val = HTML_MARKDOWN_ATTR_MAP[attrs.delete('markdown')]
      if val == :block
        warning("Cannot use block-level parsing in span-level HTML tag (line #{line}) - using default mode")
      elsif val == :span
        do_parsing = true
      elsif val == :default
        do_parsing = HTML_CONTENT_MODEL[tag_name] != :raw
      elsif val == :raw
        do_parsing = false
      end
    end

    el = Element.new(:html_element, tag_name, attrs, :category => :span, :location => line,
                     :content_model => (do_parsing ? :span : :raw), :is_closed => !!@src[4], :opening_tag => result)
    @tree.children << el
    stop_re = /<\/#{Regexp.escape(tag_name)}\s*>/
    stop_re = Regexp.new(stop_re.source, Regexp::IGNORECASE) if HTML_ELEMENT[tag_name]
    if !@src[4] && !HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
      if parse_spans(el, stop_re, (do_parsing ? nil : [:span_html]))
        @src.scan(stop_re)
      else
        warning("Found no end tag for '#{el.value}' (line #{line}) - auto-closing it")
        add_text(@src.rest, el)
        @src.terminate
      end
    end
    Kramdown::Parser::Html::ElementConverter.convert(@root, el) if @options[:html_to_native]
  else
    add_text(@src.getch)
  end
end

#parse_typographic_symsObject

Parse the typographic symbols at the current location.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/monkey_patches/kramdown/parser/kramdown/typographic_symbol.rb', line 6

def parse_typographic_syms
  start_line_number = @src.current_line_number
  @src.pos += @src.matched_size
  val = TYPOGRAPHIC_SYMS_SUBST[@src.matched]
  if val.kind_of?(Symbol)
    @tree.children << Element.new(:typographic_sym, val, nil, :location => start_line_number, symbol: @src.matched)
  elsif @src.matched == '\\<<'
    @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt'),
                                  nil, :location => start_line_number)
    @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt'),
                                  nil, :location => start_line_number)
  else
    @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt'),
                                  nil, :location => start_line_number)
    @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt'),
                                  nil, :location => start_line_number)
  end
end