def read_span(src, escaped, exit_on_chars=nil, exit_on_strings=nil)
escaped = Array(escaped)
con = SpanContext.new
dquote_state = squote_state = :closed
c = d = prev_char = nil
while true
c = src.cur_char
if c && c =~ /[[:alnum:]]/
con.push_char src.shift_char
prev_char = c
next
end
break if Array(exit_on_chars).include?(c)
if Array(exit_on_strings).any? {|x| src.cur_chars_are x }
break unless !(['*', '_'] & Array(exit_on_strings)).empty? &&
['**', '__'].include?(src.cur_chars(2)) &&
!['***', '___'].include?(src.cur_chars(3))
end
next if check_span_extensions(src, con)
case c = src.cur_char
when ' '
if src.cur_chars_are " \n"
src.ignore_chars(3)
con.push_element md_br
prev_char = ' '
next
elsif src.cur_chars_are ' >>'
src.ignore_chars(3)
con.push_element md_entity('nbsp')
con.push_element md_entity('raquo')
elsif src.cur_chars(5) =~ / '\d\ds/
src.ignore_chars(2)
con.push_space
con.push_element md_entity('rsquo')
elsif src.cur_chars_are " '"
src.ignore_chars(2)
con.push_space
con.push_element md_entity('lsquo')
squote_state = :open
else
src.ignore_char
con.push_space
end
when "\n", "\t"
src.ignore_char
con.push_space
when '`'
read_inline_code(src, con)
when '<'
case d = src.next_char
when '<'
if src.cur_chars_are '<< '
src.ignore_chars(3)
con.push_element md_entity('laquo')
con.push_element md_entity('nbsp')
else
src.ignore_chars(2)
con.push_element md_entity('laquo')
end
when '!'
if src.cur_chars_are '<!--'
read_inline_html(src, con)
else
con.push_char src.shift_char
end
when '?'
read_xml_instr_span(src, con)
when ' ', "\t"
con.push_char src.shift_char
else
if src.next_matches(/<mailto:/) ||
src.next_matches(/<[\w\.]+\@/)
read_email_el(src, con)
elsif src.next_matches(/<\w+:/)
read_url_el(src, con)
elsif src.next_matches(/<\w/)
read_inline_html(src, con)
else
con.push_char src.shift_char
end
end
when '>'
if src.next_char == '>'
src.ignore_chars(2)
con.push_element md_entity('raquo')
else
con.push_char src.shift_char
end
when "\\"
d = src.next_char
if d == "'"
src.ignore_chars(2)
con.push_element md_entity('apos')
elsif d == '"'
src.ignore_chars(2)
con.push_element md_entity('quot')
elsif escaped.include? d
src.ignore_chars(2)
con.push_char d
else
con.push_char src.shift_char
end
when '['
if markdown_extra? && src.next_char == '^'
read_footnote_ref(src,con)
elsif IgnoreWikiLinks && src.next_char == '['
con.push_char src.shift_char
con.push_char src.shift_char
else
read_link(src, con)
end
when '!'
if src.next_char == '['
read_image(src, con)
else
con.push_char src.shift_char
end
when '&'
if m = src.read_regexp(/\&(\w+);/)
con.push_element md_entity(m[1])
elsif m = src.read_regexp(/\&\#(x)?(\w+);/)
num = m[1] ? m[2].hex : m[2].to_i
con.push_element md_entity(num)
else
con.push_char src.shift_char
end
when '*'
if !src.next_char
maruku_error "Opening * as last char.", src, con, 'Treating as literal'
con.push_char src.shift_char
else
follows = src.cur_chars(4)
if follows =~ /^\*\*\*[^\s\*]/
con.push_element read_emstrong(src, '***')
elsif follows =~ /^\*\*[^\s\*]/
con.push_element read_strong(src, '**')
elsif follows =~ /^\*[^\s\*]/
con.push_element read_em(src, '*')
else
con.push_char src.shift_char
end
end
when '_'
if !src.next_char
maruku_error "Opening _ as last char", src, con, 'Treating as literal'
con.push_char src.shift_char
else
if con.is_end?
follows = src.cur_chars(4)
if follows =~ /^\_\_\_[^\s\_]/
con.push_element read_emstrong(src, '___')
elsif follows =~ /^\_\_[^\s\_]/
con.push_element read_strong(src, '__')
elsif follows =~ /^\_[^\s\_]/
con.push_element read_em(src, '_')
else
con.push_char src.shift_char
end
else
con.push_char src.shift_char
end
end
when '{'
if ['#', '.', ':'].include? src.next_char
src.ignore_char
interpret_extension(src, con, '}')
src.ignore_char
else
con.push_char src.shift_char
end
when nil
maruku_error( ("Unclosed span (waiting for %s" +
"#{exit_on_strings.inspect})") %
[ exit_on_chars ? "#{exit_on_chars.inspect} or" : "" ],
src, con)
break
when '-'
if src.next_char == '-'
if src.cur_chars_are '---'
src.ignore_chars(3)
con.push_element md_entity('mdash')
else
src.ignore_chars(2)
con.push_element md_entity('ndash')
end
else
con.push_char src.shift_char
end
when '.'
if src.cur_chars_are '...'
src.ignore_chars(3)
con.push_element md_entity('hellip')
elsif src.cur_chars_are '. . .'
src.ignore_chars(5)
con.push_element md_entity('hellip')
else
con.push_char src.shift_char
end
when '"'
if dquote_state == :closed
dquote_state = :open
src.ignore_char
con.push_element md_entity('ldquo')
else
dquote_state = :closed
src.ignore_char
con.push_element md_entity('rdquo')
end
when "'"
if src.cur_chars(4) =~ /'\d\ds/
src.ignore_char
con.push_element md_entity('rsquo')
elsif squote_state == :open
squote_state = :closed unless src.next_char =~ /[[:alpha:]]/
src.ignore_char
con.push_element md_entity('rsquo')
else
if prev_char =~ /[[:alpha:]]/
src.ignore_char
con.push_element md_entity('rsquo')
else
src.ignore_char
con.push_element md_entity('lsquo')
squote_state = :open
end
end
else
con.push_char src.shift_char
end
prev_char = c
end
con.push_string_if_present
merge_ial(con.elements, src, con)
if (s = con.elements.first).kind_of? String
if s[0, 1] == ' '
con.elements[0] = s[1..-1]
end
con.elements.shift if s.empty?
end
con.elements.shift if (con.elements.first.kind_of?(String) && con.elements.first.empty?)
if (s = con.elements.last).kind_of? String
s.chop! if s[-1, 1] == ' '
con.elements.pop if s.empty?
end
con.elements
end