class MarkdownProcessor
# `MarkdownEmitter` used for ouput.
- var emitter: MarkdownEmitter is noinit
+ var emitter: MarkdownEmitter is noinit, protected writable
# Work in extended mode (default).
#
init do self.emitter = new MarkdownEmitter(self)
# Process the mardown `input` string and return the processed output.
- fun process(input: String): Streamable do
+ fun process(input: String): Writable do
# init processor
link_refs.clear
last_link_ref = null
#
# Markdown allows link refs to be defined over two lines:
#
- # [id]: http://example.com/longish/path/to/resource/here
+ # [id]: http://example.com/longish/path/to/resource/here
# "Optional Title Here"
#
private var last_link_ref: nullable LinkRef = null
# The emitter use a `Decorator` to select the output format.
class MarkdownEmitter
+ # Kind of processor used for parsing.
+ type PROCESSOR: MarkdownProcessor
+
# Processor containing link refs.
- var processor: MarkdownProcessor
+ var processor: PROCESSOR
+
+ # Kind of decorator used for decoration.
+ type DECORATOR: Decorator
# Decorator used for output.
# Default is `HTMLDecorator`
- var decorator: Decorator = new HTMLDecorator is writable
+ var decorator: DECORATOR is writable, lazy do
+ return new HTMLDecorator
+ end
# Create a new `MarkdownEmitter` using a custom `decorator`.
- init with_decorator(processor: MarkdownProcessor, decorator: Decorator) do
+ init with_decorator(processor: PROCESSOR, decorator: DECORATOR) do
init processor
self.decorator = decorator
end
fun emit_in(block: Block) do block.emit_in(self)
# Transform and emit mardown text
- fun emit_text(text: Text) do
- emit_text_until(text, 0, null)
- end
+ fun emit_text(text: Text) do emit_text_until(text, 0, null)
# Transform and emit mardown text starting at `from` and
# until a token with the same type as `token` is found.
end
# Append `e` to current buffer.
- fun add(e: Streamable) do
+ fun add(e: Writable) do
if e isa Text then
current_buffer.append e
else
end
# Append `c` to current buffer.
- fun addc(c: Char) do current_buffer.add c
+ fun addc(c: Char) do add c.to_s
# Append a "\n" line break.
- fun addn do current_buffer.add '\n'
+ fun addn do add "\n"
end
# A Link Reference.
# Default decorator used is `HTMLDecorator`.
interface Decorator
+ # Kind of emitter used for decoration.
+ type EMITTER: MarkdownEmitter
+
# Render a ruler block.
- fun add_ruler(v: MarkdownEmitter, block: BlockRuler) is abstract
+ fun add_ruler(v: EMITTER, block: BlockRuler) is abstract
# Render a headline block with corresponding level.
- fun add_headline(v: MarkdownEmitter, block: BlockHeadline) is abstract
+ fun add_headline(v: EMITTER, block: BlockHeadline) is abstract
# Render a paragraph block.
- fun add_paragraph(v: MarkdownEmitter, block: BlockParagraph) is abstract
+ fun add_paragraph(v: EMITTER, block: BlockParagraph) is abstract
# Render a code or fence block.
- fun add_code(v: MarkdownEmitter, block: BlockCode) is abstract
+ fun add_code(v: EMITTER, block: BlockCode) is abstract
# Render a blockquote.
- fun add_blockquote(v: MarkdownEmitter, block: BlockQuote) is abstract
+ fun add_blockquote(v: EMITTER, block: BlockQuote) is abstract
# Render an unordered list.
- fun add_unorderedlist(v: MarkdownEmitter, block: BlockUnorderedList) is abstract
+ fun add_unorderedlist(v: EMITTER, block: BlockUnorderedList) is abstract
# Render an ordered list.
- fun add_orderedlist(v: MarkdownEmitter, block: BlockOrderedList) is abstract
+ fun add_orderedlist(v: EMITTER, block: BlockOrderedList) is abstract
# Render a list item.
- fun add_listitem(v: MarkdownEmitter, block: BlockListItem) is abstract
+ fun add_listitem(v: EMITTER, block: BlockListItem) is abstract
# Render an emphasis text.
- fun add_em(v: MarkdownEmitter, text: Text) is abstract
+ fun add_em(v: EMITTER, text: Text) is abstract
# Render a strong text.
- fun add_strong(v: MarkdownEmitter, text: Text) is abstract
+ fun add_strong(v: EMITTER, text: Text) is abstract
# Render a strike text.
#
# Extended mode only (see `MarkdownProcessor::ext_mode`)
- fun add_strike(v: MarkdownEmitter, text: Text) is abstract
+ fun add_strike(v: EMITTER, text: Text) is abstract
# Render a link.
- fun add_link(v: MarkdownEmitter, link: Text, name: Text, comment: nullable Text) is abstract
+ fun add_link(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
# Render an image.
- fun add_image(v: MarkdownEmitter, link: Text, name: Text, comment: nullable Text) is abstract
+ fun add_image(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
# Render an abbreviation.
- fun add_abbr(v: MarkdownEmitter, name: Text, comment: Text) is abstract
+ fun add_abbr(v: EMITTER, name: Text, comment: Text) is abstract
# Render a code span reading from a buffer.
- fun add_span_code(v: MarkdownEmitter, buffer: Text, from, to: Int) is abstract
+ fun add_span_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
# Render a text and escape it.
- fun append_value(v: MarkdownEmitter, value: Text) is abstract
+ fun append_value(v: EMITTER, value: Text) is abstract
# Render code text from buffer and escape it.
- fun append_code(v: MarkdownEmitter, buffer: Text, from, to: Int) is abstract
+ fun append_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
# Render a character escape.
- fun escape_char(v: MarkdownEmitter, char: Char) is abstract
+ fun escape_char(v: EMITTER, char: Char) is abstract
# Render a line break
- fun add_line_break(v: MarkdownEmitter) is abstract
+ fun add_line_break(v: EMITTER) is abstract
# Generate a new html valid id from a `String`.
fun strip_id(txt: String): String is abstract
comment = lr.title
end
else
- var tid = name.write_to_string.replace("\n", " ").to_lower
+ var tid = name.write_to_string.replace("\n", " ").to_lower
if v.processor.link_refs.has_key(tid) then
var lr = v.processor.link_refs[tid]
link = lr.link
# Safe mode can be activated to limit reading to valid xml.
private fun read_xml(out: FlatBuffer, start: Int, safe_mode: Bool): Int do
var pos = 0
+ var is_valid = true
var is_close_tag = false
if start + 1 >= length then return -1
if self[start + 1] == '/' then
pos = read_xml_until(tmp, pos, ' ', '/', '>')
if pos == -1 then return -1
var tag = tmp.write_to_string.trim.to_lower
- if tag.is_html_unsafe then
+ if not tag.is_valid_html_tag then
+ out.append "<"
+ pos = -1
+ else if tag.is_html_unsafe then
+ is_valid = false
out.append "<"
if is_close_tag then out.add '/'
out.append tmp
if pos == -1 then return -1
end
if self[pos] == '>' then
- out.add '>'
+ if is_valid then
+ out.add '>'
+ else
+ out.append ">"
+ end
return pos
end
return -1
return tpl.write_to_string.to_lower
end
+ private fun is_valid_html_tag: Bool do
+ if is_empty then return false
+ for c in self do
+ if not c.is_alpha then return false
+ end
+ return true
+ end
+
# Read and escape the markdown contained in `self`.
private fun escape(out: FlatBuffer, c: Char, pos: Int): Int do
if c == '\\' or c == '[' or c == ']' or c == '(' or c == ')' or c == '{' or
private fun meta_from_fence: nullable Text do
for i in [0..chars.length[ do
var c = chars[i]
- print c
if c != ' ' and c != '`' and c != '~' then
return substring_from(i).trim
end
# var md = "**Hello World!**"
# var html = md.md_to_html
# assert html == "<p><strong>Hello World!</strong></p>\n"
- fun md_to_html: Streamable do
+ fun md_to_html: Writable do
var processor = new MarkdownProcessor
return processor.process(self)
end