diff --git a/lib/compile/jar/commons-lang3-3.1.jar b/lib/compile/jar/commons-lang3-3.1.jar new file mode 100644 index 0000000..a85e539 Binary files /dev/null and b/lib/compile/jar/commons-lang3-3.1.jar differ diff --git a/lib/compile/jar/jdb-util-1.5.2.jar b/lib/compile/jar/jdb-util-1.5.2.jar new file mode 100644 index 0000000..9181cd3 Binary files /dev/null and b/lib/compile/jar/jdb-util-1.5.2.jar differ diff --git a/lib/runtime/jar/commons-lang3-3.1.jar b/lib/runtime/jar/commons-lang3-3.1.jar new file mode 100644 index 0000000..a85e539 Binary files /dev/null and b/lib/runtime/jar/commons-lang3-3.1.jar differ diff --git a/lib/runtime/jar/jdb-util-1.5.2.jar b/lib/runtime/jar/jdb-util-1.5.2.jar new file mode 100644 index 0000000..9181cd3 Binary files /dev/null and b/lib/runtime/jar/jdb-util-1.5.2.jar differ diff --git a/project.properties b/project.properties index 4b0c514..ae09524 100644 --- a/project.properties +++ b/project.properties @@ -1,7 +1,7 @@ -#Thu, 29 Dec 2011 15:43:59 -0600 +#Mon, 02 Jan 2012 20:27:05 -0600 name=jlp -version=1.3a -build.number=0 +version=1.3 +build.number=1 lib.local=true release.dir=release main.class=com.jdblabs.jlp.JLPMain diff --git a/resources/main/docco.css b/resources/main/css/docco.css similarity index 100% rename from resources/main/docco.css rename to resources/main/css/docco.css diff --git a/resources/main/css/jlp.css b/resources/main/css/jlp.css new file mode 100644 index 0000000..07e9f90 --- /dev/null +++ b/resources/main/css/jlp.css @@ -0,0 +1,65 @@ +body { + font-family: 'Palatine Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + color: #252519; + margin: 0; + padding 0; } + +a { color: #261a3b; } +a:visited { color: #261a3b; } + +p{ margin: 0 0 15px 0; } + +h1, h2, h3, h4, h5, h6 { margin: 0 0 15px 0; } + +h1 { margin-top: 40px; } + +dt { font-weight: bold; } + +ul { + margin: 0; + padding-top: 0; } + +#container { position: relative; } + +table td { + border: 0; + outline: 0; } + +td.docs, th.docs { + max-width: 600px; + min-width: 450px; + min-height: 5pc; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; } + +.docs pre { + background: #f8f8ff; + border: 1px solid #dedede; + margin: 15px 0 15px; + padding-left: 15px; } + +.docs tt, .docs code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; } + +.docs table { + border: thin solid #dedede; + margin-left: 60px; } + +td.code, th.code { + padding: 14px 15px 16px 25px; + width: 100%; + vertical-align: top; + border-left: 1px solid #e5e5ee; } + +pre, tt, code { + font-size: 12px; + line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; } diff --git a/resources/main/jlp.css b/resources/main/jlp.css deleted file mode 100644 index 3159b27..0000000 --- a/resources/main/jlp.css +++ /dev/null @@ -1,132 +0,0 @@ -body { - font-family: 'Palatine Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; - font-size: 15px; - line-height: 22px; - color: #252519; - margin: 0; - padding 0; } - -a { color: #261a3b; } -a:visited { color: #261a3b; } - -p{ margin: 0 0 15px 0; } - -h1, h2, h3, h4, h5, h6 { margin: 0 0 15px 0; } - -h1 { margin-top: 40px; } - -dt { font-weight: bold; } - -ul { - margin: 0; - padding-top: 0; } - -#container { position: relative; } - -table td { - border: 0; - outline: 0; } - -td.docs, th.docs { - max-width: 600px; - min-width: 450px; - min-height: 5pc; - padding: 10px 25px 1px 50px; - overflow-x: hidden; - vertical-align: top; - text-align: left; } - -.docs pre { - background: #f8f8ff; - border: 1px solid #dedede; - margin: 15px 0 15px; - padding-left: 15px; } - -.docs tt, .docs code { - background: #f8f8ff; - border: 1px solid #dedede; - font-size: 12px; - padding: 0 0.2em; } - -.docs table { - border: thin solid #dedede; - margin-left: 60px; } - -td.code, th.code { - padding: 14px 15px 16px 25px; - width: 100%; - vertical-align: top; - background: #f5f5ff; - border-left: 1px solid #e5e5ee; } - -pre, tt, code { - font-size: 12px; - line-height: 18px; - font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; - margin: 0; padding: 0; } - - -/*---------------------- Syntax Highlighting -----------------------------*/ -td.linenos { background-color: #f0f0f0; padding-right: 10px; } -span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } -body .hll { background-color: #ffffcc } -body .c { color: #408080; font-style: italic } /* Comment */ -body .err { border: 1px solid #FF0000 } /* Error */ -body .k { color: #954121 } /* Keyword */ -body .o { color: #666666 } /* Operator */ -body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ -body .cp { color: #BC7A00 } /* Comment.Preproc */ -body .c1 { color: #408080; font-style: italic } /* Comment.Single */ -body .cs { color: #408080; font-style: italic } /* Comment.Special */ -body .gd { color: #A00000 } /* Generic.Deleted */ -body .ge { font-style: italic } /* Generic.Emph */ -body .gr { color: #FF0000 } /* Generic.Error */ -body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -body .gi { color: #00A000 } /* Generic.Inserted */ -body .go { color: #808080 } /* Generic.Output */ -body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -body .gs { font-weight: bold } /* Generic.Strong */ -body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -body .gt { color: #0040D0 } /* Generic.Traceback */ -body .kc { color: #954121 } /* Keyword.Constant */ -body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ -body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ -body .kp { color: #954121 } /* Keyword.Pseudo */ -body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ -body .kt { color: #B00040 } /* Keyword.Type */ -body .m { color: #666666 } /* Literal.Number */ -body .s { color: #219161 } /* Literal.String */ -body .na { color: #7D9029 } /* Name.Attribute */ -body .nb { color: #954121 } /* Name.Builtin */ -body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -body .no { color: #880000 } /* Name.Constant */ -body .nd { color: #AA22FF } /* Name.Decorator */ -body .ni { color: #999999; font-weight: bold } /* Name.Entity */ -body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -body .nf { color: #0000FF } /* Name.Function */ -body .nl { color: #A0A000 } /* Name.Label */ -body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -body .nt { color: #954121; font-weight: bold } /* Name.Tag */ -body .nv { color: #19469D } /* Name.Variable */ -body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -body .w { color: #bbbbbb } /* Text.Whitespace */ -body .mf { color: #666666 } /* Literal.Number.Float */ -body .mh { color: #666666 } /* Literal.Number.Hex */ -body .mi { color: #666666 } /* Literal.Number.Integer */ -body .mo { color: #666666 } /* Literal.Number.Oct */ -body .sb { color: #219161 } /* Literal.String.Backtick */ -body .sc { color: #219161 } /* Literal.String.Char */ -body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ -body .s2 { color: #219161 } /* Literal.String.Double */ -body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -body .sh { color: #219161 } /* Literal.String.Heredoc */ -body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -body .sx { color: #954121 } /* Literal.String.Other */ -body .sr { color: #BB6688 } /* Literal.String.Regex */ -body .s1 { color: #219161 } /* Literal.String.Single */ -body .ss { color: #19469D } /* Literal.String.Symbol */ -body .bp { color: #954121 } /* Name.Builtin.Pseudo */ -body .vc { color: #19469D } /* Name.Variable.Class */ -body .vg { color: #19469D } /* Name.Variable.Global */ -body .vi { color: #19469D } /* Name.Variable.Instance */ -body .il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/resources/main/syntax-highlighter.jar b/resources/main/syntax-highlighter.jar new file mode 100644 index 0000000..915ffd6 Binary files /dev/null and b/resources/main/syntax-highlighter.jar differ diff --git a/src/main/com/jdblabs/jlp/JLPMain.groovy b/src/main/com/jdblabs/jlp/JLPMain.groovy index 74ddeab..a257c69 100644 --- a/src/main/com/jdblabs/jlp/JLPMain.groovy +++ b/src/main/com/jdblabs/jlp/JLPMain.groovy @@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory */ public class JLPMain { - public static final String VERSION = "1.2" + public static final String VERSION = "1.3" private static Logger log = LoggerFactory.getLogger(JLPMain.class) @@ -79,7 +79,7 @@ public class JLPMain { /// Get the CSS theme to use. We will start by assuming the default will /// be used. - def css = JLPMain.class.getResourceAsStream("/jlp.css") + def css = JLPMain.class.getResourceAsStream("/css/jlp.css") /// If the CSS file was specified on the command-line, let's look for it. if (opts.'css-file') { @@ -102,10 +102,6 @@ public class JLPMain { "${cssFile.canonicalPath}'." println " Using the default CSS." }} - /// Extract the text from our css source (either an InputStream or a - /// File) - css = css.text - /// #### Create the input file list. /// We will start with the filenames passed as arguments on the command diff --git a/src/main/com/jdblabs/jlp/JLPPegParser.java b/src/main/com/jdblabs/jlp/JLPPegParser.java index 4e6bc73..86993f3 100644 --- a/src/main/com/jdblabs/jlp/JLPPegParser.java +++ b/src/main/com/jdblabs/jlp/JLPPegParser.java @@ -277,7 +277,8 @@ public class JLPPegParser extends BaseParser implements JLPParser { swap(), push(popAsString() + ((DocText) pop()).value))), - push(new Directive(popAsString(), popAsString(), popAsInt()))); } + push(new Directive(popAsString(), popAsString(), popAsInt(), + (DocBlock)peek()))); } /** * #### MLongDirective @@ -300,7 +301,8 @@ public class JLPPegParser extends BaseParser implements JLPParser { swap(), push(popAsString() + ((DocText) pop()).value))), - push(new Directive(popAsString(), popAsString(), popAsInt()))); } + push(new Directive(popAsString(), popAsString(), popAsInt(), + (DocBlock) peek()))); } /** * #### SShortDirective @@ -318,7 +320,8 @@ public class JLPPegParser extends BaseParser implements JLPParser { FirstOf(AUTHOR_DIR, ORG_DIR, INCLUDE_DIR, COPYRIGHT_DIR), push(match()), RemainingSDocLine(), - push(new Directive(match().trim(), popAsString(), popAsInt()))); } + push(new Directive(match().trim(), popAsString(), popAsInt(), + (DocBlock) peek()))); } /** * #### MShortDirective @@ -336,7 +339,8 @@ public class JLPPegParser extends BaseParser implements JLPParser { FirstOf(AUTHOR_DIR, ORG_DIR, INCLUDE_DIR, COPYRIGHT_DIR), push(match()), RemainingMDocLine(), - push(new Directive(match().trim(), popAsString(), popAsInt()))); } + push(new Directive(match().trim(), popAsString(), popAsInt(), + (DocBlock) peek()))); } /** * #### SDocText @@ -492,8 +496,7 @@ public class JLPPegParser extends BaseParser implements JLPParser { boolean addToDocBlock(ASTNode an) { DocBlock docBlock = (DocBlock) pop(); if (an instanceof Directive) { - docBlock.directives.add((Directive) an); - ((Directive) an).parentBlock = docBlock; } + docBlock.directives.add((Directive) an); } else if (an instanceof DocText) { docBlock.docTexts.add((DocText) an); } else { throw new IllegalStateException(); } diff --git a/src/main/com/jdblabs/jlp/LinkAnchor.groovy b/src/main/com/jdblabs/jlp/LinkAnchor.groovy index 54d163a..1fe59b0 100644 --- a/src/main/com/jdblabs/jlp/LinkAnchor.groovy +++ b/src/main/com/jdblabs/jlp/LinkAnchor.groovy @@ -4,7 +4,7 @@ */ package com.jdblabs.jlp -import com.jdblabs.jlp.ast.Directive +import com.jdblabs.jlp.ast.ASTNode /** * A *LinkAnchor* in the documentation is very similar to an HTML anchor. It @@ -20,10 +20,13 @@ import com.jdblabs.jlp.ast.Directive */ public class LinkAnchor { + public enum LinkType { OrgLink, FileLink } + /// The anchor id. This comes from the text after the directive. public String id - public Directive directive + public LinkType type + public ASTNode source public String sourceDocId } diff --git a/src/main/com/jdblabs/jlp/LiterateMarkdownGenerator.groovy b/src/main/com/jdblabs/jlp/LiterateMarkdownGenerator.groovy index 6eac0a4..af0f994 100644 --- a/src/main/com/jdblabs/jlp/LiterateMarkdownGenerator.groovy +++ b/src/main/com/jdblabs/jlp/LiterateMarkdownGenerator.groovy @@ -10,6 +10,8 @@ import com.jdblabs.jlp.ast.Directive.DirectiveType import org.pegdown.Extensions import org.pegdown.PegDownProcessor +import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4 as escape + import java.util.List /** @@ -41,7 +43,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { case DirectiveType.Org: LinkAnchor anchor = new LinkAnchor( id: directive.value, - directive: directive, + source: directive, sourceDocId: processor.currentDocId) processor.linkAnchors[anchor.id] = anchor @@ -71,15 +73,41 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { """ - ${processor.currentDocId} + ${escape(processor.currentDocId)} - + + + + + + """) + + /// If there is a language-specific brush, include it + def shBrush = processor.shBrushForSourceType( + processor.currentDoc.sourceType) + + if (shBrush) { sb.append(""" + + """) } + + /// Finish our header and begin the body. + sb.append(""" +
- + """) @@ -108,6 +136,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { /// Create the `tr` that will hold the `Block`. If we found an `@org` /// directive we will add the id here. + // TODO: should this be escaped? if (orgDir) { sb.append("\n") } else { sb.append("") } @@ -183,8 +212,9 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { codeLines = codeLines.collect { arr -> arr[1] } - /// Write out the lines in a
 block
-        return "
${codeLines.join('')}
" } + /// Write out the lines in a `
` block
+        return "
" +
+            "${escape(codeLines.join(''))}
" } /** @api Emit a *DocText*. */ protected String emit(DocText docText) { return docText.value } @@ -229,36 +259,13 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { /// Replace internal `jlp://` links with actual links based on`@org` /// references. - html = html.replaceAll(/href=['"]jlp:\/\/([^\s"]+)['"]/) { wholeMatch, linkId -> - - /// Get the org data we found in the parse phase for this org id. - def link = processor.linkAnchors[linkId] - String newLink - - if (!link) { - // We do not have any reference to this id. - /* TODO: log error */ - newLink = "href=\"broken_link(${linkId})\"" } - - /// This link points to a location in this document. - else if (processor.currentDocId == link.sourceDocId) { - newLink = "href=\"#${linkId}\"" } - - /// The link should point to a different document. - else { - TargetDoc thisDoc = processor.currentDoc - TargetDoc linkDoc = processor.docs[link.sourceDocId] - - String pathToLinkedDoc = processor.getRelativeFilepath( - thisDoc.sourceFile.parentFile, - linkDoc.sourceFile) - - /** The target document may not be in the same directory - * as us, backtrack to the (relative) top of our directory - * structure. */ - newLink = 'href="' + pathToLinkedDoc + ".html#${linkId}\"" } - - return newLink } + html = html.replaceAll(/href=['"](jlp:\/\/[^\s"']+)['"]/) { match, link-> + return 'href="' + resolveLink(link) + '"' } return html; } + + /// Shortcut for `processor.resolveLink(url, processor.currentDoc)`. + protected String resolveLink(String url) { + processor.resolveLink(url, processor.currentDoc) } + } diff --git a/src/main/com/jdblabs/jlp/Processor.groovy b/src/main/com/jdblabs/jlp/Processor.groovy index 43bd6e5..11d689b 100644 --- a/src/main/com/jdblabs/jlp/Processor.groovy +++ b/src/main/com/jdblabs/jlp/Processor.groovy @@ -4,6 +4,8 @@ */ package com.jdblabs.jlp +import com.jdbernard.util.JarUtils +import java.util.jar.JarInputStream import org.parboiled.BaseParser import org.parboiled.Parboiled import org.slf4j.Logger @@ -35,9 +37,9 @@ public class Processor { /// The root of the output path. public File outputRoot - /// The CSS that will be used for the resulting HTML documents. Note that - /// this is the CSS file contents, not the name of a CSS file. - public String css + /// The CSS that will be used for the resulting HTML documents. This object + /// can be any object that responds to the `text` property. + public def css /// A shortcut for `docs[currentDocId]` public TargetDoc currentDoc @@ -62,7 +64,7 @@ public class Processor { * @api Process the input files given, writing the resulting documentation * to the directory named in `outputDir`, using the CSS given in `css` */ - public static void process(File outputDir, String css, + public static void process(File outputDir, def css, List inputFiles) { /// Find the closest common parent folder to all of the files given. @@ -91,18 +93,34 @@ public class Processor { /// Remember that the data for the processing run was initialized by the /// constructor. + /// * Write the CSS file to our output directory. + File cssFile = new File(outputRoot, "css/jlp.css") + cssFile.parentFile.mkdirs() + cssFile.text = css.text + + /// * Extract the syntax highlighter files to the output directory. + File shFile = new File(outputRoot, "temp.jar") + getClass().getResourceAsStream("/syntax-highlighter.jar").withStream { is -> + shFile.withOutputStream { os -> + while (is.available()) { os.write(is.read()) }}} + JarUtils.extract(shFile, outputRoot) + shFile.delete() + /// * Create the processing context for each input file. We are using /// the relative path of the file as the document id. inputFiles.each { file -> def docId = getRelativeFilepath(inputRoot, file) - docs[docId] = new TargetDoc(sourceFile: file) } + docs[docId] = new TargetDoc( + sourceDocId: docId, + sourceFile: file, + sourceType: sourceTypeForFile(file)) } /// * Run the parse phase on each of the files. For each file, we load /// the parser for that file type and parse the file into an abstract /// syntax tree (AST). processDocs { log.trace("Parsing '{}'.", currentDocId) - def parser = getParser(sourceTypeForFile(currentDoc.sourceFile)) + def parser = getParser(currentDoc.sourceType) // TODO: error detection currentDoc.sourceAST = parser.parse(currentDoc.sourceFile.text) } @@ -111,7 +129,7 @@ public class Processor { /// for an explanation of the generator phases). processDocs { log.trace("Second-pass parsing for '{}'.", currentDocId) - def generator = getGenerator(sourceTypeForFile(currentDoc.sourceFile)) + def generator = getGenerator(currentDoc.sourceType) // TODO: error detection generator.parse(currentDoc.sourceAST) } @@ -119,7 +137,7 @@ public class Processor { /// * Second pass by the generators, the emit phase. processDocs { log.trace("Emitting documentation for '{}'.", currentDocId) - def generator = getGenerator(sourceTypeForFile(currentDoc.sourceFile)) + def generator = getGenerator(currentDoc.sourceType) currentDoc.output = generator.emit(currentDoc.sourceAST) } /// * Write the output to the output directory. @@ -138,10 +156,6 @@ public class Processor { /// Create the directory for this file if it does not exist. if (!outputDir.exists()) { outputDir.mkdirs() } - /// Write the CSS file if it does not exist. - File cssFile = new File(outputDir, "jlp.css") - if (!cssFile.exists()) { cssFile.withWriter{ it.println css } } - /// Copy the source file over. // TODO: make this behavior customizable. (new File(outputRoot, relativePath)).withWriter { @@ -164,6 +178,67 @@ public class Processor { return c() } } + /*** + * #### resolveLink + * Given a link, resolve it against the current output root. + * + * If this is a full URL, then we will attempt to resolve it based on the + * URL protocol. If this is not a full URL then it will resolve the link + * against the output root. + * + * `jlp://` + * : Resolve the link by looking for a matching link anchor defined + * in the documentation. + * + * *other protocol* + * : Return the link as-is. + * + * *absolute path (starts with `/`)* + * : Returns the link resolved directly against the output root. + * + * *relative path (no leading `/`)* + * : Returns the link resolved against the `TargetDoc` passed in. + */ + public String resolveLink(String link, TargetDoc targetDoc) { + switch (link) { + + /// JLP link, let's resolve with a link anchor + case ~/^jlp:.*/: + /// Get the org data we found in the parse phase for this org id. + def m = (link =~ /jlp:\/\/(.+)/) + def linkId = m[0][1] + def linkAnchor = linkAnchors[m[0][1]] + + if (!linkAnchor) { + // We do not have any reference to this id. + /* TODO: log error */ + return "broken_link(${linkId})" } + + /// This link points to a location in this document. + else if (targetDoc.sourceDocId == linkAnchor.sourceDocId) { + return "#${linkId}" } + + /// The link should point to a different document. + else { + TargetDoc linkDoc = docs[linkAnchor.sourceDocId] + + String pathToLinkedDoc = getRelativeFilepath( + targetDoc.sourceFile.parentFile, linkDoc.sourceFile) + + return "${pathToLinkedDoc}.html#${linkId}" } + + /// Other protocol: return as-is. + case ~/^\w+:.*/: return link + + /// Absolute link, resolve relative to the output root. + case ~/^\/.*/: return outputRoot.canonicalPath + link + + /// Relative link, resolve using the output root and the source + /// document relative to the input root. + default: + def relPath = getRelativePath(inputRoot, targetDoc.sourceFile) + return "${relPath}/${link}" }} + /** * #### getRelativeFilepath * Assuming our current directory is `root`, get the relative path to @@ -235,14 +310,31 @@ public class Processor { /// Lookup the file type by extension switch (extension) { case 'c': case 'h': return 'c'; - case 'c++': case 'h++': case 'cpp': case 'hpp': return 'c++'; + case 'c++': case 'h++': case 'cpp': case 'hpp': return 'cpp'; case 'erl': case 'hrl': return 'erlang'; case 'groovy': return 'groovy'; case 'java': return 'java'; case 'js': return 'javascript'; case 'md': return 'markdown'; + case 'html': return 'html'; + case 'xml': case 'xhtml': return 'xml'; default: return 'unknown'; }} + /** + * #### shBrushForSourceType + * Lookup the syntax highlighter brush for the given source type. + * @org jlp.jdb-labs.com/Processor/shBrushForSourceType + */ + public static String shBrushForSourceType(String sourceType) { + switch (sourceType) { + case 'c': case 'cpp': return 'shBrushCpp' + case 'erlang': return 'shBrushErlang' + case 'groovy': return 'shBrushGroovy' + case 'java': return 'shBrushJava' + case 'javascript': return 'shBrushJScript' + case 'html': case 'xml': return 'shBrushXml' + default: return null }} + /** * #### getGenerator * Get a generator for the given source file type. @@ -278,8 +370,13 @@ public class Processor { case 'markdown': parsers[sourceType] = new MarkdownParser() break + case 'html': case 'xml': + parsers[sourceType] = Parboiled.createParser( + JLPPegParser, '', + '#$%^&*()_-+=|;:\'",<>?~`', '<

${processor.currentDocId}

${escape(processor.currentDocId)}