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.
This commit is contained in:
parent
051cd54dcd
commit
a86b55726f
@ -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
|
|
||||||
========= ===================
|
|
@ -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
|
any restrictions on what comes before the start delimiter or after the end
|
||||||
delimiter.
|
delimiter.
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
========= ==========
|
========= ===================
|
||||||
Created: 2011-09-07
|
Created : 2011-09-07
|
||||||
Resolved: YYYY-MM-DD
|
Resolved: 2011-12-25T23:26:07
|
||||||
========= ==========
|
========= ===================
|
||||||
|
18
doc/issues/0010fs5.rst
Normal file
18
doc/issues/0010fs5.rst
Normal file
@ -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 `<a id="link-name"/>` 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
|
||||||
|
========= ===================
|
@ -1,7 +1,7 @@
|
|||||||
#Wed, 04 Jan 2012 18:05:01 -0600
|
#Fri, 06 Jan 2012 12:20:57 -0600
|
||||||
name=jlp
|
name=jlp
|
||||||
version=1.4
|
version=1.5
|
||||||
build.number=11
|
build.number=6
|
||||||
lib.local=true
|
lib.local=true
|
||||||
release.dir=release
|
release.dir=release
|
||||||
main.class=com.jdblabs.jlp.JLPMain
|
main.class=com.jdblabs.jlp.JLPMain
|
||||||
|
@ -15,6 +15,12 @@ h1, h2, h3, h4, h5, h6 { margin: 0 0 15px 0; }
|
|||||||
|
|
||||||
h1 { margin-top: 40px; }
|
h1 { margin-top: 40px; }
|
||||||
|
|
||||||
|
hr {
|
||||||
|
background-color: black;
|
||||||
|
color: black;
|
||||||
|
border: none;
|
||||||
|
height: 1px; }
|
||||||
|
|
||||||
dt { font-weight: bold; }
|
dt { font-weight: bold; }
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
@ -36,17 +42,17 @@ td.docs, th.docs {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
text-align: left; }
|
text-align: left; }
|
||||||
|
|
||||||
.docs pre {
|
.docs tt, .docs code, .docs pre {
|
||||||
background: #f8f8ff;
|
|
||||||
border: 1px solid #dedede;
|
|
||||||
margin: 15px 0 15px;
|
|
||||||
padding-left: 15px; }
|
|
||||||
|
|
||||||
.docs tt, .docs code {
|
|
||||||
background: #f8f8ff;
|
background: #f8f8ff;
|
||||||
border: 1px solid #dedede;
|
border: 1px solid #dedede;
|
||||||
padding: 0 0.2em; }
|
padding: 0 0.2em; }
|
||||||
|
|
||||||
|
.docs pre {
|
||||||
|
margin: 15px 0 15px;
|
||||||
|
padding-left: 15px; }
|
||||||
|
|
||||||
|
.docs pre > code { border: none; }
|
||||||
|
|
||||||
.docs table {
|
.docs table {
|
||||||
border: thin solid #dedede;
|
border: thin solid #dedede;
|
||||||
margin-left: 60px; }
|
margin-left: 60px; }
|
||||||
|
@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory
|
|||||||
*/
|
*/
|
||||||
public class JLPMain {
|
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)
|
private static Logger log = LoggerFactory.getLogger(JLPMain.class)
|
||||||
|
|
||||||
|
@ -20,12 +20,12 @@ import com.jdblabs.jlp.ast.ASTNode
|
|||||||
*/
|
*/
|
||||||
public class LinkAnchor {
|
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.
|
/// The anchor id. This comes from the text after the directive.
|
||||||
public String id
|
public String id
|
||||||
|
|
||||||
public LinkType type
|
public LinkType type = LinkType.BlockLink
|
||||||
public ASTNode source
|
public ASTNode source
|
||||||
public String sourceDocId
|
public String sourceDocId
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package com.jdblabs.jlp
|
package com.jdblabs.jlp
|
||||||
|
|
||||||
import com.jdblabs.jlp.ast.*
|
import com.jdblabs.jlp.ast.*
|
||||||
|
import com.jdblabs.jlp.LinkAnchor.LinkType
|
||||||
import com.jdblabs.jlp.ast.Directive.DirectiveType
|
import com.jdblabs.jlp.ast.Directive.DirectiveType
|
||||||
|
|
||||||
import org.pegdown.Extensions
|
import org.pegdown.Extensions
|
||||||
@ -38,6 +39,56 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
/** ### Parse phase implementation. ### */
|
/** ### 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
|
/** Implement the parse phase for [`Directive`] nodes. We are interested
|
||||||
* specifically in saving the link anchor information from *org*
|
* specifically in saving the link anchor information from *org*
|
||||||
* directives.
|
* directives.
|
||||||
@ -139,10 +190,12 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
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` (already found in the
|
/// Look for an `@org` directive in the `Block` that is marked as a
|
||||||
/// parse phase)..
|
/// block link (we may have many `orgs` in a block that are not block
|
||||||
Directive orgDir = block.docBlock.directives.find {
|
/// links).
|
||||||
it.type == DirectiveType.Org }
|
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`
|
/// Create the `tr` that will hold the `Block`. If we found an `@org`
|
||||||
/// directive we will add the id here.
|
/// 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.
|
/// Later we will need a string builder to hold our result.
|
||||||
StringBuilder sb
|
StringBuilder sb
|
||||||
|
|
||||||
/** Add all the directives. We are also assigning priorities here that
|
/** We want to treat the whole block as one markdown chunk so we will
|
||||||
* we will use along with the line numbers to sort the elements. Our
|
* concatenate the directives and texts and send the whole block at
|
||||||
* goal is to preserve the order of doc blocks and doc-block-like
|
* once to the markdown processor.
|
||||||
* elements (examples, api documentation, etc.) while pushing orgs
|
*/
|
||||||
* and potentially other directives to the top. We used to re-order
|
emitQueue = docBlock.directives + docBlock.docTexts
|
||||||
* authorship and copyright tags but stopped: in literate-style docs
|
emitQueue.sort { it.lineNumber }
|
||||||
* 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)
|
|
||||||
|
|
||||||
/** 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()
|
sb = new StringBuilder()
|
||||||
emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) }
|
emitQueue.each { queueItem -> sb.append(emit(queueItem)) }
|
||||||
|
|
||||||
return processMarkdown(sb.toString())
|
return processMarkdown(sb.toString())
|
||||||
}
|
}
|
||||||
@ -244,7 +270,6 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
processMarkdown(directive.value) + "</div>\n"
|
processMarkdown(directive.value) + "</div>\n"
|
||||||
|
|
||||||
/// `@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"
|
||||||
|
|
||||||
@ -258,9 +283,15 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
|
|||||||
// TODO:
|
// TODO:
|
||||||
// case DirectiveType.Include:
|
// case DirectiveType.Include:
|
||||||
|
|
||||||
/// An `@org` directive is ignored here. We already emitted the id
|
/// An `@org` directive may be emitted if the [`LinkAnchor`] is an
|
||||||
/// when we started the block.
|
/// `InlineLink` type.
|
||||||
case DirectiveType.Org: return "" } }
|
///
|
||||||
|
/// [`LinkAnchor`]: jlp://jlp.jdb-labs.com/LinkAnchor
|
||||||
|
case DirectiveType.Org:
|
||||||
|
def link = processor.linkAnchors[directive.value]
|
||||||
|
if (link.type == LinkType.InlineLink) {
|
||||||
|
return "<a id='${directive.value}'></a>\n" }
|
||||||
|
else { return "" }}}
|
||||||
|
|
||||||
/** This is a helper method to process a block of text as Markdown. We need
|
/** 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
|
* to do some additional processing to deal with `jlp://` org links that
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.jdblabs.jlp
|
package com.jdblabs.jlp
|
||||||
|
|
||||||
|
import com.jdblabs.jlp.LinkAnchor.LinkType
|
||||||
import com.jdbernard.util.JarUtils
|
import com.jdbernard.util.JarUtils
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import org.parboiled.BaseParser
|
import org.parboiled.BaseParser
|
||||||
@ -213,11 +214,15 @@ public class Processor {
|
|||||||
|
|
||||||
if (!linkAnchor) {
|
if (!linkAnchor) {
|
||||||
// We do not have any reference to this id.
|
// 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})" }
|
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.
|
/// This link points to a location in this document.
|
||||||
else if (targetDoc.sourceDocId == linkAnchor.sourceDocId) {
|
if (targetDoc.sourceDocId == linkAnchor.sourceDocId) {
|
||||||
return "#${linkId}" }
|
return "#${linkId}" }
|
||||||
|
|
||||||
/// The link should point to a different document.
|
/// The link should point to a different document.
|
||||||
|
Loading…
Reference in New Issue
Block a user