class Kramdown::Parser::Kramdown

Used for parsing a document in kramdown format.

If you want to extend the functionality of the parser, you need to do the following:

Here is a small example for an extended parser class that parses ERB style tags as raw text if they are used as span-level elements (an equivalent block-level parser should probably also be made to handle the block case):

require 'kramdown/parser/kramdown'

class Kramdown::Parser::ERBKramdown < Kramdown::Parser::Kramdown

   def initialize(source, options)
     super
     @span_parsers.unshift(:erb_tags)
   end

   ERB_TAGS_START = /<%.*?%>/

   def parse_erb_tags
     @src.pos += @src.matched_size
     @tree.children << Element.new(:raw, @src.matched)
   end
   define_parser(:erb_tags, ERB_TAGS_START, '<%')

end

The new parser can be used like this:

require 'kramdown/document'
# require the file with the above parser class

Kramdown::Document.new(input_text, :input => 'ERBKramdown').to_html

Constants

ABBREV_DEFINITION_START
ACHARS
ALD_ANY_CHARS
ALD_CLASS_NAME
ALD_ID_CHARS
ALD_ID_NAME
ALD_START
ALD_TYPE_ANY
ALD_TYPE_CLASS_NAME
ALD_TYPE_ID_NAME
ALD_TYPE_ID_OR_CLASS
ALD_TYPE_ID_OR_CLASS_MULTI
ALD_TYPE_KEY_VALUE_PAIR
ALD_TYPE_REF
ATX_HEADER_START
BLANK_LINE
BLOCKQUOTE_START
BLOCK_BOUNDARY
BLOCK_EXTENSIONS_START
BLOCK_MATH_START
CODEBLOCK_MATCH
CODEBLOCK_START
CODESPAN_DELIMITER
DEFINITION_LIST_START
Data

Struct class holding all the needed data for one block/span-level parser method.

EMPHASIS_START
EOB_MARKER
ESCAPED_CHARS
EXT_BLOCK_START
EXT_BLOCK_STOP_STR
EXT_SPAN_START
EXT_START_STR
EXT_STOP_STR
FENCED_CODEBLOCK_MATCH
FENCED_CODEBLOCK_START
FOOTNOTE_DEFINITION_START
FOOTNOTE_MARKER_START
HEADER_ID
HR_START
HTML_BLOCK_START
HTML_MARKDOWN_ATTR_MAP

Mapping of markdown attribute value to content model. I.e. :raw when “0”, :default when “1” (use default content model for the HTML element), :span when “span”, :block when block and for everything else nil is returned.

HTML_SPAN_START
IAL_BLOCK
IAL_BLOCK_START
IAL_CLASS_ATTR
IAL_SPAN_START
INDENT

Regexp for matching indentation (one tab or four spaces)

INLINE_MATH_START
LAZY_END
LAZY_END_HTML_SPAN_ELEMENTS
LAZY_END_HTML_START
LAZY_END_HTML_STOP
LINE_BREAK
LIST_ITEM_IAL
LIST_ITEM_IAL_CHECK
LIST_START
LIST_START_OL
LIST_START_UL
OPT_SPACE

Regexp for matching the optional space (zero or up to three spaces)

PARAGRAPH_END
PARAGRAPH_MATCH
PARAGRAPH_START
PARSE_FIRST_LIST_LINE_REGEXP_CACHE
SETEXT_HEADER_START
SMART_QUOTES_RE
SPAN_EXTENSIONS_START
SQ_CLOSE
SQ_PUNCT
SQ_RULES
SQ_SUBSTS
TABLE_FSEP_LINE
TABLE_HSEP_ALIGN
TABLE_LINE
TABLE_PIPE_CHECK
TABLE_ROW_LINE
TABLE_SEP_LINE
TABLE_START
TRAILING_WHITESPACE
TYPOGRAPHIC_SYMS
TYPOGRAPHIC_SYMS_RE
TYPOGRAPHIC_SYMS_SUBST

Protected Class Methods

define_parser(name, start_re, span_start = nil, meth_name = "parse_ click to toggle source

Add a parser method

  • with the given name,

  • using start_re as start regexp

  • and, for span parsers, span_start as a String that can be used in a regexp and which identifies the starting character(s)

to the registry. The method name is automatically derived from the name or can explicitly be set by using the meth_name parameter.

    # File lib/kramdown/parser/kramdown.rb
322 def self.define_parser(name, start_re, span_start = nil, meth_name = "parse_#{name}")
323   raise "A parser with the name #{name} already exists!" if @@parsers.has_key?(name)
324   @@parsers[name] = Data.new(name, start_re, span_start, meth_name)
325 end
has_parser?(name) click to toggle source

Return true if there is a parser called name.

    # File lib/kramdown/parser/kramdown.rb
333 def self.has_parser?(name)
334   @@parsers.has_key?(name)
335 end
parser(name = nil) click to toggle source

Return the Data structure for the parser name.

    # File lib/kramdown/parser/kramdown.rb
328 def self.parser(name = nil)
329   @@parsers[name]
330 end

Private Class Methods

new(source, options) click to toggle source

Create a new Kramdown parser object with the given options.

Calls superclass method Kramdown::Parser::Base::new
   # File lib/kramdown/parser/kramdown.rb
64 def initialize(source, options)
65   super
66 
67   reset_env
68 
69   @alds = {}
70   @footnotes = {}
71   @link_defs = {}
72   update_link_definitions(@options[:link_defs])
73 
74   @block_parsers = [:blank_line, :codeblock, :codeblock_fenced, :blockquote, :atx_header,
75                     :horizontal_rule, :list, :definition_list, :block_html, :setext_header,
76                     :block_math, :table, :footnote_definition, :link_definition, :abbrev_definition,
77                     :block_extensions, :eob_marker, :paragraph]
78   @span_parsers =  [:emphasis, :codespan, :autolink, :span_html, :footnote_marker, :link, :smart_quotes, :inline_math,
79                    :span_extensions, :html_entity, :typographic_syms, :line_break, :escaped_chars]
80 
81 end
new(source, options) click to toggle source

Initialize the parser object with the source string and the parsing options.

The @root element, the @warnings array and @text_type (specifies the default type for newly created text nodes) are automatically initialized.

   # File lib/kramdown/parser/base.rb
51 def initialize(source, options)
52   @source = source
53   @options = Kramdown::Options.merge(options)
54   @root = Element.new(:root, nil, nil, :encoding => (source.encoding rescue nil), :location => 1,
55                       :options => {}, :abbrev_defs => {}, :abbrev_attr => {})
56   @warnings = []
57   @text_type = :text
58 end

Public Instance Methods

after_block_boundary?() click to toggle source

Return true if we are after a block boundary.

   # File lib/kramdown/parser/kramdown/block_boundary.rb
20 def after_block_boundary?
21   !@tree.children.last || @tree.children.last.type == :blank ||
22     (@tree.children.last.type == :eob && @tree.children.last.value.nil?) || @block_ial
23 end
before_block_boundary?() click to toggle source

Return true if we are before a block boundary.

   # File lib/kramdown/parser/kramdown/block_boundary.rb
26 def before_block_boundary?
27   @src.check(self.class::BLOCK_BOUNDARY)
28 end
correct_abbreviations_attributes() click to toggle source

Correct abbreviation attributes.

   # File lib/kramdown/parser/kramdown/abbreviation.rb
30 def correct_abbreviations_attributes
31   @root.options[:abbrev_attr].keys.each do |k|
32     @root.options[:abbrev_attr][k] = @root.options[:abbrev_attr][k].attr
33   end
34 end
handle_extension(name, opts, body, type, line_no = nil) click to toggle source
    # File lib/kramdown/parser/kramdown/extensions.rb
 94 def handle_extension(name, opts, body, type, line_no = nil)
 95   case name
 96   when 'comment'
 97     @tree.children << Element.new(:comment, body, nil, :category => type, :location => line_no) if body.kind_of?(String)
 98     true
 99   when 'nomarkdown'
100     @tree.children << Element.new(:raw, body, nil, :category => type, :location => line_no, :type => opts['type'].to_s.split(/\s+/)) if body.kind_of?(String)
101     true
102   when 'options'
103     opts.select do |k,v|
104       k = k.to_sym
105       if Kramdown::Options.defined?(k)
106         begin
107           val = Kramdown::Options.parse(k, v)
108           @options[k] = val
109           (@root.options[:options] ||= {})[k] = val
110         rescue
111         end
112         false
113       else
114         true
115       end
116     end.each do |k,v|
117       warning("Unknown kramdown option '#{k}'")
118     end
119     @tree.children << new_block_el(:eob, :extension) if type == :block
120     true
121   else
122     false
123   end
124 end
handle_kramdown_html_tag(el, closed, handle_body) click to toggle source
   # File lib/kramdown/parser/kramdown/html.rb
24 def handle_kramdown_html_tag(el, closed, handle_body)
25   if @block_ial
26     el.options[:ial] = @block_ial
27     @block_ial = nil
28   end
29 
30   content_model = if @tree.type != :html_element || @tree.options[:content_model] != :raw
31                     (@options[:parse_block_html] ? HTML_CONTENT_MODEL[el.value] : :raw)
32                   else
33                     :raw
34                   end
35   if val = HTML_MARKDOWN_ATTR_MAP[el.attr.delete('markdown')]
36     content_model = (val == :default ? HTML_CONTENT_MODEL[el.value] : val)
37   end
38 
39   @src.scan(TRAILING_WHITESPACE) if content_model == :block
40   el.options[:content_model] = content_model
41   el.options[:is_closed] = closed
42 
43   if !closed && handle_body
44     if content_model == :block
45       if !parse_blocks(el)
46         warning("Found no end tag for '#{el.value}' (line #{el.options[:location]}) - auto-closing it")
47       end
48     elsif content_model == :span
49       curpos = @src.pos
50       if @src.scan_until(/(?=<\/#{el.value}\s*>)/mi)
51         add_text(extract_string(curpos...@src.pos, @src), el)
52         @src.scan(HTML_TAG_CLOSE_RE)
53       else
54         add_text(@src.rest, el)
55         @src.terminate
56         warning("Found no end tag for '#{el.value}' (line #{el.options[:location]}) - auto-closing it")
57       end
58     else
59       parse_raw_html(el, &method(:handle_kramdown_html_tag))
60     end
61     @src.scan(TRAILING_WHITESPACE) unless (@tree.type == :html_element && @tree.options[:content_model] == :raw)
62   end
63 end
paragraph_end() click to toggle source
   # File lib/kramdown/parser/kramdown/paragraph.rb
55 def paragraph_end
56   self.class::PARAGRAPH_END
57 end
parse() click to toggle source

The source string provided on initialization is parsed into the @root element.

    # File lib/kramdown/parser/kramdown.rb
 86 def parse
 87   configure_parser
 88   parse_blocks(@root, adapt_source(source))
 89   update_tree(@root)
 90   correct_abbreviations_attributes
 91   replace_abbreviations(@root)
 92   @footnotes.each do |name,data|
 93     update_tree(data[:content])
 94     replace_abbreviations(data[:content])
 95   end
 96   @footnotes.each do |name, data|
 97     next if data.key?(:marker)
 98     line = data[:content].options[:location]
 99     warning("Footnote definition for '#{name}' on line #{line} is unreferenced - ignoring")
100   end
101 end
parse_abbrev_definition() click to toggle source

Parse the link definition at the current location.

   # File lib/kramdown/parser/kramdown/abbreviation.rb
16 def parse_abbrev_definition
17   start_line_number = @src.current_line_number
18   @src.pos += @src.matched_size
19   abbrev_id, abbrev_text = @src[1], @src[2]
20   abbrev_text.strip!
21   warning("Duplicate abbreviation ID '#{abbrev_id}' on line #{start_line_number} - overwriting") if @root.options[:abbrev_defs][abbrev_id]
22   @tree.children << new_block_el(:eob, :abbrev_def)
23   @root.options[:abbrev_defs][abbrev_id] = abbrev_text
24   @root.options[:abbrev_attr][abbrev_id] = @tree.children.last
25   true
26 end
parse_attribute_list(str, opts) click to toggle source

Parse the string str and extract all attributes and add all found attributes to the hash opts.

   # File lib/kramdown/parser/kramdown/extensions.rb
17 def parse_attribute_list(str, opts)
18   return if str.strip.empty? || str.strip == ':'
19   attrs = str.scan(ALD_TYPE_ANY)
20   attrs.each do |key, sep, val, ref, id_and_or_class, _, _|
21     if ref
22       (opts[:refs] ||= []) << ref
23     elsif id_and_or_class
24       id_and_or_class.scan(ALD_TYPE_ID_OR_CLASS).each do |id_attr, class_attr|
25         if class_attr
26           opts[IAL_CLASS_ATTR] = (opts[IAL_CLASS_ATTR] || '') << " #{class_attr}"
27           opts[IAL_CLASS_ATTR].lstrip!
28         else
29           opts['id'] = id_attr
30         end
31       end
32     else
33       val.gsub!(/\\(\}|#{sep})/, "\\1")
34       opts[key] = val
35     end
36   end
37   warning("No or invalid attributes found in IAL/ALD content: #{str}") if attrs.length == 0
38 end
parse_atx_header() click to toggle source

Parse the Atx header at the current location.

   # File lib/kramdown/parser/kramdown/header.rb
31 def parse_atx_header
32   return false if !after_block_boundary?
33   text, id = parse_header_contents
34   text.sub!(/[\t ]#+\z/, '') && text.rstrip!
35   return false if text.empty?
36   add_header(@src["level"].length, text, id)
37   true
38 end
parse_blank_line() click to toggle source

Parse the blank line at the current postition.

   # File lib/kramdown/parser/kramdown/blank_line.rb
16 def parse_blank_line
17   @src.pos += @src.matched_size
18   if @tree.children.last && @tree.children.last.type == :blank
19     @tree.children.last.value << @src.matched
20   else
21     @tree.children << new_block_el(:blank, @src.matched)
22   end
23   true
24 end
parse_block_extensions() click to toggle source

Parse one of the block extensions (ALD, block IAL or generic extension) at the current location.

    # File lib/kramdown/parser/kramdown/extensions.rb
152 def parse_block_extensions
153   if @src.scan(ALD_START)
154     parse_attribute_list(@src[2], @alds[@src[1]] ||= Utils::OrderedHash.new)
155     @tree.children << new_block_el(:eob, :ald)
156     true
157   elsif @src.check(EXT_BLOCK_START)
158     parse_extension_start_tag(:block)
159   elsif @src.scan(IAL_BLOCK_START)
160     if @tree.children.last && @tree.children.last.type != :blank &&
161         (@tree.children.last.type != :eob || [:link_def, :abbrev_def, :footnote_def].include?(@tree.children.last.value))
162       parse_attribute_list(@src[1], @tree.children.last.options[:ial] ||= Utils::OrderedHash.new)
163       @tree.children << new_block_el(:eob, :ial) unless @src.check(IAL_BLOCK_START)
164     else
165       parse_attribute_list(@src[1], @block_ial ||= Utils::OrderedHash.new)
166     end
167     true
168   else
169     false
170   end
171 end
parse_block_html() click to toggle source

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

   # File lib/kramdown/parser/kramdown/html.rb
69 def parse_block_html
70   line = @src.current_line_number
71   if result = @src.scan(HTML_COMMENT_RE)
72     @tree.children << Element.new(:xml_comment, result, nil, :category => :block, :location => line)
73     @src.scan(TRAILING_WHITESPACE)
74     true
75   elsif result = @src.scan(HTML_INSTRUCTION_RE)
76     @tree.children << Element.new(:xml_pi, result, nil, :category => :block, :location => line)
77     @src.scan(TRAILING_WHITESPACE)
78     true
79   else
80     if result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1].downcase)
81       @src.pos += @src.matched_size
82       handle_html_start_tag(line, &method(:handle_kramdown_html_tag))
83       Kramdown::Parser::Html::ElementConverter.convert(@root, @tree.children.last) if @options[:html_to_native]
84       true
85     elsif result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_CLOSE_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1].downcase)
86       name = @src[1].downcase
87 
88       if @tree.type == :html_element && @tree.value == name
89         @src.pos += @src.matched_size
90         throw :stop_block_parsing, :found
91       else
92         false
93       end
94     else
95       false
96     end
97   end
98 end
parse_block_math() click to toggle source

Parse the math block at the current location.

   # File lib/kramdown/parser/kramdown/math.rb
18 def parse_block_math
19   start_line_number = @src.current_line_number
20   if !after_block_boundary?
21     return false
22   elsif @src[1]
23     @src.scan(/^#{OPT_SPACE}\\/) if @src[3]
24     return false
25   end
26 
27   saved_pos = @src.save_pos
28   @src.pos += @src.matched_size
29   data = @src[2].strip
30   if before_block_boundary?
31     @tree.children << new_block_el(:math, data, nil, :category => :block, :location => start_line_number)
32     true
33   else
34     @src.revert_pos(saved_pos)
35     false
36   end
37 end
parse_blockquote() click to toggle source

Parse the blockquote at the current location.

   # File lib/kramdown/parser/kramdown/blockquote.rb
20 def parse_blockquote
21   start_line_number = @src.current_line_number
22   result = @src.scan(PARAGRAPH_MATCH)
23   while !@src.match?(self.class::LAZY_END)
24     result << @src.scan(PARAGRAPH_MATCH)
25   end
26   result.gsub!(BLOCKQUOTE_START, '')
27 
28   el = new_block_el(:blockquote, nil, nil, :location => start_line_number)
29   @tree.children << el
30   parse_blocks(el, result)
31   true
32 end
parse_codeblock() click to toggle source

Parse the indented codeblock at the current location.

   # File lib/kramdown/parser/kramdown/codeblock.rb
22 def parse_codeblock
23   start_line_number = @src.current_line_number
24   data = @src.scan(self.class::CODEBLOCK_MATCH)
25   data.gsub!(/\n( {0,3}\S)/, ' \\1')
26   data.gsub!(INDENT, '')
27   @tree.children << new_block_el(:codeblock, data, nil, :location => start_line_number)
28   true
29 end
parse_codeblock_fenced() click to toggle source

Parse the fenced codeblock at the current location.

   # File lib/kramdown/parser/kramdown/codeblock.rb
37 def parse_codeblock_fenced
38   if @src.check(self.class::FENCED_CODEBLOCK_MATCH)
39     start_line_number = @src.current_line_number
40     @src.pos += @src.matched_size
41     el = new_block_el(:codeblock, @src[5], nil, :location => start_line_number, :fenced => true)
42     lang = @src[3].to_s.strip
43     unless lang.empty?
44       el.options[:lang] = lang
45       el.attr['class'] = "language-#{@src[4]}"
46     end
47     @tree.children << el
48     true
49   else
50     false
51   end
52 end
parse_codespan() click to toggle source

Parse the codespan at the current scanner location.

   # File lib/kramdown/parser/kramdown/codespan.rb
16 def parse_codespan
17   start_line_number = @src.current_line_number
18   result = @src.scan(CODESPAN_DELIMITER)
19   simple = (result.length == 1)
20   saved_pos = @src.save_pos
21 
22   if simple && @src.pre_match =~ /\s\Z/ && @src.match?(/\s/)
23     add_text(result)
24     return
25   end
26 
27   if text = @src.scan_until(/#{result}/)
28     text.sub!(/#{result}\Z/, '')
29     if !simple
30       text = text[1..-1] if text[0..0] == ' '
31       text = text[0..-2] if text[-1..-1] == ' '
32     end
33     @tree.children << Element.new(:codespan, text, nil, :location => start_line_number)
34   else
35     @src.revert_pos(saved_pos)
36     add_text(result)
37   end
38 end
parse_definition_list() click to toggle source

Parse the ordered or unordered list at the current location.

    # File lib/kramdown/parser/kramdown/list.rb
153 def parse_definition_list
154   children = @tree.children
155   if !children.last || (children.length == 1 && children.last.type != :p ) ||
156       (children.length >= 2 && children[-1].type != :p && (children[-1].type != :blank || children[-1].value != "\n" || children[-2].type != :p))
157     return false
158   end
159 
160   first_as_para = false
161   deflist = new_block_el(:dl)
162   para = @tree.children.pop
163   if para.type == :blank
164     para = @tree.children.pop
165     first_as_para = true
166   end
167   deflist.options[:location] = para.options[:location] # take location from preceding para which is the first definition term
168   para.children.first.value.split(/\n/).each do |term|
169     el = Element.new(:dt, nil, nil, :location => @src.current_line_number)
170     term.sub!(self.class::LIST_ITEM_IAL) do
171       parse_attribute_list($1, el.options[:ial] ||= {})
172       ''
173     end
174     el.options[:raw_text] = term
175     el.children << Element.new(:raw_text, term)
176     deflist.children << el
177   end
178   deflist.options[:ial] = para.options[:ial]
179 
180   item = nil
181   content_re, lazy_re, indent_re = nil
182   def_start_re = DEFINITION_LIST_START
183   last_is_blank = false
184   while !@src.eos?
185     start_line_number = @src.current_line_number
186     if @src.scan(def_start_re)
187       item = Element.new(:dd, nil, nil, :location => start_line_number)
188       item.options[:first_as_para] = first_as_para
189       item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
190       deflist.children << item
191 
192       item.value.sub!(self.class::LIST_ITEM_IAL) do |match|
193         parse_attribute_list($1, item.options[:ial] ||= {})
194         ''
195       end
196 
197       def_start_re = /^( {0,#{[3, indentation - 1].min}}:)([\t| ].*?\n)/
198       first_as_para = false
199       last_is_blank = false
200     elsif @src.check(EOB_MARKER)
201       break
202     elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
203       result.sub!(/^(\t+)/) { " "*($1 ? 4*$1.length : 0) }
204       result.sub!(indent_re, '')
205       item.value << result
206       first_as_para = false
207       last_is_blank = false
208     elsif result = @src.scan(BLANK_LINE)
209       first_as_para = true
210       item.value << result
211       last_is_blank = true
212     else
213       break
214     end
215   end
216 
217   last = nil
218   deflist.children.each do |it|
219     next if it.type == :dt
220 
221     parse_blocks(it, it.value)
222     it.value = nil
223     next if it.children.size == 0
224 
225     if it.children.last.type == :blank
226       last = it.children.pop
227     else
228       last = nil
229     end
230 
231     if it.children.first && it.children.first.type == :p && !it.options.delete(:first_as_para)
232       it.children.first.children.first.value << "\n" if it.children.size > 1
233       it.children.first.options[:transparent] = true
234     end
235   end
236 
237   if @tree.children.length >= 1 && @tree.children.last.type == :dl
238     @tree.children[-1].children.concat(deflist.children)
239   elsif @tree.children.length >= 2 && @tree.children[-1].type == :blank && @tree.children[-2].type == :dl
240     @tree.children.pop
241     @tree.children[-1].children.concat(deflist.children)
242   else
243     @tree.children << deflist
244   end
245 
246   @tree.children << last if !last.nil?
247 
248   true
249 end
parse_emphasis() click to toggle source

Parse the emphasis at the current location.

   # File lib/kramdown/parser/kramdown/emphasis.rb
16 def parse_emphasis
17   start_line_number = @src.current_line_number
18   saved_pos = @src.save_pos
19 
20   result = @src.scan(EMPHASIS_START)
21   element = (result.length == 2 ? :strong : :em)
22   type = result[0..0]
23 
24   if (type == '_' && @src.pre_match =~ /[[:alpha:]-]\z/) || @src.check(/\s/) ||
25       @tree.type == element || @stack.any? {|el, _| el.type == element}
26     add_text(result)
27     return
28   end
29 
30   sub_parse = lambda do |delim, elem|
31     el = Element.new(elem, nil, nil, :location => start_line_number)
32     stop_re = /#{Regexp.escape(delim)}/
33     found = parse_spans(el, stop_re) do
34       (@src.pre_match[-1, 1] !~ /\s/) &&
35         (elem != :em || !@src.match?(/#{Regexp.escape(delim*2)}(?!#{Regexp.escape(delim)})/)) &&
36         (type != '_' || !@src.match?(/#{Regexp.escape(delim)}[[:alnum:]]/)) && el.children.size > 0
37     end
38     [found, el, stop_re]
39   end
40 
41   found, el, stop_re = sub_parse.call(result, element)
42   if !found && element == :strong && @tree.type != :em
43     @src.revert_pos(saved_pos)
44     @src.pos += 1
45     found, el, stop_re = sub_parse.call(type, :em)
46   end
47   if found
48     @src.scan(stop_re)
49     @tree.children << el
50   else
51     @src.revert_pos(saved_pos)
52     @src.pos += result.length
53     add_text(result)
54   end
55 end
parse_eob_marker() click to toggle source

Parse the EOB marker at the current location.

   # File lib/kramdown/parser/kramdown/eob.rb
16 def parse_eob_marker
17   @src.pos += @src.matched_size
18   @tree.children << new_block_el(:eob)
19   true
20 end
parse_escaped_chars() click to toggle source

Parse the backslash-escaped character at the current location.

   # File lib/kramdown/parser/kramdown/escaped_chars.rb
16 def parse_escaped_chars
17   @src.pos += @src.matched_size
18   add_text(@src[1])
19 end
parse_extension_start_tag(type) click to toggle source

Parse the generic extension at the current point. The parameter type can either be :block or :span depending whether we parse a block or span extension tag.

   # File lib/kramdown/parser/kramdown/extensions.rb
55 def parse_extension_start_tag(type)
56   saved_pos = @src.save_pos
57   start_line_number = @src.current_line_number
58   @src.pos += @src.matched_size
59 
60   error_block = lambda do |msg|
61     warning(msg)
62     @src.revert_pos(saved_pos)
63     add_text(@src.getch) if type == :span
64     false
65   end
66 
67   if @src[4] || @src.matched == '{:/}'
68     name = (@src[4] ? "for '#{@src[4]}' " : '')
69     return error_block.call("Invalid extension stop tag #{name} found on line #{start_line_number} - ignoring it")
70   end
71 
72   ext = @src[1]
73   opts = {}
74   body = nil
75   parse_attribute_list(@src[2] || '', opts)
76 
77   if !@src[3]
78     stop_re = (type == :block ? /#{EXT_BLOCK_STOP_STR % ext}/ : /#{EXT_STOP_STR % ext}/)
79     if result = @src.scan_until(stop_re)
80       body = result.sub!(stop_re, '')
81       body.chomp! if type == :block
82     else
83       return error_block.call("No stop tag for extension '#{ext}' found on line #{start_line_number} - ignoring it")
84     end
85   end
86 
87   if !handle_extension(ext, opts, body, type, start_line_number)
88     error_block.call("Invalid extension with name '#{ext}' specified on line #{start_line_number} - ignoring it")
89   else
90     true
91   end
92 end
parse_first_list_line(indentation, content) click to toggle source

Used for parsing the first line of a list item or a definition, i.e. the line with list item marker or the definition marker.

   # File lib/kramdown/parser/kramdown/list.rb
31 def parse_first_list_line(indentation, content)
32   if content =~ self.class::LIST_ITEM_IAL_CHECK
33     indentation = 4
34   else
35     while content =~ /^ *\t/
36       temp = content.scan(/^ */).first.length + indentation
37       content.sub!(/^( *)(\t+)/) {$1 << " "*(4 - (temp % 4) + ($2.length - 1)*4)}
38     end
39     indentation += content[/^ */].length
40   end
41   content.sub!(/^\s*/, '')
42 
43   [content, indentation, *PARSE_FIRST_LIST_LINE_REGEXP_CACHE[indentation]]
44 end
parse_footnote_definition() click to toggle source

Parse the foot note definition at the current location.

   # File lib/kramdown/parser/kramdown/footnote.rb
20 def parse_footnote_definition
21   start_line_number = @src.current_line_number
22   @src.pos += @src.matched_size
23 
24   el = Element.new(:footnote_def, nil, nil, :location => start_line_number)
25   parse_blocks(el, @src[2].gsub(INDENT, ''))
26   warning("Duplicate footnote name '#{@src[1]}' on line #{start_line_number} - overwriting") if @footnotes[@src[1]]
27   @tree.children << new_block_el(:eob, :footnote_def)
28   (@footnotes[@src[1]] = {})[:content] = el
29   @footnotes[@src[1]][:eob] = @tree.children.last
30   true
31 end
parse_footnote_marker() click to toggle source

Parse the footnote marker at the current location.

   # File lib/kramdown/parser/kramdown/footnote.rb
38 def parse_footnote_marker
39   start_line_number = @src.current_line_number
40   @src.pos += @src.matched_size
41   fn_def = @footnotes[@src[1]]
42   if fn_def
43     if fn_def[:eob]
44       update_attr_with_ial(fn_def[:eob].attr, fn_def[:eob].options[:ial] || {})
45       fn_def[:attr] = fn_def[:eob].attr
46       fn_def[:options] = fn_def[:eob].options
47       fn_def.delete(:eob)
48     end
49     fn_def[:marker] ||= []
50     fn_def[:marker].push(Element.new(:footnote, fn_def[:content], fn_def[:attr],
51                                      fn_def[:options].merge(:name => @src[1], :location => start_line_number)))
52     @tree.children << fn_def[:marker].last
53   else
54     warning("Footnote definition for '#{@src[1]}' not found on line #{start_line_number}")
55     add_text(@src.matched)
56   end
57 end
parse_horizontal_rule() click to toggle source

Parse the horizontal rule at the current location.

   # File lib/kramdown/parser/kramdown/horizontal_rule.rb
16 def parse_horizontal_rule
17   start_line_number = @src.current_line_number
18   @src.pos += @src.matched_size
19   @tree.children << new_block_el(:hr, nil, nil, :location => start_line_number)
20   true
21 end
parse_html_entity() click to toggle source

Parse the HTML entity at the current location.

   # File lib/kramdown/parser/kramdown/html_entity.rb
16 def parse_html_entity
17   start_line_number = @src.current_line_number
18   @src.pos += @src.matched_size
19   begin
20     @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity(@src[1] || (@src[2] && @src[2].to_i) || @src[3].hex),
21                                   nil, :original => @src.matched, :location => start_line_number)
22   rescue ::Kramdown::Error
23     @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('amp'),
24                                   nil, :location => start_line_number)
25     add_text(@src.matched[1..-1])
26   end
27 end
parse_inline_math() click to toggle source

Parse the inline math at the current location.

   # File lib/kramdown/parser/kramdown/math.rb
44 def parse_inline_math
45   start_line_number = @src.current_line_number
46   @src.pos += @src.matched_size
47   @tree.children << Element.new(:math, @src[1].strip, nil, :category => :span, :location => start_line_number)
48 end
parse_line_break() click to toggle source

Parse the line break at the current location.

   # File lib/kramdown/parser/kramdown/line_break.rb
16 def parse_line_break
17   @tree.children << Element.new(:br, nil, nil, :location => @src.current_line_number)
18   @src.pos += @src.matched_size
19 end
parse_list() click to toggle source

Parse the ordered or unordered list at the current location.

    # File lib/kramdown/parser/kramdown/list.rb
 52 def parse_list
 53   start_line_number = @src.current_line_number
 54   type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL])
 55   list = new_block_el(type, nil, nil, :location => start_line_number)
 56 
 57   item = nil
 58   content_re, lazy_re, indent_re = nil
 59   eob_found = false
 60   nested_list_found = false
 61   last_is_blank = false
 62   while !@src.eos?
 63     start_line_number = @src.current_line_number
 64     if last_is_blank && @src.check(HR_START)
 65       break
 66     elsif @src.scan(EOB_MARKER)
 67       eob_found = true
 68       break
 69     elsif @src.scan(list_start_re)
 70       item = Element.new(:li, nil, nil, :location => start_line_number)
 71       item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
 72       list.children << item
 73 
 74       item.value.sub!(self.class::LIST_ITEM_IAL) do |match|
 75         parse_attribute_list($1, item.options[:ial] ||= {})
 76         ''
 77       end
 78 
 79       list_start_re = (type == :ul ? /^( {0,#{[3, indentation - 1].min}}[+*-])([\t| ].*?\n)/ :
 80                        /^( {0,#{[3, indentation - 1].min}}\d+\.)([\t| ].*?\n)/)
 81       nested_list_found = (item.value =~ LIST_START)
 82       last_is_blank = false
 83       item.value = [item.value]
 84     elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
 85       result.sub!(/^(\t+)/) { " " * 4 * $1.length }
 86       indentation_found = result.sub!(indent_re, '')
 87       if !nested_list_found && indentation_found && result =~ LIST_START
 88         item.value << ''
 89         nested_list_found = true
 90       elsif nested_list_found && !indentation_found && result =~ LIST_START
 91         result = " " * (indentation + 4) << result
 92       end
 93       item.value.last << result
 94       last_is_blank = false
 95     elsif result = @src.scan(BLANK_LINE)
 96       nested_list_found = true
 97       last_is_blank = true
 98       item.value.last << result
 99     else
100       break
101     end
102   end
103 
104   @tree.children << list
105 
106   last = nil
107   list.children.each do |it|
108     temp = Element.new(:temp, nil, nil, :location => it.options[:location])
109 
110     env = save_env
111     location = it.options[:location]
112     it.value.each do |val|
113       @src = ::Kramdown::Utils::StringScanner.new(val, location)
114       parse_blocks(temp)
115       location = @src.current_line_number
116     end
117     restore_env(env)
118 
119     it.children = temp.children
120     it.value = nil
121     next if it.children.size == 0
122 
123     # Handle the case where an EOB marker is inserted by a block IAL for the first paragraph
124     it.children.delete_at(1) if it.children.first.type == :p &&
125       it.children.length >= 2 && it.children[1].type == :eob && it.children.first.options[:ial]
126 
127     if it.children.first.type == :p &&
128         (it.children.length < 2 || it.children[1].type != :blank ||
129          (it == list.children.last && it.children.length == 2 && !eob_found)) &&
130         (list.children.last != it || list.children.size == 1 ||
131          list.children[0..-2].any? {|cit| !cit.children.first || cit.children.first.type != :p || cit.children.first.options[:transparent]})
132       it.children.first.children.first.value << "\n" if it.children.size > 1 && it.children[1].type != :blank
133       it.children.first.options[:transparent] = true
134     end
135 
136     if it.children.last.type == :blank
137       last = it.children.pop
138     else
139       last = nil
140     end
141   end
142 
143   @tree.children << last if !last.nil? && !eob_found
144 
145   true
146 end
parse_paragraph() click to toggle source

Parse the paragraph at the current location.

   # File lib/kramdown/parser/kramdown/paragraph.rb
30 def parse_paragraph
31   pos = @src.pos
32   start_line_number = @src.current_line_number
33   result = @src.scan(PARAGRAPH_MATCH)
34   while !@src.match?(paragraph_end)
35     result << @src.scan(PARAGRAPH_MATCH)
36   end
37   result.rstrip!
38   if @tree.children.last && @tree.children.last.type == :p
39     last_item_in_para = @tree.children.last.children.last
40     if last_item_in_para && last_item_in_para.type == @text_type
41       joiner = (extract_string((pos - 3)...pos, @src) == "  \n" ? "  \n" : "\n")
42       last_item_in_para.value << joiner << result
43     else
44       add_text(result, @tree.children.last)
45     end
46   else
47     @tree.children << new_block_el(:p, nil, nil, :location => start_line_number)
48     result.lstrip!
49     add_text(result, @tree.children.last)
50   end
51   true
52 end
parse_setext_header() click to toggle source

Parse the Setext header at the current location.

   # File lib/kramdown/parser/kramdown/header.rb
18 def parse_setext_header
19   return false if !after_block_boundary?
20   text, id = parse_header_contents
21   return false if text.empty?
22   add_header(@src["level"] == '-' ? 2 : 1, text, id)
23   true
24 end
parse_smart_quotes() click to toggle source

Parse the smart quotes at current location.

    # File lib/kramdown/parser/kramdown/smart_quotes.rb
157 def parse_smart_quotes
158   start_line_number = @src.current_line_number
159   substs = SQ_RULES.find {|reg, subst| @src.scan(reg)}[1]
160   substs.each do |subst|
161     if subst.kind_of?(Integer)
162       add_text(@src[subst])
163     else
164       val = SQ_SUBSTS[[subst, @src[subst.to_s[-1,1].to_i]]] || subst
165       @tree.children << Element.new(:smart_quote, val, nil, :location => start_line_number)
166     end
167   end
168 end
parse_span_extensions() click to toggle source

Parse the extension span at the current location.

    # File lib/kramdown/parser/kramdown/extensions.rb
180 def parse_span_extensions
181   if @src.check(EXT_SPAN_START)
182     parse_extension_start_tag(:span)
183   elsif @src.check(IAL_SPAN_START)
184     if @tree.children.last && @tree.children.last.type != :text
185       @src.pos += @src.matched_size
186       attr = Utils::OrderedHash.new
187       parse_attribute_list(@src[1], attr)
188       update_ial_with_ial(@tree.children.last.options[:ial] ||= Utils::OrderedHash.new, attr)
189       update_attr_with_ial(@tree.children.last.attr, attr)
190     else
191       warning("Found span IAL after text - ignoring it")
192       add_text(@src.getch)
193     end
194   else
195     add_text(@src.getch)
196   end
197 end
parse_span_html() click to toggle source

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

    # File lib/kramdown/parser/kramdown/html.rb
105 def parse_span_html
106   line = @src.current_line_number
107   if result = @src.scan(HTML_COMMENT_RE)
108     @tree.children << Element.new(:xml_comment, result, nil, :category => :span, :location => line)
109   elsif result = @src.scan(HTML_INSTRUCTION_RE)
110     @tree.children << Element.new(:xml_pi, result, nil, :category => :span, :location => line)
111   elsif result = @src.scan(HTML_TAG_CLOSE_RE)
112     warning("Found invalidly used HTML closing tag for '#{@src[1]}' on line #{line}")
113     add_text(result)
114   elsif result = @src.scan(HTML_TAG_RE)
115     tag_name = @src[1]
116     tag_name.downcase! if HTML_ELEMENT[tag_name.downcase]
117     if HTML_BLOCK_ELEMENTS.include?(tag_name)
118       warning("Found block HTML tag '#{tag_name}' in span-level text on line #{line}")
119       add_text(result)
120       return
121     end
122 
123     attrs = parse_html_attributes(@src[2], line, HTML_ELEMENT[tag_name])
124     attrs.each {|name, value| value.gsub!(/\n+/, ' ')}
125 
126     do_parsing = (HTML_CONTENT_MODEL[tag_name] == :raw || @tree.options[:content_model] == :raw ? false : @options[:parse_span_html])
127     if val = HTML_MARKDOWN_ATTR_MAP[attrs.delete('markdown')]
128       if val == :block
129         warning("Cannot use block-level parsing in span-level HTML tag (line #{line}) - using default mode")
130       elsif val == :span
131         do_parsing = true
132       elsif val == :default
133         do_parsing = HTML_CONTENT_MODEL[tag_name] != :raw
134       elsif val == :raw
135         do_parsing = false
136       end
137     end
138 
139     el = Element.new(:html_element, tag_name, attrs, :category => :span, :location => line,
140                      :content_model => (do_parsing ? :span : :raw), :is_closed => !!@src[4])
141     @tree.children << el
142     stop_re = /<\/#{Regexp.escape(tag_name)}\s*>/
143     stop_re = Regexp.new(stop_re.source, Regexp::IGNORECASE) if HTML_ELEMENT[tag_name]
144     if !@src[4] && !HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
145       if parse_spans(el, stop_re, (do_parsing ? nil : [:span_html]))
146         @src.scan(stop_re)
147       else
148         warning("Found no end tag for '#{el.value}' (line #{line}) - auto-closing it")
149         add_text(@src.rest, el)
150         @src.terminate
151       end
152     end
153     Kramdown::Parser::Html::ElementConverter.convert(@root, el) if @options[:html_to_native]
154   else
155     add_text(@src.getch)
156   end
157 end
parse_table() click to toggle source

Parse the table at the current location.

    # File lib/kramdown/parser/kramdown/table.rb
 24 def parse_table
 25   return false if !after_block_boundary?
 26 
 27   saved_pos = @src.save_pos
 28   orig_pos = @src.pos
 29   table = new_block_el(:table, nil, nil, :alignment => [], :location => @src.current_line_number)
 30   leading_pipe = (@src.check(TABLE_LINE) =~ /^\s*\|/)
 31   @src.scan(TABLE_SEP_LINE)
 32 
 33   rows = []
 34   has_footer = false
 35   columns = 0
 36 
 37   add_container = lambda do |type, force|
 38     if !has_footer || type != :tbody || force
 39       cont = Element.new(type)
 40       cont.children, rows = rows, []
 41       table.children << cont
 42     end
 43   end
 44 
 45   while !@src.eos?
 46     break if !@src.check(TABLE_LINE)
 47     if @src.scan(TABLE_SEP_LINE)
 48       if rows.empty?
 49         # nothing to do, ignoring multiple consecutive separator lines
 50       elsif table.options[:alignment].empty? && !has_footer
 51         add_container.call(:thead, false)
 52         table.options[:alignment] = @src[1].scan(TABLE_HSEP_ALIGN).map do |left, right|
 53           (left.empty? && right.empty? && :default) || (right.empty? && :left) || (left.empty? && :right) || :center
 54         end
 55       else # treat as normal separator line
 56         add_container.call(:tbody, false)
 57       end
 58     elsif @src.scan(TABLE_FSEP_LINE)
 59       add_container.call(:tbody, true) if !rows.empty?
 60       has_footer = true
 61     elsif @src.scan(TABLE_ROW_LINE)
 62       trow = Element.new(:tr)
 63 
 64       # parse possible code spans on the line and correctly split the line into cells
 65       env = save_env
 66       cells = []
 67       @src[1].split(/(<code.*?>.*?<\/code>)/).each_with_index do |str, i|
 68         if i % 2 == 1
 69           (cells.empty? ? cells : cells.last) << str
 70         else
 71           reset_env(:src => Kramdown::Utils::StringScanner.new(str, @src.current_line_number))
 72           root = Element.new(:root)
 73           parse_spans(root, nil, [:codespan])
 74 
 75           root.children.each do |c|
 76             if c.type == :raw_text
 77               f, *l = c.value.split(/(?<!\\)\|/, -1).map {|t| t.gsub(/\\\|/, '|')}
 78               (cells.empty? ? cells : cells.last) << f
 79               cells.concat(l)
 80             else
 81               delim = (c.value.scan(/`+/).max || '') + '`'
 82               tmp = "#{delim}#{' ' if delim.size > 1}#{c.value}#{' ' if delim.size > 1}#{delim}"
 83               (cells.empty? ? cells : cells.last) << tmp
 84             end
 85           end
 86         end
 87       end
 88       restore_env(env)
 89 
 90       cells.shift if leading_pipe && cells.first.strip.empty?
 91       cells.pop if cells.last.strip.empty?
 92       cells.each do |cell_text|
 93         tcell = Element.new(:td)
 94         tcell.children << Element.new(:raw_text, cell_text.strip)
 95         trow.children << tcell
 96       end
 97       columns = [columns, cells.length].max
 98       rows << trow
 99     else
100       break
101     end
102   end
103 
104   if !before_block_boundary?
105     @src.revert_pos(saved_pos)
106     return false
107   end
108 
109   # Parse all lines of the table with the code span parser
110   env = save_env
111   l_src = ::Kramdown::Utils::StringScanner.new(extract_string(orig_pos...(@src.pos-1), @src),
112                                                @src.current_line_number)
113   reset_env(:src => l_src)
114   root = Element.new(:root)
115   parse_spans(root, nil, [:codespan, :span_html])
116   restore_env(env)
117 
118   # Check if each line has at least one unescaped pipe that is not inside a code span/code
119   # HTML element
120   # Note: It doesn't matter that we parse *all* span HTML elements because the row splitting
121   # algorithm above only takes <code> elements into account!
122   pipe_on_line = false
123   while (c = root.children.shift)
124     next unless (lines = c.value)
125     lines = lines.split("\n")
126     if c.type == :codespan
127       if lines.size > 2 || (lines.size == 2 && !pipe_on_line)
128         break
129       elsif lines.size == 2 && pipe_on_line
130         pipe_on_line = false
131       end
132     else
133       break if lines.size > 1 && !pipe_on_line && lines.first !~ /^#{TABLE_PIPE_CHECK}/
134       pipe_on_line = (lines.size > 1 ? false : pipe_on_line) || (lines.last =~ /^#{TABLE_PIPE_CHECK}/)
135     end
136   end
137   @src.revert_pos(saved_pos) and return false if !pipe_on_line
138 
139   add_container.call(has_footer ? :tfoot : :tbody, false) if !rows.empty?
140 
141   if !table.children.any? {|el| el.type == :tbody}
142     warning("Found table without body on line #{table.options[:location]} - ignoring it")
143     @src.revert_pos(saved_pos)
144     return false
145   end
146 
147   # adjust all table rows to have equal number of columns, same for alignment defs
148   table.children.each do |kind|
149     kind.children.each do |row|
150       (columns - row.children.length).times do
151         row.children << Element.new(:td)
152       end
153     end
154   end
155   if table.options[:alignment].length > columns
156     table.options[:alignment] = table.options[:alignment][0...columns]
157   else
158     table.options[:alignment] += [:default] * (columns - table.options[:alignment].length)
159   end
160 
161   @tree.children << table
162 
163   true
164 end
parse_typographic_syms() click to toggle source

Parse the typographic symbols at the current location.

   # File lib/kramdown/parser/kramdown/typographic_symbol.rb
21 def parse_typographic_syms
22   start_line_number = @src.current_line_number
23   @src.pos += @src.matched_size
24   val = TYPOGRAPHIC_SYMS_SUBST[@src.matched]
25   if val.kind_of?(Symbol)
26     @tree.children << Element.new(:typographic_sym, val, nil, :location => start_line_number)
27   elsif @src.matched == '\\<<'
28     @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt'),
29                                   nil, :location => start_line_number)
30     @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt'),
31                                   nil, :location => start_line_number)
32   else
33     @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt'),
34                                   nil, :location => start_line_number)
35     @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt'),
36                                   nil, :location => start_line_number)
37   end
38 end
replace_abbreviations(el, regexps = nil) click to toggle source

Replace the abbreviation text with elements.

   # File lib/kramdown/parser/kramdown/abbreviation.rb
37 def replace_abbreviations(el, regexps = nil)
38   return if @root.options[:abbrev_defs].empty?
39   if !regexps
40     sorted_abbrevs = @root.options[:abbrev_defs].keys.sort {|a,b| b.length <=> a.length}
41     regexps = [Regexp.union(*sorted_abbrevs.map {|k| /#{Regexp.escape(k)}/})]
42     regexps << /(?=(?:\W|^)#{regexps.first}(?!\w))/ # regexp should only match on word boundaries
43   end
44   el.children.map! do |child|
45     if child.type == :text
46       if child.value =~ regexps.first
47         result = []
48         strscan = Kramdown::Utils::StringScanner.new(child.value, child.options[:location])
49         text_lineno = strscan.current_line_number
50         while temp = strscan.scan_until(regexps.last)
51           abbr_lineno = strscan.current_line_number
52           abbr = strscan.scan(regexps.first) # begin of line case of abbr with \W char as first one
53           if abbr.nil?
54             temp << strscan.scan(/\W|^/)
55             abbr = strscan.scan(regexps.first)
56           end
57           result << Element.new(:text, temp, nil, :location => text_lineno)
58           result << Element.new(:abbreviation, abbr, nil, :location => abbr_lineno)
59           text_lineno = strscan.current_line_number
60         end
61         result << Element.new(:text, strscan.rest, nil, :location => text_lineno)
62       else
63         child
64       end
65     else
66       replace_abbreviations(child, regexps)
67       child
68     end
69   end.flatten!
70 end
update_ial_with_ial(ial, opts) click to toggle source

Update the ial with the information from the inline attribute list opts.

   # File lib/kramdown/parser/kramdown/extensions.rb
41 def update_ial_with_ial(ial, opts)
42   (ial[:refs] ||= []) << opts[:refs]
43   opts.each do |k,v|
44     if k == IAL_CLASS_ATTR
45       ial[k] = (ial[k] || '') << " #{v}"
46       ial[k].lstrip!
47     elsif k.kind_of?(String)
48       ial[k] = v
49     end
50   end
51 end

Protected Instance Methods

add_header(level, text, id) click to toggle source
   # File lib/kramdown/parser/kramdown/header.rb
58 def add_header(level, text, id)
59   start_line_number = @src.current_line_number
60   @src.pos += @src.matched_size
61   el = new_block_el(:header, nil, nil, :level => level, :raw_text => text, :location => start_line_number)
62   add_text(text, el)
63   el.attr['id'] = id if id
64   @tree.children << el
65 end
configure_parser() click to toggle source

Adapt the object to allow parsing like specified in the options.

    # File lib/kramdown/parser/kramdown.rb
119 def configure_parser
120   @parsers = {}
121   (@block_parsers + @span_parsers).each do |name|
122     if self.class.has_parser?(name)
123       @parsers[name] = self.class.parser(name)
124     else
125       raise Kramdown::Error, "Unknown parser: #{name}"
126     end
127   end
128   @span_start, @span_start_re = span_parser_regexps
129 end
new_block_el(*args) click to toggle source

Create a new block-level element, taking care of applying a preceding block IAL if it exists. This method should always be used for creating a block-level element!

    # File lib/kramdown/parser/kramdown.rb
299 def new_block_el(*args)
300   el = Element.new(*args)
301   if @block_ial
302     el.options[:ial] = @block_ial
303     @block_ial = nil
304   end
305   el
306 end
parse_blocks(el, text = nil) click to toggle source

Parse all block-level elements in text into the element el.

    # File lib/kramdown/parser/kramdown.rb
138 def parse_blocks(el, text = nil)
139   @stack.push([@tree, @src, @block_ial])
140   @tree, @block_ial = el, nil
141   @src = (text.nil? ? @src : ::Kramdown::Utils::StringScanner.new(text, el.options[:location]))
142 
143   status = catch(:stop_block_parsing) do
144     while !@src.eos?
145       @block_parsers.any? do |name|
146         if @src.check(@parsers[name].start_re)
147           send(@parsers[name].method)
148         else
149           false
150         end
151       end || begin
152         warning('Warning: this should not occur - no block parser handled the line')
153         add_text(@src.scan(/.*\n/))
154       end
155     end
156   end
157 
158   @tree, @src, @block_ial = *@stack.pop
159   status
160 end
parse_header_contents() click to toggle source

Returns header text and optional ID.

   # File lib/kramdown/parser/kramdown/header.rb
46 def parse_header_contents
47   text = @src["contents"]
48   text.rstrip!
49   id_match = HEADER_ID.match(text)
50   if id_match
51     id = id_match["id"]
52     text = text[0...-id_match[0].length]
53     text.rstrip!
54   end
55   [text, id]
56 end
parse_spans(el, stop_re = nil, parsers = nil, text_type = @text_type) { |: true)| ... } click to toggle source

Parse all span-level elements in the source string of @src into el.

If the parameter stop_re (a regexp) is used, parsing is immediately stopped if the regexp matches and if no block is given or if a block is given and it returns true.

The parameter parsers can be used to specify the (span-level) parsing methods that should be used for parsing.

The parameter text_type specifies the type which should be used for created text nodes.

    # File lib/kramdown/parser/kramdown.rb
207 def parse_spans(el, stop_re = nil, parsers = nil, text_type = @text_type)
208   @stack.push([@tree, @text_type]) unless @tree.nil?
209   @tree, @text_type = el, text_type
210 
211   span_start = @span_start
212   span_start_re = @span_start_re
213   span_start, span_start_re = span_parser_regexps(parsers) if parsers
214   parsers = parsers || @span_parsers
215 
216   used_re = (stop_re.nil? ? span_start_re : /(?=#{Regexp.union(stop_re, span_start)})/)
217   stop_re_found = false
218   while !@src.eos? && !stop_re_found
219     if result = @src.scan_until(used_re)
220       add_text(result)
221       if stop_re && @src.check(stop_re)
222         stop_re_found = (block_given? ? yield : true)
223       end
224       processed = parsers.any? do |name|
225         if @src.check(@parsers[name].start_re)
226           send(@parsers[name].method)
227           true
228         else
229           false
230         end
231       end unless stop_re_found
232       add_text(@src.getch) if !processed && !stop_re_found
233     else
234       (add_text(@src.rest); @src.terminate) unless stop_re
235       break
236     end
237   end
238 
239   @tree, @text_type = @stack.pop
240 
241   stop_re_found
242 end
reset_env(opts = {}) click to toggle source

Reset the current parsing environment. The parameter env can be used to set initial values for one or more environment variables.

    # File lib/kramdown/parser/kramdown.rb
246 def reset_env(opts = {})
247   opts = {:text_type => :raw_text, :stack => []}.merge(opts)
248   @src = opts[:src]
249   @tree = opts[:tree]
250   @block_ial = opts[:block_ial]
251   @stack = opts[:stack]
252   @text_type = opts[:text_type]
253 end
restore_env(env) click to toggle source

Restore the current parsing environment.

    # File lib/kramdown/parser/kramdown.rb
261 def restore_env(env)
262   @src, @tree, @block_ial, @stack,  @text_type = *env
263 end
save_env() click to toggle source

Return the current parsing environment.

    # File lib/kramdown/parser/kramdown.rb
256 def save_env
257   [@src, @tree, @block_ial, @stack,  @text_type]
258 end
span_parser_regexps(parsers = @span_parsers) click to toggle source

Create the needed span parser regexps.

    # File lib/kramdown/parser/kramdown.rb
132 def span_parser_regexps(parsers = @span_parsers)
133   span_start = /#{parsers.map {|name| @parsers[name].span_start}.join('|')}/
134   [span_start, /(?=#{span_start})/]
135 end
update_attr_with_ial(attr, ial) click to toggle source

Update the given attributes hash attr with the information from the inline attribute list ial and all referenced ALDs.

    # File lib/kramdown/parser/kramdown.rb
267 def update_attr_with_ial(attr, ial)
268   ial[:refs].each do |ref|
269     update_attr_with_ial(attr, ref) if ref = @alds[ref]
270   end if ial[:refs]
271   ial.each do |k,v|
272     if k == IAL_CLASS_ATTR
273       attr[k] = (attr[k] || '') << " #{v}"
274       attr[k].lstrip!
275     elsif k.kind_of?(String)
276       attr[k] = v
277     end
278   end
279 end
update_raw_text(item) click to toggle source

Update the raw text for automatic ID generation.

    # File lib/kramdown/parser/kramdown.rb
282 def update_raw_text(item)
283   raw_text = ''
284 
285   append_text = lambda do |child|
286     if child.type == :text
287       raw_text << child.value
288     else
289       child.children.each {|c| append_text.call(c)}
290     end
291   end
292 
293   append_text.call(item)
294   item.options[:raw_text] = raw_text
295 end
update_tree(element) click to toggle source

Update the tree by parsing all :raw_text elements with the span-level parser (resets the environment) and by updating the attributes from the IALs.

    # File lib/kramdown/parser/kramdown.rb
164 def update_tree(element)
165   last_blank = nil
166   element.children.map! do |child|
167     if child.type == :raw_text
168       last_blank = nil
169       reset_env(:src => ::Kramdown::Utils::StringScanner.new(child.value, element.options[:location]),
170                 :text_type => :text)
171       parse_spans(child)
172       child.children
173     elsif child.type == :eob
174       update_attr_with_ial(child.attr, child.options[:ial]) if child.options[:ial]
175       []
176     elsif child.type == :blank
177       if last_blank
178         last_blank.value << child.value
179         []
180       else
181         last_blank = child
182         child
183       end
184     else
185       last_blank = nil
186       update_tree(child)
187       update_attr_with_ial(child.attr, child.options[:ial]) if child.options[:ial]
188       # DEPRECATED: option auto_id_stripping will be removed in 2.0 because then this will be
189       # the default behaviour
190       if child.type == :dt || (child.type == :header && @options[:auto_id_stripping])
191         update_raw_text(child)
192       end
193       child
194     end
195   end.flatten!
196 end