Syntax highlighting, refactor of link resolution.
* Added syntax highlighting using SyntaxHighlighter v3.0.83 (see https://github.com/alexgorbatchev/SyntaxHighlighter) * Modified Directive to have a link to the DocBlock that contains it. Modified JLPPegParser to account for this change. * Modified LinkAnchor to include the ASTNode defining it. Also added LinkAnchorType enum to facilitate different types of links. * LiterateMarkdownGenerator is now escaping HTML characters in the code sections and other places it is appropriate. * Refactored the link resolution process. Added a new method `Processor.resolveLink(String, TargetDoc)` that now resolves a URL or URL fragment relative to the current output context. This function also takes over the job of resolving JLP links to link anchors and generating the correct URL. LiterateMarkdownGenerator now calls this instead of doing all the work itself. * To support the syntax highlighter, the JarUtil class from com.jdbernard.util is included and the syntax highlighter resources are extracted into the output directory from a resource jar stored in the JLP main jar. * The CSS and other assets for the output are no longer copied into every output directory, but now stored at the output root and linked correctly into the output files. * Added references to the source file and file type for TargetDocs instead of computing them on the fly during processing.
This commit is contained in:
parent
aac5915df7
commit
5028d38e35
BIN
lib/compile/jar/commons-lang3-3.1.jar
Normal file
BIN
lib/compile/jar/commons-lang3-3.1.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/jdb-util-1.5.2.jar
Normal file
BIN
lib/compile/jar/jdb-util-1.5.2.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/commons-lang3-3.1.jar
Normal file
BIN
lib/runtime/jar/commons-lang3-3.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/jdb-util-1.5.2.jar
Normal file
BIN
lib/runtime/jar/jdb-util-1.5.2.jar
Normal file
Binary file not shown.
@ -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
|
||||
|
65
resources/main/css/jlp.css
Normal file
65
resources/main/css/jlp.css
Normal file
@ -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; }
|
@ -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 */
|
BIN
resources/main/syntax-highlighter.jar
Normal file
BIN
resources/main/syntax-highlighter.jar
Normal file
Binary file not shown.
@ -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
|
||||
|
@ -277,7 +277,8 @@ public class JLPPegParser extends BaseParser<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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(); }
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
"""<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>${processor.currentDocId}</title>
|
||||
<title>${escape(processor.currentDocId)}</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" media="all" href="jlp.css"/>
|
||||
<link type="text/css" rel="stylesheet" media="all"
|
||||
href="${resolveLink('/css/jlp.css')}"></link>
|
||||
|
||||
<!-- syntax highlighting plugin -->
|
||||
<link type="text/css" rel="stylesheet" media="all"
|
||||
href="${resolveLink('/sh/styles/shCoreDefault.css')}"></link>
|
||||
<script type="text/javascript"
|
||||
src="${resolveLink('/sh/scripts/XRegExp.js')}"></script>
|
||||
<script type="text/javascript"
|
||||
src="${resolveLink('/sh/scripts/shCore.js')}"></script>""")
|
||||
|
||||
/// If there is a language-specific brush, include it
|
||||
def shBrush = processor.shBrushForSourceType(
|
||||
processor.currentDoc.sourceType)
|
||||
|
||||
if (shBrush) { sb.append("""
|
||||
|
||||
<script type="text/javascript"
|
||||
src="${resolveLink('/sh/scripts/' + shBrush + '.js')}"></script>""") }
|
||||
|
||||
/// Finish our header and begin the body.
|
||||
sb.append("""
|
||||
<script type="text/javascript">
|
||||
SyntaxHighlighter.defaults.light = true;
|
||||
SyntaxHighlighter.defaults.unindent = false;
|
||||
SyntaxHighlighter.all();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<table cellpadding="0" cellspacing="0">
|
||||
<thead><tr>
|
||||
<th class="docs"><h1>${processor.currentDocId}</h1></th>
|
||||
<th class="docs"><h1>${escape(processor.currentDocId)}</h1></th>
|
||||
<th class="code"/>
|
||||
</tr></thead>
|
||||
<tbody>""")
|
||||
@ -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<tr id='${orgDir.value}'>") }
|
||||
else { sb.append("<tr>") }
|
||||
|
||||
@ -183,8 +212,9 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
||||
|
||||
codeLines = codeLines.collect { arr -> arr[1] }
|
||||
|
||||
/// Write out the lines in a <pre><code> block
|
||||
return "<pre><code>${codeLines.join('')}</code></pre>" }
|
||||
/// Write out the lines in a `<pre>` block
|
||||
return "<pre class=\"brush: ${processor.currentDoc.sourceType};\">" +
|
||||
"${escape(codeLines.join(''))}</pre>" }
|
||||
|
||||
/** @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) }
|
||||
|
||||
}
|
||||
|
@ -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<File> 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, '<!--', '-->',
|
||||
'#$%^&*()_-+=|;:\'",<>?~`', '<<?')
|
||||
break
|
||||
case 'c':
|
||||
case 'c++':
|
||||
case 'cpp':
|
||||
case 'groovy':
|
||||
case 'java':
|
||||
case 'javascript':
|
||||
|
@ -17,6 +17,11 @@ public class TargetDoc {
|
||||
|
||||
/// The original source file.
|
||||
public File sourceFile
|
||||
public String sourceDocId
|
||||
|
||||
/// The source code type (ie. `java`, `erlang`, etc.). See
|
||||
/// [Processor.sourceTypeForFile](jlp.jdb-labs.com/Processor/sourceTypeForFile)
|
||||
public String sourceType
|
||||
|
||||
public String output
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
package com.jdblabs.jlp.ast
|
||||
|
||||
/**
|
||||
* ASTNode for *Block*s.
|
||||
* ASTNode for *Blocks*.
|
||||
* @org jlp.jdb-labs.com/ast/Block
|
||||
*/
|
||||
public class Block extends ASTNode {
|
||||
|
@ -59,10 +59,12 @@ public class Directive extends ASTNode {
|
||||
public final DirectiveType type;
|
||||
public final String value;
|
||||
|
||||
public Directive(String value, String typeString, int lineNumber) {
|
||||
public Directive(String value, String typeString, int lineNumber,
|
||||
DocBlock parentBlock) {
|
||||
super(lineNumber)
|
||||
this.value = value
|
||||
this.type = DirectiveType.parse(typeString) }
|
||||
this.type = DirectiveType.parse(typeString)
|
||||
this.parentBlock = parentBlock }
|
||||
|
||||
public String toString() { "[${lineNumber}:Directive: ${type}, ${value}]" }
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ package com.jdblabs.jlp.ast
|
||||
|
||||
/**
|
||||
* The top-level AST element. This represents a source file.
|
||||
* @org jlp.jdb-labs.com/ast/SourceFile */
|
||||
* @org jlp.jdb-labs.com/ast/SourceFile
|
||||
*/
|
||||
public class SourceFile {
|
||||
|
||||
/** The source file gets split into two parts during the initial parsing by
|
||||
|
Loading…
Reference in New Issue
Block a user