// // Copyright (c) 2015, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // ** ** MarkdownDocWriter outputs a fandoc model to ** [Markdown]`http://daringfireball.net/projects/markdown/` ** @Js class MarkdownDocWriter : DocWriter { ////////////////////////////////////////////////////////////////////////// // Constructor ////////////////////////////////////////////////////////////////////////// new make(OutStream out := Env.cur.out) { this.out = out } ////////////////////////////////////////////////////////////////////////// // Config ////////////////////////////////////////////////////////////////////////// ** Callback to perform link resolution and checking for ** every Link element |Link link|? onLink := null ** Callback to perform image link resolution and checking |Image img|? onImage := null ////////////////////////////////////////////////////////////////////////// // DocWriter ////////////////////////////////////////////////////////////////////////// override Void docStart(Doc doc) { } override Void docEnd(Doc doc) { out.flush } override Void elemStart(DocElem elem) { switch (elem.id) { case DocNodeId.para: para := elem as Para if (inListItem) { out.printLine out.print(Str.defVal.padl(indDef)) } if (para.anchorId != null) out.print("[#${para.anchorId}]") case DocNodeId.blockQuote: out.print("> ") case DocNodeId.pre: inPre = true case DocNodeId.heading: h := elem as Heading out.print(Str.defVal.padl(h.level, '#')).writeChar(' ') if (elem.anchorId != null) out.print("<a name=\"${elem.anchorId}\"></a>") case DocNodeId.unorderedList: listIndexes.push(ListIndex()) case DocNodeId.orderedList: // Markdown only supports numbered ordered lists ol := elem as OrderedList listIndexes.push(ListIndex(OrderedListStyle.number)) case DocNodeId.listItem: indent := (listIndexes.size - 1) * indDef out.print(Str.defVal.padl(indent)) out.print(liSymbol) listIndexes.peek.increment case DocNodeId.link: link := elem as Link onLink?.call(link) out.writeChar('[') case DocNodeId.image: img := elem as Image onImage?.call(img) out.print("![${img.alt}") case DocNodeId.emphasis: out.writeChar('*') case DocNodeId.strong: out.print("**") case DocNodeId.code: out.print("`") case DocNodeId.hr: out.print("---\n") } } override Void elemEnd(DocElem elem) { switch (elem.id) { case DocNodeId.para: if (!inListItem) out.printLine // blank line case DocNodeId.pre: inPre = false case DocNodeId.heading: out.printLine // blank line case DocNodeId.orderedList: case DocNodeId.unorderedList: listIndexes.pop // fall-through if (listIndexes.isEmpty) out.printLine // blank line case DocNodeId.link: link := elem as Link out.print("](${link.uri})") case DocNodeId.image: img := elem as Image out.print("](${img.uri})") case DocNodeId.emphasis: out.writeChar('*') case DocNodeId.strong: out.print("**") case DocNodeId.code: out.print("`") } if (elem.isBlock) out.writeChar('\n') } override Void text(DocText text) { if (inPre) { indent := indCode if (inListItem) indent += (listIndexes.size - 1) * indDef pad := Str.defVal.padl(indent) text.str.splitLines.each |line| { out.print(pad).printLine(line) } } else { out.print(text.str) } } ////////////////////////////////////////////////////////////////////////// // MarkdownDocWriter ////////////////////////////////////////////////////////////////////////// ** Get the symbol to use for the current list item. private Str liSymbol() { li := listIndexes.peek if (li.style == null) { numUl := listIndexes.findAll { it.style == null }.size return ulSymbols[(numUl-1) % ulSymbols.size] } return li.toStr } private Bool inListItem() { !listIndexes.isEmpty } ////////////////////////////////////////////////////////////////////////// // Fields ////////////////////////////////////////////////////////////////////////// const static private Str[] ulSymbols := ["* ", "+ ", "- "] const static private Int indDef := 4 const static private Int indCode := 4 private OutStream out private ListIndex[] listIndexes := [,] private Bool inPre ////////////////////////////////////////////////////////////////////////// // Main ////////////////////////////////////////////////////////////////////////// static Void main(Str[] args := Env.cur.args) { doc := FandocParser().parse(args[0], File(args[0].toUri).in) doc.write(MarkdownDocWriter()) } }