current_line = line
current_block = root
while current_line != null do
- current_line.kind(self).process(self)
+ line_kind(current_line.as(not null)).process(self)
end
self.in_list = old_mode
self.current_block = old_root
# Is the current recursion in list mode?
# Used when visiting blocks with `recurse`
private var in_list = false
+
+ # The type of line.
+ # see: `md_line_*`
+ fun line_kind(md: MDLine): Line do
+ var value = md.value
+ var leading = md.leading
+ var trailing = md.trailing
+ if md.is_empty then return new LineEmpty
+ if md.leading > 3 then return new LineCode
+ if value[leading] == '#' then return new LineHeadline
+ if value[leading] == '>' then return new LineBlockquote
+
+ if value.length - leading - trailing > 2 then
+ if value[leading] == '`' and md.count_chars_start('`') >= 3 then
+ return new LineFence
+ end
+ if value[leading] == '~' and md.count_chars_start('~') >= 3 then
+ return new LineFence
+ end
+ end
+
+ if value.length - leading - trailing > 2 and
+ (value[leading] == '*' or value[leading] == '-' or value[leading] == '_') then
+ if md.count_chars(value[leading]) >= 3 then
+ return new LineHR
+ end
+ end
+
+ if value.length - leading >= 2 and value[leading + 1] == ' ' then
+ var c = value[leading]
+ if c == '*' or c == '-' or c == '+' then return new LineUList
+ end
+
+ if value.length - leading >= 3 and value[leading].is_digit then
+ var i = leading + 1
+ while i < value.length and value[i].is_digit do i += 1
+ if i + 1 < value.length and value[i] == '.' and value[i + 1] == ' ' then
+ return new LineOList
+ end
+ end
+
+ if value[leading] == '<' and md.check_html then return new LineXML
+
+ var next = md.next
+ if next != null and not next.is_empty then
+ if next.count_chars('=') > 0 then
+ return new LineHeadline1
+ end
+ if next.count_chars('-') > 0 then
+ return new LineHeadline2
+ end
+ end
+ return new LineOther
+ end
+
end
# Emit output corresponding to blocks content.
# Is the link an abreviation?
var is_abbrev = false
+ # Create a link with a title.
init with_title(link: String, title: nullable String) do
self.link = link
self.title = title
var line = first_line
while line != null do
if not line.is_empty then
- var kind = line.kind(v)
+ var kind = v.line_kind(line)
if kind isa LineList then
line.value = kind.extract_value(line)
else
class BlockCode
super Block
+ # Number of char to skip at the beginning of the line.
+ #
+ # Block code lines start at 4 spaces.
+ protected var line_start = 4
+
redef fun emit(v) do v.decorator.add_code(v, self)
redef fun emit_lines(v) do
var line = block.first_line
while line != null do
if not line.is_empty then
- v.decorator.append_code(v, line.value, 4, line.value.length)
+ v.decorator.append_code(v, line.value, line_start, line.value.length)
end
v.addn
line = line.next
# this class is only used for typing purposes.
class BlockFence
super BlockCode
+
+ # Fence code lines start at 0 spaces.
+ redef var line_start = 0
end
# A markdown headline.
var line = block.first_line
line = line.next
while line != null do
- var t = line.kind(v)
+ var t = v.line_kind(line)
if t isa LineList or
(not line.is_empty and (line.prev_empty and line.leading == 0 and
not (t isa LineList))) then
# Is the next line empty?
var next_empty: Bool = false is writable
+ # Initialize a new MDLine from its string value
init(value: String) do
self.value = value
self.leading = process_leading
if next != null then next.prev_empty = true
end
- # The type of line.
- # see `md_line_*`
- fun kind(v: MarkdownProcessor): Line do
- var value = self.value
- if is_empty then return new LineEmpty
- if leading > 3 then return new LineCode
- if value[leading] == '#' then return new LineHeadline
- if value[leading] == '>' then return new LineBlockquote
-
- if value.length - leading - trailing > 2 then
- if value[leading] == '`' and count_chars_start('`') >= 3 then
- return new LineFence
- end
- if value[leading] == '~' and count_chars_start('~') >= 3 then
- return new LineFence
- end
- end
-
- if value.length - leading - trailing > 2 and
- (value[leading] == '*' or value[leading] == '-' or value[leading] == '_') then
- if count_chars(value[leading]) >= 3 then
- return new LineHR
- end
- end
-
- if value.length - leading >= 2 and value[leading + 1] == ' ' then
- var c = value[leading]
- if c == '*' or c == '-' or c == '+' then return new LineUList
- end
-
- if value.length - leading >= 3 and value[leading].is_digit then
- var i = leading + 1
- while i < value.length and value[i].is_digit do i += 1
- if i + 1 < value.length and value[i] == '.' and value[i + 1] == ' ' then
- return new LineOList
- end
- end
-
- if value[leading] == '<' and check_html then return new LineXML
-
- if next != null and not next.is_empty then
- if next.count_chars('=') > 0 then
- return new LineHeadline1
- end
- if next.count_chars('-') > 0 then
- return new LineHeadline2
- end
- end
- return new LineOther
- end
-
# Number or leading spaces on this line.
var leading: Int = 0 is writable
# go to block end
var was_empty = line.prev_empty
while line != null and not line.is_empty do
- var t = line.kind(v)
+ var t = v.line_kind(line)
if v.in_list and t isa LineList then
break
end
line = line.next
end
# build block
- var bk: Block
if line != null and not line.is_empty then
var block = v.current_block.split(line.prev.as(not null))
if v.in_list and not was_empty then
redef fun process(v) do
var line = v.current_line
# lookup block end
- while line != null and (line.is_empty or line.kind(v) isa LineCode) do
+ while line != null and (line.is_empty or v.line_kind(line) isa LineCode) do
line = line.next
end
# split at block end line
while line != null do
if not line.is_empty and (line.prev_empty and
line.leading == 0 and
- not line.kind(v) isa LineBlockquote) then break
+ not v.line_kind(line) isa LineBlockquote) then break
line = line.next
end
# build sub block
# go to fence end
var line = v.current_line.next
while line != null do
- if line.kind(v) isa LineFence then break
+ if v.line_kind(line) isa LineFence then break
line = line.next
end
if line != null then
end
block.kind = new BlockFence(block)
block.first_line.clear
- if block.last_line.kind(v) isa LineFence then
+ var last = block.last_line
+ if last != null and v.line_kind(last) isa LineFence then
block.last_line.clear
end
block.remove_surrounding_empty_lines
var line = v.current_line
# go to list end
while line != null do
- var t = line.kind(v)
+ var t = v.line_kind(line)
if not line.is_empty and (line.prev_empty and line.leading == 0 and
not t isa LineList) then break
line = line.next
# Create a new block kind based on this line.
protected fun block_kind(block: MDBlock): BlockList is abstract
+ # Extract string value from `MDLine`.
protected fun extract_value(line: MDLine): String is abstract
end
var c0: Char
var c1: Char
var c2: Char
- var c3: Char
if pos > 0 then
c0 = self[pos - 1]
else
c2 = ' '
end
- if pos + 3 < length then
- c3 = self[pos + 3]
- else
- c3 = ' '
- end
if c == '*' then
if c1 == '*' then
assert res == exp
end
+ fun test_process_code3 do
+ var test = """
+Here is an example of AppleScript:
+~~~
+tell application "Foo"
+ beep
+end tell
+
+<div class="footer">
+ © 2004 Foo Corporation
+</div>
+~~~
+"""
+ var exp = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application "Foo"
+ beep
+end tell
+
+<div class="footer">
+ &copy; 2004 Foo Corporation
+</div>
+</code></pre>
+"""
+ var res = test.md_to_html.write_to_string
+ assert res == exp
+ end
+
+ fun test_process_code4 do
+ var test = """
+Here is an example of AppleScript:
+```
+tell application "Foo"
+ beep
+end tell
+
+<div class="footer">
+ © 2004 Foo Corporation
+</div>
+```
+"""
+ var exp = """
+<p>Here is an example of AppleScript:</p>
+<pre><code>tell application "Foo"
+ beep
+end tell
+
+<div class="footer">
+ &copy; 2004 Foo Corporation
+</div>
+</code></pre>
+"""
+ var res = test.md_to_html.write_to_string
+ assert res == exp
+ end
+
+
fun test_process_nesting1 do
var test = """
> ## This is a header.
fun test_line_type do
var v = new MarkdownProcessor
subject = new MDLine("")
- assert subject.kind(v) isa LineEmpty
+ assert v.line_kind(subject) isa LineEmpty
subject = new MDLine(" ")
- assert subject.kind(v) isa LineEmpty
+ assert v.line_kind(subject) isa LineEmpty
subject = new MDLine("text ")
- assert subject.kind(v) isa LineOther
+ assert v.line_kind(subject) isa LineOther
subject = new MDLine(" # Title")
- assert subject.kind(v) isa LineHeadline
+ assert v.line_kind(subject) isa LineHeadline
subject = new MDLine(" ### Title")
- assert subject.kind(v) isa LineHeadline
+ assert v.line_kind(subject) isa LineHeadline
subject = new MDLine(" code")
- assert subject.kind(v) isa LineCode
+ assert v.line_kind(subject) isa LineCode
subject = new MDLine(" ~~~")
- assert subject.kind(v) isa LineFence
+ assert v.line_kind(subject) isa LineFence
subject = new MDLine(" ```")
- assert subject.kind(v) isa LineFence
+ assert v.line_kind(subject) isa LineFence
subject = new MDLine(" Title ")
subject.next = new MDLine("== ")
- assert subject.kind(v) isa LineHeadline1
+ assert v.line_kind(subject) isa LineHeadline1
subject = new MDLine(" Title ")
subject.next = new MDLine("-- ")
- assert subject.kind(v) isa LineHeadline2
+ assert v.line_kind(subject) isa LineHeadline2
subject = new MDLine(" * * * ")
- assert subject.kind(v) isa LineHR
+ assert v.line_kind(subject) isa LineHR
subject = new MDLine(" *** ")
- assert subject.kind(v) isa LineHR
+ assert v.line_kind(subject) isa LineHR
subject = new MDLine("- -- ")
- assert subject.kind(v) isa LineHR
+ assert v.line_kind(subject) isa LineHR
subject = new MDLine("--------- ")
- assert subject.kind(v) isa LineHR
+ assert v.line_kind(subject) isa LineHR
subject = new MDLine(" >")
- assert subject.kind(v) isa LineBlockquote
+ assert v.line_kind(subject) isa LineBlockquote
subject = new MDLine("<p></p>")
- assert subject.kind(v) isa LineXML
+ assert v.line_kind(subject) isa LineXML
subject = new MDLine("<p>")
- assert subject.kind(v) isa LineOther
+ assert v.line_kind(subject) isa LineOther
subject = new MDLine(" * foo")
- assert subject.kind(v) isa LineUList
+ assert v.line_kind(subject) isa LineUList
subject = new MDLine("- foo")
- assert subject.kind(v) isa LineUList
+ assert v.line_kind(subject) isa LineUList
subject = new MDLine("+ foo")
- assert subject.kind(v) isa LineUList
+ assert v.line_kind(subject) isa LineUList
subject = new MDLine("1. foo")
- assert subject.kind(v) isa LineOList
+ assert v.line_kind(subject) isa LineOList
subject = new MDLine(" 11111. foo")
- assert subject.kind(v) isa LineOList
+ assert v.line_kind(subject) isa LineOList
end
fun test_count_chars do