Worked on literate output using Markdown as the text markup language.

* Added CSS based on Docco (blatantly copied).
* Updated sample text to better fit the emerging usage patterns. Some of the
  things I did to make it render nicely for the Literate output may cause
  problems when we go to render API output. I will cross that bridge when I come
  to it.
* Added parsing infrastructure to the Generator behaviour to allow a
  pre-processing pass over the document. Current the LiterateMarkdownGenerator
  is using this to compile a map of `@org` references.
* Tweaked the HTML output slightly.
* Added a layer over the PegDownProcessor in LiterateMarkdownGenerator to
  capture and transform `jlp://` links into relative links.
This commit is contained in:
Jonathan Bernard
2011-09-06 16:13:21 -05:00
parent c2c2f9da3d
commit 7a5870dc09
8 changed files with 520 additions and 90 deletions

View File

@ -15,18 +15,43 @@ public abstract class JLPBaseGenerator {
protected Map<String, String> generate(Map<String, SourceFile> sources) {
Map result = [:]
// run the parse phase
sources.each { sourceId, sourceAST ->
// set up the current generator state for this source
docState.currentDocId = sourceId
docState.codeTrees[sourceId] = sourceAST.codeAST
parse(sourceAST) }
// run the emit phase
sources.each { sourceId, sourceAST ->
// set up the current generator state for this source
docState.currentDocId = sourceId
// generate the doc for this source
result[sourceId] = emit(sourceAST) }
// return our results
return result }
protected void parse(SourceFile sourceFile) {
sourceFile.blocks.each { block -> parse(block) } }
protected void parse(Block block) {
parse(block.docBlock)
parse(block.codeBlock) }
protected void parse(DocBlock docBlock) {
docBlock.directives.each { directive -> parse(directive) }
docBlock.docTexts.each { docText -> parse(docText) } }
protected abstract void parse(Directive directive)
protected abstract void parse(CodeBlock codeBlock)
protected abstract void parse(DocText docText)
protected abstract String emit(SourceFile sourceFile)
protected abstract String emit(Block block)
protected abstract String emit(DocBlock docBlock)

View File

@ -16,13 +16,29 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
super()
pegdown = new PegDownProcessor(
Extensions.TABLES & Extensions.DEFINITIONS) }
Extensions.TABLES | Extensions.DEFINITIONS) }
protected static Map<String, String> generateDocuments(
Map<String, SourceFile> sources) {
LiterateMarkdownGenerator inst = new LiterateMarkdownGenerator()
return inst.generate(sources) }
protected void parse(Directive directive) {
switch(directive.type) {
case DirectiveType.Org:
def orgMap = [:]
orgMap.id = directive.value
orgMap.directive = directive
orgMap.sourceDocId = docState.currentDocId
docState.orgs[directive.value] = orgMap
break;
default:
break // do nothing
} }
protected void parse(CodeBlock codeBlock) {} // nothing to do
protected void parse(DocText docText) {} // nothing to do
protected String emit(SourceFile sourceFile) {
StringBuilder sb = new StringBuilder()
@ -34,13 +50,13 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
<head>
<title>${docState.currentDocId}</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- <link rel="syltesheet" media="all" href=""/> -->
<link rel="stylesheet" media="all" href="docco.css"/>
</head>
<body>
<div id="container">
<table cellpadding="0" cellspacing="0">
<thead><tr>
<th class="doc"><h1>${docState.currentDocId}</h1></th>
<th class="docs"><h1>${docState.currentDocId}</h1></th>
<th class="code"/>
</tr></thead>
<tbody>""")
@ -70,7 +86,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
else { sb.append("<tr>") }
// Create the `td` for the documentation.
sb.append('\n<td class="doc">')
sb.append('\n<td class="docs">')
sb.append(emit(block.docBlock))
sb.append('</td>')
@ -94,14 +110,11 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
emitQueue = docBlock.directives.collect { directive ->
def queueItem = [lineNumber: directive.lineNumber, value: directive]
switch(directive.type) {
case DirectiveType.Api: queueItem.priority = 12; break
case DirectiveType.Api: queueItem.priority = 50; break
case DirectiveType.Author: queueItem.priority = 10; break
case DirectiveType.Copyright: queueItem.priority = 11; break
case DirectiveType.Example: queueItem.priority = 50; break
case DirectiveType.Org:
docState.orgs[directive.value] = directive
queueItem.priority = 0
break }
case DirectiveType.Org: queueItem.priority = 0; break }
return queueItem }
@ -110,14 +123,11 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
[lineNumber: docText.lineNumber, priority: 50, value: docText] })
println emitQueue
println "----------"
// Sort the emit queue by priority, then line number.
emitQueue.sort(
{i1, i2 -> i1.priority != i2.priority ?
i1.priority - i2.priority :
i1.line - i2.line} as Comparator)
i1.lineNumber - i2.lineNumber} as Comparator)
// Finally, we want to treat the whole block as one markdown chunk, so
// we will concatenate the values in the emit queue and then process
@ -125,7 +135,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
sb = new StringBuilder()
emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) }
return pegdown.markdownToHtml(sb.toString())
return processMarkdown(sb.toString())
}
protected String emit(CodeBlock codeBlock) {
@ -153,19 +163,41 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
// process input inside HTML elements).
case DirectiveType.Api:
return "<div class='api'>" +
pegdown.markdownToHtml(directive.value) + "</div>\n"
processMarkdown(directive.value) + "</div>\n"
// An `@author` directive is turned into a definition list.
case DirectiveType.Author:
return "Author\n: ${directive.value}\n"
case DirectiveType.Copyright:
return "&copy; ${directive.value}\n"
return "\n&copy; ${directive.value}\n"
// An `@example` directive is returned as is
case DirectiveType.Example: return directive.value
case DirectiveType.Example:
return directive.value
// An `@org` directive is ignored.
case DirectiveType.Org: return "" }
}
protected String processMarkdown(String markdown) {
// convert to HTML from Markdown
String html = pegdown.markdownToHtml(markdown)
// replace internal `jlp://` links with actual links based on`@org`
// references
html = html.replaceAll(/jlp:\/\/([^\s"]+)/) { wholeMatch, linkId ->
def link = docState.orgs[linkId]
String newLink
if (!link) {
/* TODO: log error */
newLink = "broken_link(${linkId})" }
else if (docState.currentDocId == link.sourceDocId) {
newLink = "#$linkId" }
else { newLink = "${link.sourceDocId}#${linkId}" }
return newLink }
return html;
}
}