Hidden files are ignored. Added --version option and logging.
* Added logging with SLF4J and Logback * Added `--version` option. * Mofidied the input file rules. When an input object is a directory, JLPMain is adding all the files in that directory and its subdirectories. Now JLPMain is ignoring hidden files in the directory and subdirs. A file named explicitly on the command line is still included regardless of if it is hidden or not. * Documentation continues.
This commit is contained in:
parent
f5c7ac64e3
commit
bfc0c12127
BIN
lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
BIN
lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
BIN
lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/logback-core-0.9.26.jar
Normal file
BIN
lib/runtime/jar/logback-core-0.9.26.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
BIN
lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
#Sun, 25 Dec 2011 23:23:16 -0600
|
#Wed, 28 Dec 2011 15:44:01 -0600
|
||||||
name=jlp
|
name=jlp
|
||||||
version=1.1
|
version=1.2
|
||||||
build.number=7
|
build.number=10
|
||||||
lib.local=true
|
lib.local=true
|
||||||
release.dir=release
|
release.dir=release
|
||||||
main.class=com.jdblabs.jlp.JLPMain
|
main.class=com.jdblabs.jlp.JLPMain
|
||||||
|
17
resources/main/logback.groovy
Normal file
17
resources/main/logback.groovy
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
|
||||||
|
import ch.qos.logback.core.ConsoleAppender
|
||||||
|
import ch.qos.logback.core.FileAppender
|
||||||
|
|
||||||
|
import static ch.qos.logback.classic.Level.*
|
||||||
|
|
||||||
|
appender("stdout", ConsoleAppender) {
|
||||||
|
encoder(PatternLayoutEncoder) {
|
||||||
|
pattern = "%level %msg%n" }}
|
||||||
|
|
||||||
|
appender("logfile", FileAppender) {
|
||||||
|
file = "jlp.log"
|
||||||
|
encoder(PatternLayoutEncoder) {
|
||||||
|
pattern = "%date %level %logger{10} [%file:%line] %msg%n" }}
|
||||||
|
|
||||||
|
root(WARN, ["stdout"])
|
||||||
|
//logger("com.jdblabs.jlp", TRACE, ["file"])
|
@ -9,15 +9,47 @@ import java.util.List
|
|||||||
import java.util.Map
|
import java.util.Map
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This class defines the interface for JLP Generators and implements basic
|
||||||
|
* functionality for the parse phase.
|
||||||
* @org jlp.jdb-labs.com/JLPBaseGenerator
|
* @org jlp.jdb-labs.com/JLPBaseGenerator
|
||||||
*/
|
*/
|
||||||
public abstract class JLPBaseGenerator {
|
public abstract class JLPBaseGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The generator works in close conjunction with the JLP Processor.
|
||||||
|
* This tight coupling in intended for these two classes. The distiction
|
||||||
|
* between the two classes is scope. The Processor class is concerned with
|
||||||
|
* data and behavior common to the whole documentation process whereas the
|
||||||
|
* Generator is concerned only with one file. The Generator does have
|
||||||
|
* access to information about the overall process through this link to the
|
||||||
|
* Processor. One example of the need for this link is the resolution of
|
||||||
|
* JLP link targets which may be defined in a different file but referenced
|
||||||
|
* in the file the Generator is processing.
|
||||||
|
* @org jlp.jdb-labs.com/notes/processor-generator-coupling
|
||||||
|
*/
|
||||||
protected Processor processor
|
protected Processor processor
|
||||||
|
|
||||||
protected JLPBaseGenerator(Processor processor) {
|
protected JLPBaseGenerator(Processor processor) {
|
||||||
this.processor = processor }
|
this.processor = processor }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ### Generator phases
|
||||||
|
*
|
||||||
|
* There are two phases for JLP Generators: **parse** and **emit**.
|
||||||
|
*
|
||||||
|
* The **parse** phase allows the Generator to build up facts about the
|
||||||
|
* file being processed before emitting the documentation in the **emit**
|
||||||
|
* phase. There is a `parse` method for each `ASTNode` type. The default
|
||||||
|
* implementation for JLPBaseGenerator calls the `parse` methods in such a
|
||||||
|
* way that it visits each ASTNode in the file.
|
||||||
|
*
|
||||||
|
* The **emit** phase is where the Generator creates the documentation for
|
||||||
|
* each AST node. Unlike the parse phase, there is no default implementation
|
||||||
|
* for the emit phase as emitting the final result will be very dependant on
|
||||||
|
* the emitter.
|
||||||
|
*
|
||||||
|
* @org com.jdb-labs.jlp.JLPBaseGenerator/phases
|
||||||
|
*/
|
||||||
protected void parse(SourceFile sourceFile) {
|
protected void parse(SourceFile sourceFile) {
|
||||||
sourceFile.blocks.each { block -> parse(block) } }
|
sourceFile.blocks.each { block -> parse(block) } }
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ import com.jdblabs.jlp.ast.ASTNode
|
|||||||
import com.jdblabs.jlp.ast.SourceFile
|
import com.jdblabs.jlp.ast.SourceFile
|
||||||
import org.parboiled.Parboiled
|
import org.parboiled.Parboiled
|
||||||
import org.parboiled.parserunners.ReportingParseRunner
|
import org.parboiled.parserunners.ReportingParseRunner
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api JLPMain is the entrypoint for the system. It is responsible for parsing
|
* @api JLPMain is the entrypoint for the system. It is responsible for parsing
|
||||||
@ -16,6 +18,10 @@ import org.parboiled.parserunners.ReportingParseRunner
|
|||||||
*/
|
*/
|
||||||
public class JLPMain {
|
public class JLPMain {
|
||||||
|
|
||||||
|
public static final String VERSION = "1.2"
|
||||||
|
|
||||||
|
private static Logger log = LoggerFactory.getLogger(JLPMain.class)
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
/// #### Define command-line options.
|
/// #### Define command-line options.
|
||||||
@ -33,18 +39,24 @@ public class JLPMain {
|
|||||||
longOpt: 'css-file', args: 1, required: false, argName: 'css-file')
|
longOpt: 'css-file', args: 1, required: false, argName: 'css-file')
|
||||||
cli._(longOpt: 'relative-path-root', args: 1, required: false,
|
cli._(longOpt: 'relative-path-root', args: 1, required: false,
|
||||||
'Resolve all relative paths against this root.')
|
'Resolve all relative paths against this root.')
|
||||||
|
cli._(longOpt: 'version', 'Display the JLP version information.')
|
||||||
|
|
||||||
/// #### Parse the options.
|
/// #### Parse the options.
|
||||||
def opts = cli.parse(args)
|
def opts = cli.parse(args)
|
||||||
|
|
||||||
/// Display help if requested.
|
/// Display help and version information if requested.
|
||||||
if (opts.h) {
|
if (opts.h) {
|
||||||
cli.usage()
|
cli.usage()
|
||||||
return }
|
return }
|
||||||
|
|
||||||
|
if (opts.version) {
|
||||||
|
println "JLP v$VERSION"
|
||||||
|
return }
|
||||||
|
|
||||||
/// Get the relative path root (or set to current directory if it was
|
/// Get the relative path root (or set to current directory if it was
|
||||||
/// not given)
|
/// not given)
|
||||||
def pathRoot = new File(opts."relative-path-root" ?: ".")
|
def pathRoot = new File(opts."relative-path-root" ?: ".")
|
||||||
|
log.debug("Relative path root: '{}'.", pathRoot.canonicalPath)
|
||||||
|
|
||||||
/// If our root is non-existant we will print an error and exit.. This
|
/// If our root is non-existant we will print an error and exit.. This
|
||||||
/// is possible if a relative path root was passed as an option.
|
/// is possible if a relative path root was passed as an option.
|
||||||
@ -63,6 +75,8 @@ public class JLPMain {
|
|||||||
/// Create the output directory if it does not exist.
|
/// Create the output directory if it does not exist.
|
||||||
if (!outputDir.exists()) outputDir.mkdirs()
|
if (!outputDir.exists()) outputDir.mkdirs()
|
||||||
|
|
||||||
|
log.debug("Output directory: '{}'.", outputDir.canonicalPath)
|
||||||
|
|
||||||
/// Get the CSS theme to use. We will start by assuming the default will
|
/// Get the CSS theme to use. We will start by assuming the default will
|
||||||
/// be used.
|
/// be used.
|
||||||
def css = JLPMain.class.getResourceAsStream("/jlp.css")
|
def css = JLPMain.class.getResourceAsStream("/jlp.css")
|
||||||
@ -76,7 +90,10 @@ public class JLPMain {
|
|||||||
cssFile = new File(pathRoot, cssFile.path) }
|
cssFile = new File(pathRoot, cssFile.path) }
|
||||||
|
|
||||||
/// Finally, make sure the CSS file actually exists.
|
/// Finally, make sure the CSS file actually exists.
|
||||||
if (cssFile.exists()) { css = cssFile }
|
if (cssFile.exists()) {
|
||||||
|
css = cssFile
|
||||||
|
log.debug("Loading CSS from this file: '{}'.",
|
||||||
|
cssFile.canonicalPath) }
|
||||||
|
|
||||||
/// If it does not, we are going to warn the user and keep the
|
/// If it does not, we are going to warn the user and keep the
|
||||||
/// default.
|
/// default.
|
||||||
@ -111,9 +128,9 @@ public class JLPMain {
|
|||||||
|
|
||||||
/// If this file is a directory, we want to add all the files in it
|
/// If this file is a directory, we want to add all the files in it
|
||||||
/// to our input list, recursing into all the subdirectories and
|
/// to our input list, recursing into all the subdirectories and
|
||||||
/// adding their files as well.
|
/// adding their files as well. We will ignore hidden files.
|
||||||
if (file.isDirectory()) { file.eachFileRecurse {
|
if (file.isDirectory()) { file.eachFileRecurse {
|
||||||
if (it.isFile()) { inputFiles << it }}}
|
if (it.isFile() && !it.isHidden()) { inputFiles << it }}}
|
||||||
|
|
||||||
/// Not a directory, just add the file.
|
/// Not a directory, just add the file.
|
||||||
else { inputFiles << file } }
|
else { inputFiles << file } }
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp;
|
package com.jdblabs.jlp;
|
||||||
|
|
||||||
import com.jdblabs.jlp.ast.SourceFile;
|
import com.jdblabs.jlp.ast.SourceFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JLPParser is a simple interface. It has one method to return a parsed
|
||||||
|
* [`SourceFile`](jlp://jlp.jdb-labs.com/SourceFile) given an input string. It may
|
||||||
|
* be expanded in the future to be an abstract class implementing methods that
|
||||||
|
* take additional input for convenience.
|
||||||
|
* @org jlp.jdb-labs.com/JLPParser
|
||||||
|
*/
|
||||||
public interface JLPParser {
|
public interface JLPParser {
|
||||||
public SourceFile parse(String input); }
|
public SourceFile parse(String input); }
|
||||||
|
@ -10,6 +10,10 @@ import org.parboiled.Rule;
|
|||||||
import org.parboiled.annotations.*;
|
import org.parboiled.annotations.*;
|
||||||
import org.parboiled.parserunners.ReportingParseRunner;
|
import org.parboiled.parserunners.ReportingParseRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @org jlp.jdb-labs.com/JLPPegParser
|
||||||
|
*/
|
||||||
@BuildParseTree
|
@BuildParseTree
|
||||||
public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
||||||
|
|
||||||
@ -38,77 +42,79 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* SourceFile = (Block / DocBlock / CodeBlock)+
|
*
|
||||||
|
* SourceFile = (Block / DocBlock / CodeBlock)+
|
||||||
*
|
*
|
||||||
* Pushes a SourceFile node on the stack.
|
* Pushes a SourceFile node on the stack.
|
||||||
*/
|
*/
|
||||||
public Rule SourceFile() {
|
public Rule SourceFile() {
|
||||||
return Sequence(
|
return Sequence(
|
||||||
// At the start of processing a new SourceFile, we need to set up
|
/// At the start of processing a new SourceFile, we need to set up
|
||||||
// our internal state.
|
/// our internal state.
|
||||||
|
|
||||||
// Clear the line count.
|
/// Clear the line count.
|
||||||
clearLineCount(),
|
clearLineCount(),
|
||||||
|
|
||||||
// Add the top-level SourceFile AST node.
|
/// Add the top-level SourceFile AST node.
|
||||||
push(new SourceFile()),
|
push(new SourceFile()),
|
||||||
|
|
||||||
// A SourceFile is made up of one or more Blocks
|
/// A SourceFile is made up of one or more Blocks
|
||||||
OneOrMore(Sequence(
|
OneOrMore(Sequence(
|
||||||
// All of these options result in one new Block pushed onto the
|
/// All of these options result in one new Block pushed onto the
|
||||||
// stack.
|
/// stack.
|
||||||
FirstOf(
|
FirstOf(
|
||||||
|
|
||||||
// Match a whole Block. This pushes a Block on the stack.
|
/// Match a whole Block. This pushes a Block on the stack.
|
||||||
Block(),
|
Block(),
|
||||||
|
|
||||||
// A standalone DocBlock. We will create an empty CodeBlock
|
/// A standalone DocBlock. We will create an empty CodeBlock
|
||||||
// to pair with it, then push a new Block onto the stack
|
/// to pair with it, then push a new Block onto the stack
|
||||||
// made from the DocBlock and the empty CodeBlock
|
/// made from the DocBlock and the empty CodeBlock
|
||||||
Sequence(
|
Sequence(
|
||||||
// 1. We need to remember the line number to create the
|
/// 1. We need to remember the line number to create the
|
||||||
// Block
|
/// Block
|
||||||
push(curLineNum),
|
push(curLineNum),
|
||||||
|
|
||||||
// 2. Match the DocBlock.
|
/// 2. Match the DocBlock.
|
||||||
DocBlock(),
|
DocBlock(),
|
||||||
|
|
||||||
// 3. Create the empty CodeBlock.
|
/// 3. Create the empty CodeBlock.
|
||||||
push(new CodeBlock(curLineNum)),
|
push(new CodeBlock(curLineNum)),
|
||||||
|
|
||||||
// 4. Create the Block and push it onto the stack.
|
/// 4. Create the Block and push it onto the stack.
|
||||||
push(new Block((CodeBlock) pop(), (DocBlock) pop(),
|
push(new Block((CodeBlock) pop(), (DocBlock) pop(),
|
||||||
popAsInt()))),
|
popAsInt()))),
|
||||||
|
|
||||||
// A standalone CodeBlock. Similar to the standalone
|
/// A standalone CodeBlock. Similar to the standalone
|
||||||
// DocBlock, we will create an empty DocBlock to pair with
|
/// DocBlock, we will create an empty DocBlock to pair with
|
||||||
// the CodeBlock to make a Block, which is pushed onto the
|
/// the CodeBlock to make a Block, which is pushed onto the
|
||||||
// stack:
|
/// stack:
|
||||||
//
|
///
|
||||||
// *Note: With the way the parser is currently written,
|
/// *Note: With the way the parser is currently written,
|
||||||
// this will only match a CodeBlock that occurs
|
/// this will only match a CodeBlock that occurs
|
||||||
// before any DocBlock.*
|
/// before any DocBlock.*
|
||||||
Sequence(
|
Sequence(
|
||||||
// 1. Remember the line number for the Block.
|
/// 1. Remember the line number for the Block.
|
||||||
push(curLineNum),
|
push(curLineNum),
|
||||||
|
|
||||||
// 2. Create the empty DocBlock.
|
/// 2. Create the empty DocBlock.
|
||||||
push(new DocBlock(curLineNum)),
|
push(new DocBlock(curLineNum)),
|
||||||
|
|
||||||
// 3. Match the CodeBlock
|
/// 3. Match the CodeBlock
|
||||||
CodeBlock(),
|
CodeBlock(),
|
||||||
|
|
||||||
// 4. Create the Block and push it onto the stack
|
/// 4. Create the Block and push it onto the stack
|
||||||
push(new Block((CodeBlock) pop(), (DocBlock) pop(),
|
push(new Block((CodeBlock) pop(), (DocBlock) pop(),
|
||||||
popAsInt())))),
|
popAsInt())))),
|
||||||
|
|
||||||
// pop the Block created by one of the above options and add it
|
/// pop the Block created by one of the above options and add it
|
||||||
// to the SourceFile
|
/// to the SourceFile
|
||||||
addBlockToSourceFile((Block) pop())))); }
|
addBlockToSourceFile((Block) pop())))); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* Block = DocBlock CodeBlock
|
*
|
||||||
|
* Block = DocBlock CodeBlock
|
||||||
*
|
*
|
||||||
* Pushes a Block onto the stack
|
* Pushes a Block onto the stack
|
||||||
*/
|
*/
|
||||||
@ -117,14 +123,15 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
push(curLineNum),
|
push(curLineNum),
|
||||||
DocBlock(), CodeBlock(),
|
DocBlock(), CodeBlock(),
|
||||||
|
|
||||||
// A DocBlock and a CodeBlock are pushed onto the stack by the
|
/// A DocBlock and a CodeBlock are pushed onto the stack by the
|
||||||
// above rules. Pop them off, along with the line number we pushed
|
/// above rules. Pop them off, along with the line number we pushed
|
||||||
// before that, and create a new Block node.
|
/// before that, and create a new Block node.
|
||||||
push(new Block((CodeBlock) pop(), (DocBlock) pop(), popAsInt()))); }
|
push(new Block((CodeBlock) pop(), (DocBlock) pop(), popAsInt()))); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* DocBlock = SDocBlock / MDocBlock
|
*
|
||||||
|
* DocBlock = SDocBlock / MDocBlock
|
||||||
*
|
*
|
||||||
* Pushes a DocBlock onto the stack.
|
* Pushes a DocBlock onto the stack.
|
||||||
*/
|
*/
|
||||||
@ -132,7 +139,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* SDocBlock = (SDirective / SDocText)+
|
*
|
||||||
|
* SDocBlock = (SDirective / SDocText)+
|
||||||
*
|
*
|
||||||
* Pushes a DocBlock object onto the stack
|
* Pushes a DocBlock object onto the stack
|
||||||
*/
|
*/
|
||||||
@ -145,7 +153,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* MDocBlock = MDOC_START (MDirective / MDocText)+ MDOC_END
|
*
|
||||||
|
* MDocBlock = MDOC_START (MDirective / MDocText)+ MDOC_END
|
||||||
*
|
*
|
||||||
* Pushes a DocBlock object onto the stack
|
* Pushes a DocBlock object onto the stack
|
||||||
*/
|
*/
|
||||||
@ -154,16 +163,17 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
push(new DocBlock(curLineNum)),
|
push(new DocBlock(curLineNum)),
|
||||||
MDOC_START,
|
MDOC_START,
|
||||||
ZeroOrMore(Sequence(
|
ZeroOrMore(Sequence(
|
||||||
// We need to be careful to exclude MDOC_END here, as there can
|
/// We need to be careful to exclude MDOC_END here, as there can
|
||||||
// be some confusion otherwise between the start of a line with
|
/// be some confusion otherwise between the start of a line with
|
||||||
// MDOC_LINE_START and MDOC_END depending on what values the
|
/// MDOC_LINE_START and MDOC_END depending on what values the
|
||||||
// user has chosen for them
|
/// user has chosen for them
|
||||||
TestNot(MDOC_END), FirstOf(MDirective(), MDocText()),
|
TestNot(MDOC_END), FirstOf(MDirective(), MDocText()),
|
||||||
addToDocBlock((ASTNode) pop()))),
|
addToDocBlock((ASTNode) pop()))),
|
||||||
MDOC_END); }
|
MDOC_END); }
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* CodeBlock = (RemainingCodeLine)+
|
*
|
||||||
|
* CodeBlock = (RemainingCodeLine)+
|
||||||
*
|
*
|
||||||
* Pushes a CodeBlock onto the stack.
|
* Pushes a CodeBlock onto the stack.
|
||||||
*/
|
*/
|
||||||
@ -175,7 +185,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* SDirective = SDocLineStart AT (SLongDirective / SShortDirective)
|
*
|
||||||
|
* SDirective = SDocLineStart AT (SLongDirective / SShortDirective)
|
||||||
*
|
*
|
||||||
* Pushes a Directive node on the stack.
|
* Pushes a Directive node on the stack.
|
||||||
*/
|
*/
|
||||||
@ -185,7 +196,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* MDirective = MDocLineStart? AT (MLongDirective / MShortDirective)
|
*
|
||||||
|
* MDirective = MDocLineStart? AT (MLongDirective / MShortDirective)
|
||||||
*
|
*
|
||||||
* Pushes a Directive node onto the stack.
|
* Pushes a Directive node onto the stack.
|
||||||
*/
|
*/
|
||||||
@ -196,7 +208,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* SLongDirective =
|
*
|
||||||
|
* SLongDirective =
|
||||||
* (API_DIR / EXAMPLE_DIR) RemainingSDocLine SDocText?
|
* (API_DIR / EXAMPLE_DIR) RemainingSDocLine SDocText?
|
||||||
*
|
*
|
||||||
* Pushes a Directive node onto the stack.
|
* Pushes a Directive node onto the stack.
|
||||||
@ -217,7 +230,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* MLongDirective =
|
*
|
||||||
|
* MLongDirective =
|
||||||
* (API_DIR / EXAMPLE_DIR) RemainingMDocLine MDocText?
|
* (API_DIR / EXAMPLE_DIR) RemainingMDocLine MDocText?
|
||||||
*
|
*
|
||||||
* Pushes a Directive node onto the stack.
|
* Pushes a Directive node onto the stack.
|
||||||
@ -238,7 +252,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* SShortDirective = (AUTHOR_DIR / ORG_DIR / COPYRIGHT_DIR) RemainingSDocLine
|
*
|
||||||
|
* SShortDirective = (AUTHOR_DIR / ORG_DIR / COPYRIGHT_DIR) RemainingSDocLine
|
||||||
*
|
*
|
||||||
* Pushes a Directive node onto the stack.
|
* Pushes a Directive node onto the stack.
|
||||||
*/
|
*/
|
||||||
@ -252,7 +267,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* MShortDirective = (AUTHOR_DIR / ORG_DIR / COPYRIGHT_DIR) RemainingMDocLine
|
*
|
||||||
|
* MShortDirective = (AUTHOR_DIR / ORG_DIR / COPYRIGHT_DIR) RemainingMDocLine
|
||||||
*
|
*
|
||||||
* Pushes a Directive node onto the stack.
|
* Pushes a Directive node onto the stack.
|
||||||
*/
|
*/
|
||||||
@ -266,7 +282,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* SDocText = (SDocLineStart !AT RemainingSDocLine)+
|
*
|
||||||
|
* SDocText = (SDocLineStart !AT RemainingSDocLine)+
|
||||||
*
|
*
|
||||||
* Pushes a DocText node onto the stack.
|
* Pushes a DocText node onto the stack.
|
||||||
*/
|
*/
|
||||||
@ -279,7 +296,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* MDocText = (MDocLineStart? !AT RemainingMDocLine)+
|
*
|
||||||
|
* MDocText = (MDocLineStart? !AT RemainingMDocLine)+
|
||||||
*
|
*
|
||||||
* Pushes a DocText node onto the stack.
|
* Pushes a DocText node onto the stack.
|
||||||
*/
|
*/
|
||||||
@ -293,7 +311,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* SDocLineStart = SPACE* SDOC_START SPACE?
|
*
|
||||||
|
* SDocLineStart = SPACE* SDOC_START SPACE?
|
||||||
*/
|
*/
|
||||||
Rule SDocLineStart() {
|
Rule SDocLineStart() {
|
||||||
return Sequence(
|
return Sequence(
|
||||||
@ -301,7 +320,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* MDocLineStart = SPACE* !MDOC_END MDOC_LINE_START SPACE?
|
*
|
||||||
|
* MDocLineStart = SPACE* !MDOC_END MDOC_LINE_START SPACE?
|
||||||
*/
|
*/
|
||||||
Rule MDocLineStart() {
|
Rule MDocLineStart() {
|
||||||
return Sequence(
|
return Sequence(
|
||||||
@ -309,7 +329,8 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* RemainingSDocLine = ((!EOL)* EOL) / ((!EOL)+ EOI)
|
*
|
||||||
|
* RemainingSDocLine = ((!EOL)* EOL) / ((!EOL)+ EOI)
|
||||||
*/
|
*/
|
||||||
Rule RemainingSDocLine() {
|
Rule RemainingSDocLine() {
|
||||||
return FirstOf(
|
return FirstOf(
|
||||||
@ -318,30 +339,32 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* RemainingMDocLine =
|
*
|
||||||
|
* RemainingMDocLine =
|
||||||
* ((!(EOL / MDOC_END))* EOL) /
|
* ((!(EOL / MDOC_END))* EOL) /
|
||||||
* ((!MDOC_END)+)
|
* ((!MDOC_END)+)
|
||||||
*/
|
*/
|
||||||
Rule RemainingMDocLine() {
|
Rule RemainingMDocLine() {
|
||||||
return FirstOf(
|
return FirstOf(
|
||||||
// End of line, still within the an M-style comment block
|
/// End of line, still within the an M-style comment block
|
||||||
Sequence(
|
Sequence(
|
||||||
ZeroOrMore(Sequence(TestNot(FirstOf(EOL, MDOC_END)), ANY)),
|
ZeroOrMore(Sequence(TestNot(FirstOf(EOL, MDOC_END)), ANY)),
|
||||||
EOL,
|
EOL,
|
||||||
incLineCount()),
|
incLineCount()),
|
||||||
|
|
||||||
// End of M-style comment block
|
/// End of M-style comment block
|
||||||
OneOrMore(Sequence(TestNot(MDOC_END), ANY))); }
|
OneOrMore(Sequence(TestNot(MDOC_END), ANY))); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the rule:
|
* Parses the rule:
|
||||||
* RemainingCodeLine =
|
*
|
||||||
|
* RemainingCodeLine =
|
||||||
* ((!(EOL / MDOC_START / SDocLineStart))* EOL) /
|
* ((!(EOL / MDOC_START / SDocLineStart))* EOL) /
|
||||||
* (!(MDOC_START / SDocLineStart))+
|
* (!(MDOC_START / SDocLineStart))+
|
||||||
*/
|
*/
|
||||||
Rule RemainingCodeLine() {
|
Rule RemainingCodeLine() {
|
||||||
return FirstOf(
|
return FirstOf(
|
||||||
// End of line, still within the code block.
|
/// End of line, still within the code block.
|
||||||
Sequence(
|
Sequence(
|
||||||
ZeroOrMore(Sequence(
|
ZeroOrMore(Sequence(
|
||||||
TestNot(FirstOf(EOL, MDOC_START, SDocLineStart())),
|
TestNot(FirstOf(EOL, MDOC_START, SDocLineStart())),
|
||||||
@ -349,7 +372,7 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
EOL,
|
EOL,
|
||||||
incLineCount()),
|
incLineCount()),
|
||||||
|
|
||||||
// Found an MDOC_START or SDocLineStart
|
/// Found an MDOC_START or SDocLineStart
|
||||||
OneOrMore(Sequence(TestNot(FirstOf(MDOC_START, SDocLineStart())), ANY))); }
|
OneOrMore(Sequence(TestNot(FirstOf(MDOC_START, SDocLineStart())), ANY))); }
|
||||||
|
|
||||||
Rule AT = Ch('@').label("AT");
|
Rule AT = Ch('@').label("AT");
|
||||||
@ -357,13 +380,13 @@ public class JLPPegParser extends BaseParser<Object> implements JLPParser {
|
|||||||
Rule NOT_EOL = Sequence(TestNot(EOL), ANY).label("NOT_EOL");
|
Rule NOT_EOL = Sequence(TestNot(EOL), ANY).label("NOT_EOL");
|
||||||
Rule SPACE = AnyOf(" \t").label("SPACE");
|
Rule SPACE = AnyOf(" \t").label("SPACE");
|
||||||
|
|
||||||
// Configurable
|
/// Configurable
|
||||||
Rule MDOC_START;
|
Rule MDOC_START;
|
||||||
Rule MDOC_END;
|
Rule MDOC_END;
|
||||||
Rule MDOC_LINE_START;
|
Rule MDOC_LINE_START;
|
||||||
Rule SDOC_START;
|
Rule SDOC_START;
|
||||||
|
|
||||||
// directive terminals
|
/// directive terminals
|
||||||
Rule AUTHOR_DIR = IgnoreCase("author");
|
Rule AUTHOR_DIR = IgnoreCase("author");
|
||||||
Rule COPYRIGHT_DIR = IgnoreCase("copyright");
|
Rule COPYRIGHT_DIR = IgnoreCase("copyright");
|
||||||
Rule API_DIR = IgnoreCase("api");
|
Rule API_DIR = IgnoreCase("api");
|
||||||
|
@ -1,10 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp
|
package com.jdblabs.jlp
|
||||||
|
|
||||||
import com.jdblabs.jlp.ast.Directive
|
import com.jdblabs.jlp.ast.Directive
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A *LinkAnchor* in the documentation is very similar to an HTML anchor. It
|
||||||
|
* creates a reference in the documentation that can used by the author to
|
||||||
|
* link to this point.
|
||||||
|
*
|
||||||
|
* The author uses a URL with the `jlp` protocol to refer to anchors. For
|
||||||
|
* example: `jlp://jlp.jdb-labs.com/LinkAnchor` refers to this documentation.
|
||||||
|
*
|
||||||
|
* @api LinkAnchor is a data class to hold information about the link anchors
|
||||||
|
* defined by `@org` directives.
|
||||||
|
* @org jlp.jdb-labs.com/LinkAnchor
|
||||||
|
*/
|
||||||
public class LinkAnchor {
|
public class LinkAnchor {
|
||||||
|
|
||||||
|
/// The anchor id. This comes from the text after the directive.
|
||||||
public String id
|
public String id
|
||||||
|
|
||||||
public Directive directive
|
public Directive directive
|
||||||
public String sourceDocId
|
public String sourceDocId
|
||||||
|
|
||||||
|
@ -13,10 +13,14 @@ import org.pegdown.PegDownProcessor
|
|||||||
import java.util.List
|
import java.util.List
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The LiterateMarkdownGenerator is an implementation of JLPBaseGenerator that
|
||||||
|
* uses [Markdown](http://daringfireball.net/projects/markdown/) to process the
|
||||||
|
* documentation into HTML output.
|
||||||
* @org jlp.jdb-labs.com/LiterateMarkdownGenerator
|
* @org jlp.jdb-labs.com/LiterateMarkdownGenerator
|
||||||
*/
|
*/
|
||||||
public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
||||||
|
|
||||||
|
/// We will use the PegDown library for generating the Markdown output.
|
||||||
protected PegDownProcessor pegdown
|
protected PegDownProcessor pegdown
|
||||||
|
|
||||||
public LiterateMarkdownGenerator(Processor processor) {
|
public LiterateMarkdownGenerator(Processor processor) {
|
||||||
@ -25,6 +29,13 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
pegdown = new PegDownProcessor(
|
pegdown = new PegDownProcessor(
|
||||||
Extensions.TABLES | Extensions.DEFINITIONS) }
|
Extensions.TABLES | Extensions.DEFINITIONS) }
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
/** ### Parse phase implementation. ### */
|
||||||
|
// ===================================
|
||||||
|
|
||||||
|
/** Implement the parse phase for *Directive* nodes. We are interested
|
||||||
|
* specifically in saving the link anchor information from *org*
|
||||||
|
* directives. */
|
||||||
protected void parse(Directive directive) {
|
protected void parse(Directive directive) {
|
||||||
switch(directive.type) {
|
switch(directive.type) {
|
||||||
case DirectiveType.Org:
|
case DirectiveType.Org:
|
||||||
@ -39,14 +50,23 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
break // do nothing
|
break // do nothing
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
/** We are not doing any parsing for *CodeBlocks* or *DocTexts*. We have to
|
||||||
|
* implement them, as they are abstract on JLPBaseGenerator. */
|
||||||
protected void parse(CodeBlock codeBlock) {} // nothing to do
|
protected void parse(CodeBlock codeBlock) {} // nothing to do
|
||||||
protected void parse(DocText docText) {} // nothing to do
|
protected void parse(DocText docText) {} // nothing to do
|
||||||
|
|
||||||
|
// ==================================
|
||||||
|
/** ### Emit phase implementation. ### */
|
||||||
|
// ==================================
|
||||||
|
|
||||||
|
/** @api Emit a *SourceFile*.
|
||||||
|
* Each *SourceFile* becomes one ouput HTML file. This method is the entry
|
||||||
|
* point for a file to be emitted. */
|
||||||
protected String emit(SourceFile sourceFile) {
|
protected String emit(SourceFile sourceFile) {
|
||||||
StringBuilder sb = new StringBuilder()
|
StringBuilder sb = new StringBuilder()
|
||||||
|
|
||||||
|
|
||||||
// Create the HTML file head
|
/// Create the HTML head and begin the body.
|
||||||
sb.append(
|
sb.append(
|
||||||
"""<!DOCTYPE html>
|
"""<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@ -64,10 +84,10 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody>""")
|
<tbody>""")
|
||||||
|
|
||||||
// Emit the document to Markdown
|
/// Emit all of the blocks in the body of the html file.
|
||||||
sourceFile.blocks.each { block -> sb.append(emit(block)) }
|
sourceFile.blocks.each { block -> sb.append(emit(block)) }
|
||||||
|
|
||||||
// Create the HTML file foot
|
/// Create the HTML footer.
|
||||||
sb.append(
|
sb.append(
|
||||||
""" </tbody>
|
""" </tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -77,39 +97,47 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
|
|
||||||
return sb.toString() }
|
return sb.toString() }
|
||||||
|
|
||||||
|
/** @api Emit a *Block*. */
|
||||||
protected String emit(Block block) {
|
protected String emit(Block block) {
|
||||||
StringBuilder sb = new StringBuilder()
|
StringBuilder sb = new StringBuilder()
|
||||||
|
|
||||||
// Look for an `@org` directive in the `Block`
|
/// Look for an `@org` directive in the `Block` (already found in the
|
||||||
|
/// parse phase)..
|
||||||
Directive orgDir = block.docBlock.directives.find {
|
Directive orgDir = block.docBlock.directives.find {
|
||||||
it.type == DirectiveType.Org }
|
it.type == DirectiveType.Org }
|
||||||
|
|
||||||
// Create the `tr` that will hold the `Block`
|
/// Create the `tr` that will hold the `Block`. If we found an `@org`
|
||||||
|
/// directive we will add the id here.
|
||||||
if (orgDir) { sb.append("\n<tr id='${orgDir.value}'>") }
|
if (orgDir) { sb.append("\n<tr id='${orgDir.value}'>") }
|
||||||
else { sb.append("<tr>") }
|
else { sb.append("<tr>") }
|
||||||
|
|
||||||
// Create the `td` for the documentation.
|
/// Create the `td` for the documentation.
|
||||||
sb.append('\n<td class="docs">')
|
sb.append('\n<td class="docs">')
|
||||||
sb.append(emit(block.docBlock))
|
sb.append(emit(block.docBlock))
|
||||||
sb.append('</td>')
|
sb.append('</td>')
|
||||||
|
|
||||||
// Create the `td` for the `CodeBlock`
|
/// Create the `td` for the `CodeBlock`
|
||||||
sb.append('\n<td class="code">')
|
sb.append('\n<td class="code">')
|
||||||
sb.append(emit(block.codeBlock))
|
sb.append(emit(block.codeBlock))
|
||||||
sb.append('</td>')
|
sb.append('</td>')
|
||||||
|
|
||||||
// Close the table row.
|
/// Close the table row.
|
||||||
sb.append('</tr>') }
|
sb.append('</tr>') }
|
||||||
|
|
||||||
|
/** @api Emit a *DocBlock*. */
|
||||||
protected String emit(DocBlock docBlock) {
|
protected String emit(DocBlock docBlock) {
|
||||||
// Create a queue for the doc block elements, we are going to
|
/// Create a queue for the doc block elements, we are going to
|
||||||
// sort them by type and line number
|
/// sort them by type and line number
|
||||||
List emitQueue
|
List emitQueue
|
||||||
|
|
||||||
// Later we will need a string builder to hold our result.
|
/// Later we will need a string builder to hold our result.
|
||||||
StringBuilder sb
|
StringBuilder sb
|
||||||
|
|
||||||
// Add all the directives
|
/** Add all the directives. We are also assigning priorities here that
|
||||||
|
* we will use along with the line numbers to sort the elements. Our
|
||||||
|
* goal is to preserve the order of doc blocks and doc-block-like
|
||||||
|
* elements (examples, api documentation, etc.) while pushing orgs,
|
||||||
|
* authorship and other directives to the top. */
|
||||||
emitQueue = docBlock.directives.collect { directive ->
|
emitQueue = docBlock.directives.collect { directive ->
|
||||||
def queueItem = [lineNumber: directive.lineNumber, value: directive]
|
def queueItem = [lineNumber: directive.lineNumber, value: directive]
|
||||||
switch(directive.type) {
|
switch(directive.type) {
|
||||||
@ -121,90 +149,98 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
|
|
||||||
return queueItem }
|
return queueItem }
|
||||||
|
|
||||||
// Add all the doc text blocks
|
/// Add all the doc text blocks.
|
||||||
emitQueue.addAll(docBlock.docTexts.collect { docText ->
|
emitQueue.addAll(docBlock.docTexts.collect { docText ->
|
||||||
[lineNumber: docText.lineNumber, priority: 50, value: docText] })
|
[lineNumber: docText.lineNumber, priority: 50, value: docText] })
|
||||||
|
|
||||||
|
|
||||||
// Sort the emit queue by priority, then line number.
|
/// Sort the emit queue by priority, then line number.
|
||||||
emitQueue.sort(
|
emitQueue.sort(
|
||||||
{i1, i2 -> i1.priority != i2.priority ?
|
{i1, i2 -> i1.priority != i2.priority ?
|
||||||
i1.priority - i2.priority :
|
i1.priority - i2.priority :
|
||||||
i1.lineNumber - i2.lineNumber} as Comparator)
|
i1.lineNumber - i2.lineNumber} as Comparator)
|
||||||
|
|
||||||
// Finally, we want to treat the whole block as one markdown chunk, so
|
/** 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
|
* we will concatenate the values in the emit queue and then send the
|
||||||
// the whole block once
|
* whole block at once to the markdown processor. */
|
||||||
sb = new StringBuilder()
|
sb = new StringBuilder()
|
||||||
emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) }
|
emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) }
|
||||||
|
|
||||||
return processMarkdown(sb.toString())
|
return processMarkdown(sb.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @api Emit a *CodeBlock*. */
|
||||||
protected String emit(CodeBlock codeBlock) {
|
protected String emit(CodeBlock codeBlock) {
|
||||||
def codeLines
|
def codeLines
|
||||||
|
|
||||||
// Collect the lines into an array.
|
/// Collect the lines into an array.
|
||||||
codeLines = codeBlock.lines.collect { lineNumber, line ->
|
codeLines = codeBlock.lines.collect { lineNumber, line ->
|
||||||
[lineNumber, line] }
|
[lineNumber, line] }
|
||||||
|
|
||||||
// Sort by line number.
|
/// Sort by line number.
|
||||||
codeLines.sort({ i1, i2 -> i1[0] - i2[0] } as Comparator)
|
codeLines.sort({ i1, i2 -> i1[0] - i2[0] } as Comparator)
|
||||||
|
|
||||||
codeLines = codeLines.collect { arr -> arr[1] }
|
codeLines = codeLines.collect { arr -> arr[1] }
|
||||||
|
|
||||||
// write out the lines in a <pre><code> block
|
/// Write out the lines in a <pre><code> block
|
||||||
return "<pre><code>${codeLines.join('')}</code></pre>" }
|
return "<pre><code>${codeLines.join('')}</code></pre>" }
|
||||||
|
|
||||||
|
/** @api Emit a *DocText*. */
|
||||||
protected String emit(DocText docText) { return docText.value }
|
protected String emit(DocText docText) { return docText.value }
|
||||||
|
|
||||||
|
/** @api Emit a *Directive*. */
|
||||||
protected String emit(Directive directive) {
|
protected String emit(Directive directive) {
|
||||||
switch(directive.type) {
|
switch(directive.type) {
|
||||||
|
|
||||||
// An `@api` directive is immediately processed and wrapped in a
|
/** An `@api` directive is immediately processed and wrapped in a
|
||||||
// div (we need to process this now because Markdown does not
|
* div (we need to process this now because Markdown does not
|
||||||
// process input inside HTML elements).
|
* process input inside HTML elements). */
|
||||||
case DirectiveType.Api:
|
case DirectiveType.Api:
|
||||||
return "<div class='api'>" +
|
return "<div class='api'>" +
|
||||||
processMarkdown(directive.value) + "</div>\n"
|
processMarkdown(directive.value) + "</div>\n"
|
||||||
|
|
||||||
// An `@author` directive is turned into a definition list.
|
/// `@author` directive is turned into a definition list.
|
||||||
|
|
||||||
case DirectiveType.Author:
|
case DirectiveType.Author:
|
||||||
return "Author\n: ${directive.value}\n"
|
return "Author\n: ${directive.value}\n"
|
||||||
|
|
||||||
case DirectiveType.Copyright:
|
case DirectiveType.Copyright:
|
||||||
return "\n© ${directive.value}\n"
|
return "\n© ${directive.value}\n"
|
||||||
|
|
||||||
// An `@example` directive is returned as is
|
/// An `@example` directive is returned as is.
|
||||||
case DirectiveType.Example:
|
case DirectiveType.Example:
|
||||||
return directive.value
|
return directive.value
|
||||||
|
|
||||||
// An `@org` directive is ignored.
|
/// An `@org` directive is ignored here. We already emitted the id
|
||||||
case DirectiveType.Org: return "" }
|
/// when we started the block.
|
||||||
}
|
case DirectiveType.Org: return "" } }
|
||||||
|
|
||||||
|
/** This is a helper method to process a block of text as Markdown. We need
|
||||||
|
* to do some additional processing to deal with `jlp://` org links that
|
||||||
|
* may be present. */
|
||||||
protected String processMarkdown(String markdown) {
|
protected String processMarkdown(String markdown) {
|
||||||
// convert to HTML from Markdown
|
|
||||||
|
/// Convert to HTML from Markdown
|
||||||
String html = pegdown.markdownToHtml(markdown)
|
String html = pegdown.markdownToHtml(markdown)
|
||||||
|
|
||||||
// replace internal `jlp://` links with actual links based on`@org`
|
/// Replace internal `jlp://` links with actual links based on`@org`
|
||||||
// references
|
/// references.
|
||||||
html = html.replaceAll(/jlp:\/\/([^\s"]+)/) { wholeMatch, linkId ->
|
html = html.replaceAll(/href=['"]jlp:\/\/([^\s"]+)['"]/) { wholeMatch, linkId ->
|
||||||
|
|
||||||
// Get the org data stored for this org id.
|
/// Get the org data we found in the parse phase for this org id.
|
||||||
def link = processor.linkAnchors[linkId]
|
def link = processor.linkAnchors[linkId]
|
||||||
String newLink
|
String newLink
|
||||||
|
|
||||||
if (!link) {
|
if (!link) {
|
||||||
// We do not have any reference to this id.
|
// We do not have any reference to this id.
|
||||||
/* TODO: log error */
|
/* TODO: log error */
|
||||||
newLink = "broken_link(${linkId})" }
|
newLink = "href=\"broken_link(${linkId})\"" }
|
||||||
|
|
||||||
// This link points to a location in this document.
|
/// This link points to a location in this document.
|
||||||
else if (processor.currentDocId == link.sourceDocId) {
|
else if (processor.currentDocId == link.sourceDocId) {
|
||||||
newLink = "#$linkId" }
|
newLink = "href=\"#${linkId}\"" }
|
||||||
|
|
||||||
// The link should point to a different document.
|
/// The link should point to a different document.
|
||||||
else {
|
else {
|
||||||
TargetDoc thisDoc = processor.currentDoc
|
TargetDoc thisDoc = processor.currentDoc
|
||||||
TargetDoc linkDoc = processor.docs[link.sourceDocId]
|
TargetDoc linkDoc = processor.docs[link.sourceDocId]
|
||||||
@ -213,13 +249,12 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
thisDoc.sourceFile.parentFile,
|
thisDoc.sourceFile.parentFile,
|
||||||
linkDoc.sourceFile)
|
linkDoc.sourceFile)
|
||||||
|
|
||||||
// The target document may not be in the same directory
|
/** The target document may not be in the same directory
|
||||||
// as us, backtrack to the (relative) top of our directory
|
* as us, backtrack to the (relative) top of our directory
|
||||||
// structure.
|
* structure. */
|
||||||
newLink = pathToLinkedDoc + ".html#${linkId}" }
|
newLink = 'href="' + pathToLinkedDoc + ".html#${linkId}\"" }
|
||||||
|
|
||||||
return newLink }
|
return newLink }
|
||||||
|
|
||||||
return html;
|
return html; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp
|
package com.jdblabs.jlp
|
||||||
|
|
||||||
import com.jdblabs.jlp.ast.*
|
import com.jdblabs.jlp.ast.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @org jlp.jdb-labs.com/MarkdownParser
|
||||||
|
*/
|
||||||
public class MarkdownParser implements JLPParser {
|
public class MarkdownParser implements JLPParser {
|
||||||
|
|
||||||
public SourceFile parse(String input) {
|
public SourceFile parse(String input) {
|
||||||
|
@ -6,6 +6,8 @@ package com.jdblabs.jlp
|
|||||||
|
|
||||||
import org.parboiled.BaseParser
|
import org.parboiled.BaseParser
|
||||||
import org.parboiled.Parboiled
|
import org.parboiled.Parboiled
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processor processes one batch of input files to create a set of output files.
|
* Processor processes one batch of input files to create a set of output files.
|
||||||
@ -49,6 +51,8 @@ public class Processor {
|
|||||||
protected Map<String, JLPParser> parsers = [:]
|
protected Map<String, JLPParser> parsers = [:]
|
||||||
protected Map<String, JLPBaseGenerator> generators = [:]
|
protected Map<String, JLPBaseGenerator> generators = [:]
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(getClass())
|
||||||
|
|
||||||
/// ### Public Methods.
|
/// ### Public Methods.
|
||||||
/// @org jlp.jdb-labs.com/Processor/public-methods
|
/// @org jlp.jdb-labs.com/Processor/public-methods
|
||||||
|
|
||||||
@ -97,14 +101,16 @@ public class Processor {
|
|||||||
/// the parser for that file type and parse the file into an abstract
|
/// the parser for that file type and parse the file into an abstract
|
||||||
/// syntax tree (AST).
|
/// syntax tree (AST).
|
||||||
processDocs {
|
processDocs {
|
||||||
|
log.trace("Parsing '{}'.", currentDocId)
|
||||||
def parser = getParser(sourceTypeForFile(currentDoc.sourceFile))
|
def parser = getParser(sourceTypeForFile(currentDoc.sourceFile))
|
||||||
// TODO: error detection
|
// TODO: error detection
|
||||||
currentDoc.sourceAST = parser.parse(currentDoc.sourceFile.text) }
|
currentDoc.sourceAST = parser.parse(currentDoc.sourceFile.text) }
|
||||||
|
|
||||||
/// * Run our generator parse phase (see
|
/// * Run our generator parse phase (see
|
||||||
/// jlp://com.jdb-labs.jlp.JLPBaseGenerator/phases for an explanation
|
/// [`JLPBaseGenerator`](jlp://com.jdb-labs.jlp.JLPBaseGenerator/phases)
|
||||||
/// of the generator phases).
|
/// for an explanation of the generator phases).
|
||||||
processDocs {
|
processDocs {
|
||||||
|
log.trace("Second-pass parsing for '{}'.", currentDocId)
|
||||||
def generator = getGenerator(sourceTypeForFile(currentDoc.sourceFile))
|
def generator = getGenerator(sourceTypeForFile(currentDoc.sourceFile))
|
||||||
// TODO: error detection
|
// TODO: error detection
|
||||||
generator.parse(currentDoc.sourceAST) }
|
generator.parse(currentDoc.sourceAST) }
|
||||||
@ -112,6 +118,7 @@ public class Processor {
|
|||||||
|
|
||||||
/// * Second pass by the generators, the emit phase.
|
/// * Second pass by the generators, the emit phase.
|
||||||
processDocs {
|
processDocs {
|
||||||
|
log.trace("Emitting documentation for '{}'.", currentDocId)
|
||||||
def generator = getGenerator(sourceTypeForFile(currentDoc.sourceFile))
|
def generator = getGenerator(sourceTypeForFile(currentDoc.sourceFile))
|
||||||
currentDoc.output = generator.emit(currentDoc.sourceAST) }
|
currentDoc.output = generator.emit(currentDoc.sourceAST) }
|
||||||
|
|
||||||
@ -125,6 +132,9 @@ public class Processor {
|
|||||||
File outputFile = new File(outputRoot, relativePath + ".html")
|
File outputFile = new File(outputRoot, relativePath + ".html")
|
||||||
File outputDir = outputFile.parentFile
|
File outputDir = outputFile.parentFile
|
||||||
|
|
||||||
|
log.trace("Saving output for '{}' to '{}'",
|
||||||
|
currentDocId, outputFile)
|
||||||
|
|
||||||
/// Create the directory for this file if it does not exist.
|
/// Create the directory for this file if it does not exist.
|
||||||
if (!outputDir.exists()) { outputDir.mkdirs() }
|
if (!outputDir.exists()) { outputDir.mkdirs() }
|
||||||
|
|
||||||
|
@ -1,10 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp
|
package com.jdblabs.jlp
|
||||||
|
|
||||||
import com.jdblabs.jlp.ast.SourceFile
|
import com.jdblabs.jlp.ast.SourceFile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api TargetDoc is a data class to hold information about the output file.
|
||||||
|
* @org jlp.jdb-labs.com/TargetDoc
|
||||||
|
*/
|
||||||
public class TargetDoc {
|
public class TargetDoc {
|
||||||
|
|
||||||
|
/// The result of parsing the input file.
|
||||||
public SourceFile sourceAST
|
public SourceFile sourceAST
|
||||||
|
|
||||||
|
/// The original source file.
|
||||||
public File sourceFile
|
public File sourceFile
|
||||||
|
|
||||||
public String output
|
public String output
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp.ast
|
package com.jdblabs.jlp.ast
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This defines the interface for nodes of the source file parse tree.
|
||||||
|
* @org jlp.jdb-labs.com/ast/ASTNode
|
||||||
|
*/
|
||||||
public class ASTNode {
|
public class ASTNode {
|
||||||
|
|
||||||
|
/// This is the line number the element began on.
|
||||||
protected int lineNumber
|
protected int lineNumber
|
||||||
|
|
||||||
public ASTNode(int lineNum) { this.lineNumber = lineNum }
|
public ASTNode(int lineNum) { this.lineNumber = lineNum }
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp.ast
|
package com.jdblabs.jlp.ast
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASTNode for *Block*s.
|
||||||
|
* @org jlp.jdb-labs.com/ast/Block
|
||||||
|
*/
|
||||||
public class Block extends ASTNode {
|
public class Block extends ASTNode {
|
||||||
public final DocBlock docBlock
|
public final DocBlock docBlock
|
||||||
public final CodeBlock codeBlock
|
public final CodeBlock codeBlock
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
* @org jlp.jdb-labs.com/ast/CodeBlock
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp.ast
|
package com.jdblabs.jlp.ast
|
||||||
|
|
||||||
import java.util.Map
|
import java.util.Map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api ASTNode for *CodeBlock*s.
|
||||||
|
* @org jlp.jdb-labs.com/ast/CodeBlock
|
||||||
|
*/
|
||||||
public class CodeBlock extends ASTNode {
|
public class CodeBlock extends ASTNode {
|
||||||
|
|
||||||
public Map<Integer, String> lines = [:]
|
public Map<Integer, String> lines = [:]
|
||||||
|
@ -1,7 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp.ast
|
package com.jdblabs.jlp.ast
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api ASTNode for *Directive*s.
|
||||||
|
* A documentation directive allows the author to add metadata and processing
|
||||||
|
* instructions.
|
||||||
|
* @org jlp.jdb-labs.com/ast/Directive
|
||||||
|
*/
|
||||||
public class Directive extends ASTNode {
|
public class Directive extends ASTNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are currently five types of directive currently available:
|
||||||
|
*
|
||||||
|
* API
|
||||||
|
* : The *API* directive allows the author to seperate the parts of the
|
||||||
|
* documentation that should be included in javadoc-style API
|
||||||
|
* documentation, as opposed to full literate code-style documentation.
|
||||||
|
*
|
||||||
|
* Author
|
||||||
|
* : The *Author* directive is used to specify the author of a set of
|
||||||
|
* documentation. It can be used to denote the author of an entire file
|
||||||
|
* when used in the first documentation block or just the author of a
|
||||||
|
* specific method when used in the documentation block before that
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* Copyright
|
||||||
|
* : Similar to *Author*, this directive allows you to mark the copyright
|
||||||
|
* information for a set of documentation.
|
||||||
|
*
|
||||||
|
* Example
|
||||||
|
* : Used to mark an example in the documentation. The full doc block
|
||||||
|
* following the directive will be included inline as an example,
|
||||||
|
* typically typeset in a monospace font.
|
||||||
|
*
|
||||||
|
* Org
|
||||||
|
* : Used to create a link anchor in the documentation. The `jlp` protocol
|
||||||
|
* in a URL allows the author to link back to a link anchor. Refer to
|
||||||
|
* the [LinkAnchor](jlp://jlp.jdb-labs.com/LinkAnchor) documentation for
|
||||||
|
* more information about link anchors.
|
||||||
|
*/
|
||||||
public static enum DirectiveType {
|
public static enum DirectiveType {
|
||||||
Api, Author, Copyright, Example, Org;
|
Api, Author, Copyright, Example, Org;
|
||||||
|
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp.ast
|
package com.jdblabs.jlp.ast
|
||||||
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.List
|
import java.util.List
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api ASTNode for *DocBlock*s.
|
||||||
|
* @org jlp.jdb-labs.com/ast/DocBlock
|
||||||
|
*/
|
||||||
public class DocBlock extends ASTNode {
|
public class DocBlock extends ASTNode {
|
||||||
|
|
||||||
public List<Directive> directives = new ArrayList<Directive>()
|
public List<Directive> directives = new ArrayList<Directive>()
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @author Jonathan Bernard (jdb@jdb-labs.com)
|
||||||
|
* @copyright JDB Labs 2010-2011
|
||||||
|
*/
|
||||||
package com.jdblabs.jlp.ast
|
package com.jdblabs.jlp.ast
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api ASTNode for *DocText*s.
|
||||||
|
* @org jlp.jdb-labs.com/ast/DocText
|
||||||
|
*/
|
||||||
public class DocText extends ASTNode {
|
public class DocText extends ASTNode {
|
||||||
|
|
||||||
public String value
|
public String value
|
||||||
|
@ -6,11 +6,24 @@ package com.jdblabs.jlp.ast
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The top-level AST element. This represents a source file.
|
* 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 {
|
public class SourceFile {
|
||||||
|
|
||||||
|
/** The source file gets split into two parts during the initial parsing by
|
||||||
|
* the PEG grammer: a list of documentation blocks and a list of code
|
||||||
|
* blocks. Both are stored in the `Blocks` but the code block may get
|
||||||
|
* processed again if there is a language-specific parser involved for code
|
||||||
|
* awareness. In this case, we will keep a copy of the result of that
|
||||||
|
* parsing here as well. */
|
||||||
|
|
||||||
|
/// A list of the blocks in this `SourceFile`.
|
||||||
public List<ASTNode> blocks = []
|
public List<ASTNode> blocks = []
|
||||||
|
|
||||||
|
/// The result from parsing the code in this source file. Currently there
|
||||||
|
/// are no language-specific parsers and this is always `null`.
|
||||||
public def codeAST
|
public def codeAST
|
||||||
|
|
||||||
|
/// The id for this source file, currently set to the path name for the
|
||||||
|
/// input file relative to the input root.
|
||||||
public String id
|
public String id
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user