From a86b55726f6ba1898bcae1a5607b4b44053f45f0 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Fri, 6 Jan 2012 14:41:01 -0600 Subject: [PATCH] Added file and inline link anchors. * Auto-link for documents. If there is an `org` directive in the first doc block of the file then it is used as the file link definition. If there is no such `org` directive then on is created automatically. This resolves issue #0008. There is a new LinkAnchor type for these links: `LinkType.FileLink` * Multiple `org` directives per DocBlock are now allowed. There is a new LinkAnchor link type for these link: `LinkType.InlineLink`. * Refactored `LinkType.OrgLink` to be `LinkType.BlockLink`. * Tweaked CSS * Refactored `LiterateMarkdownGenerator.emit(DocBlock)` for simplicity. --- doc/issues/0004tn7.rst | 16 --- doc/issues/0004ts7.rst | 9 +- doc/issues/{0008fn5.rst => 0008fv5.rst} | 0 doc/issues/0010fs5.rst | 18 +++ project.properties | 6 +- resources/main/css/jlp.css | 20 +-- src/main/com/jdblabs/jlp/JLPMain.groovy | 2 +- src/main/com/jdblabs/jlp/LinkAnchor.groovy | 4 +- .../jlp/LiterateMarkdownGenerator.groovy | 115 +++++++++++------- src/main/com/jdblabs/jlp/Processor.groovy | 9 +- 10 files changed, 122 insertions(+), 77 deletions(-) delete mode 100644 doc/issues/0004tn7.rst rename doc/issues/{0008fn5.rst => 0008fv5.rst} (100%) create mode 100644 doc/issues/0010fs5.rst diff --git a/doc/issues/0004tn7.rst b/doc/issues/0004tn7.rst deleted file mode 100644 index 0c31c02..0000000 --- a/doc/issues/0004tn7.rst +++ /dev/null @@ -1,16 +0,0 @@ -Fix delimited doc block behavior. -================================= - -Delimited doc blocks require that the start token be the first non-space token -on the line it is on and that the end token be on it's own line. This is not in -line with the general nature of delimited comment blocks, which do not place -any restrictions on what comes before the start delimiter or after the end -delimiter. - - ----- - -========= =================== -Created : 2011-09-07 -Resolved: 2011-12-25T23:26:07 -========= =================== diff --git a/doc/issues/0004ts7.rst b/doc/issues/0004ts7.rst index b227c37..0c31c02 100644 --- a/doc/issues/0004ts7.rst +++ b/doc/issues/0004ts7.rst @@ -7,9 +7,10 @@ line with the general nature of delimited comment blocks, which do not place any restrictions on what comes before the start delimiter or after the end delimiter. + ---- -========= ========== -Created: 2011-09-07 -Resolved: YYYY-MM-DD -========= ========== +========= =================== +Created : 2011-09-07 +Resolved: 2011-12-25T23:26:07 +========= =================== diff --git a/doc/issues/0008fn5.rst b/doc/issues/0008fv5.rst similarity index 100% rename from doc/issues/0008fn5.rst rename to doc/issues/0008fv5.rst diff --git a/doc/issues/0010fs5.rst b/doc/issues/0010fs5.rst new file mode 100644 index 0000000..9fd4a1a --- /dev/null +++ b/doc/issues/0010fs5.rst @@ -0,0 +1,18 @@ +Modify `org` behavior to include simple anchors. +================================================ + +Currently JLP supports at most one `org` directive per block which identifies the block. +It would be useful to support multiple `org` directives within a block, particularly +when there is a large block that may have many interesting internal targets. Maybe the +`org` directive should be handled differently when it is used multiple times within a +block. This would be discovered in the generator parse phase and we could change the +LinkAnchor type at that time. During the parse phase we emit the new type of anchors +as `` into the document. We would also change our search for block +ids to inly look for the single-occurance type of `orgs`. + +---- + +========= =================== +Created : 2012-01-05T11:40:35 +Resolved: 2012-01-06T14:32:46 +========= =================== diff --git a/project.properties b/project.properties index 329670f..763b9d8 100644 --- a/project.properties +++ b/project.properties @@ -1,7 +1,7 @@ -#Wed, 04 Jan 2012 18:05:01 -0600 +#Fri, 06 Jan 2012 12:20:57 -0600 name=jlp -version=1.4 -build.number=11 +version=1.5 +build.number=6 lib.local=true release.dir=release main.class=com.jdblabs.jlp.JLPMain diff --git a/resources/main/css/jlp.css b/resources/main/css/jlp.css index 354451b..3ecb1f2 100644 --- a/resources/main/css/jlp.css +++ b/resources/main/css/jlp.css @@ -15,6 +15,12 @@ h1, h2, h3, h4, h5, h6 { margin: 0 0 15px 0; } h1 { margin-top: 40px; } +hr { + background-color: black; + color: black; + border: none; + height: 1px; } + dt { font-weight: bold; } ul { @@ -36,17 +42,17 @@ td.docs, th.docs { 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 { +.docs tt, .docs code, .docs pre { background: #f8f8ff; border: 1px solid #dedede; padding: 0 0.2em; } +.docs pre { + margin: 15px 0 15px; + padding-left: 15px; } + +.docs pre > code { border: none; } + .docs table { border: thin solid #dedede; margin-left: 60px; } diff --git a/src/main/com/jdblabs/jlp/JLPMain.groovy b/src/main/com/jdblabs/jlp/JLPMain.groovy index b0a8f0a..285b496 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.4" + public static final String VERSION = "1.5" private static Logger log = LoggerFactory.getLogger(JLPMain.class) diff --git a/src/main/com/jdblabs/jlp/LinkAnchor.groovy b/src/main/com/jdblabs/jlp/LinkAnchor.groovy index 1fe59b0..bf111ae 100644 --- a/src/main/com/jdblabs/jlp/LinkAnchor.groovy +++ b/src/main/com/jdblabs/jlp/LinkAnchor.groovy @@ -20,12 +20,12 @@ import com.jdblabs.jlp.ast.ASTNode */ public class LinkAnchor { - public enum LinkType { OrgLink, FileLink } + public enum LinkType { InlineLink, BlockLink, FileLink } /// The anchor id. This comes from the text after the directive. public String id - public LinkType type + public LinkType type = LinkType.BlockLink 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 e7ad382..e629884 100644 --- a/src/main/com/jdblabs/jlp/LiterateMarkdownGenerator.groovy +++ b/src/main/com/jdblabs/jlp/LiterateMarkdownGenerator.groovy @@ -5,6 +5,7 @@ package com.jdblabs.jlp import com.jdblabs.jlp.ast.* +import com.jdblabs.jlp.LinkAnchor.LinkType import com.jdblabs.jlp.ast.Directive.DirectiveType import org.pegdown.Extensions @@ -38,6 +39,56 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { /** ### Parse phase implementation. ### */ // =================================== + /** Override the parse phase for [`SourceFile`] nodes. We are interested in + * detecting an `org` directive in the first DocBlock, or automatically + * creating one if it is not defined. The first `org` directive (found or + * created) will create a FileLink type LinkAnchor. + */ + + protected void parse(SourceFile sourceFile) { + /// First we look for an `org` directive in the first block. + def firstOrg = sourceFile.blocks[0].docBlock.directives.find { + it.type == DirectiveType.Org } + + /// And we create one if there are none. + if (!firstOrg) { + def docBlock = sourceFile.blocks[0].docBlock + firstOrg = new Directive(processor.currentDocId, 'org', 0, docBlock) + docBlock.directives << firstOrg } + + /// Now parse the file as usual. + super.parse(sourceFile) + + /// And mark the first `org` as a FileLink + processor.linkAnchors[firstOrg.value].type = LinkType.FileLink} + + /** Override the parse phase for [`DocBlock`] nodes. We are interested in + * detecting a block that has multilple `org` directives. When there are + * multiple org directives in one block we change the LinkAnchor type from + * block-level links to specific anchors in the text. This allows the + * author to create a link to exact points within the document. + * + * [`DocBlock`]: jlp://jlp.jdb-labs.com/ast/DocBlock + */ + protected void parse(DocBlock docBlock) { + /// First parse the block as usual. + super.parse(docBlock) + + /// Look for multiple `org` directives. + def orgDirectives = docBlock.directives.findAll {it.type == DirectiveType.Org } + + /// If we have multiple `org` directives in one [`DocBlock`] then we + /// want to change the corresponding [`LinkAnchors`] to type + /// `AnchorType`. + /// + /// [`DocBlock`]: jlp://jlp.jdb-labs.com/ast/DocBlock + /// [`LinkAnchors`]: jlp://jlp.jdb-labs.com/LinkAnchor + if (orgDirectives.size() > 1) { + orgDirectives.each { directive -> + /// Get the LinkAnchor for this `org` link. + def linkAnchor = processor.linkAnchors[directive.value] + linkAnchor.type = LinkType.InlineLink }}} + /** Implement the parse phase for [`Directive`] nodes. We are interested * specifically in saving the link anchor information from *org* * directives. @@ -139,10 +190,12 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { protected String emit(Block block) { StringBuilder sb = new StringBuilder() - /// Look for an `@org` directive in the `Block` (already found in the - /// parse phase).. - Directive orgDir = block.docBlock.directives.find { - it.type == DirectiveType.Org } + /// Look for an `@org` directive in the `Block` that is marked as a + /// block link (we may have many `orgs` in a block that are not block + /// links). + Directive orgDir = block.docBlock.directives.find { directive -> + directive.type == DirectiveType.Org && + processor.linkAnchors[directive.value]?.type == LinkType.BlockLink } /// Create the `tr` that will hold the `Block`. If we found an `@org` /// directive we will add the id here. @@ -172,42 +225,15 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { /// Later we will need a string builder to hold our result. StringBuilder sb - /** 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 - * and potentially other directives to the top. We used to re-order - * authorship and copyright tags but stopped: in literate-style docs - * the author should have control over their placement and in api-style - * docs we are chopping the whole block up anyways (and this is not - * that code). Given that the only item being re-ordered is *orgs*, - * which do not print anyways, it may be better to cut this out and - * just emit the blocks in their original order, or sort by line number - * only. */ - emitQueue = docBlock.directives.collect { directive -> - def queueItem = [lineNumber: directive.lineNumber, value: directive] - switch(directive.type) { - case DirectiveType.Org: queueItem.priority = 0; break - default: queueItem.priority = 50; break } - - return queueItem } - - /// Add all the doc text blocks. - emitQueue.addAll(docBlock.docTexts.collect { docText -> - [lineNumber: docText.lineNumber, priority: 50, value: docText] }) - - - /// Sort the emit queue by priority, then line number. - emitQueue.sort( - {i1, i2 -> i1.priority != i2.priority ? - i1.priority - i2.priority : - i1.lineNumber - i2.lineNumber} as Comparator) + /** We want to treat the whole block as one markdown chunk so we will + * concatenate the directives and texts and send the whole block at + * once to the markdown processor. + */ + emitQueue = docBlock.directives + docBlock.docTexts + emitQueue.sort { it.lineNumber } - /** Finally, we want to treat the whole block as one markdown chunk so - * we will concatenate the values in the emit queue and then send the - * whole block at once to the markdown processor. */ sb = new StringBuilder() - emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) } + emitQueue.each { queueItem -> sb.append(emit(queueItem)) } return processMarkdown(sb.toString()) } @@ -244,7 +270,6 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { processMarkdown(directive.value) + "\n" /// `@author` directive is turned into a definition list. - case DirectiveType.Author: return "Author\n: ${directive.value}\n" @@ -258,9 +283,15 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { // TODO: // case DirectiveType.Include: - /// An `@org` directive is ignored here. We already emitted the id - /// when we started the block. - case DirectiveType.Org: return "" } } + /// An `@org` directive may be emitted if the [`LinkAnchor`] is an + /// `InlineLink` type. + /// + /// [`LinkAnchor`]: jlp://jlp.jdb-labs.com/LinkAnchor + case DirectiveType.Org: + def link = processor.linkAnchors[directive.value] + if (link.type == LinkType.InlineLink) { + return "\n" } + else { 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 diff --git a/src/main/com/jdblabs/jlp/Processor.groovy b/src/main/com/jdblabs/jlp/Processor.groovy index 86051d8..d47194c 100644 --- a/src/main/com/jdblabs/jlp/Processor.groovy +++ b/src/main/com/jdblabs/jlp/Processor.groovy @@ -4,6 +4,7 @@ */ package com.jdblabs.jlp +import com.jdblabs.jlp.LinkAnchor.LinkType import com.jdbernard.util.JarUtils import java.util.jar.JarInputStream import org.parboiled.BaseParser @@ -213,11 +214,15 @@ public class Processor { if (!linkAnchor) { // We do not have any reference to this id. - /* TODO: log error */ + log.warn("Unable to resolve a jlp link: {}.", link) return "broken_link(${linkId})" } + /// If this is a `FileLink` then we do not need the actual + /// linkId, just the file being linked to. + if (linkAnchor.type == LinkType.FileLink) { linkId = "" } + /// This link points to a location in this document. - else if (targetDoc.sourceDocId == linkAnchor.sourceDocId) { + if (targetDoc.sourceDocId == linkAnchor.sourceDocId) { return "#${linkId}" } /// The link should point to a different document.