Skip to content

Commit

Permalink
Add support for emphasis and code tags
Browse files Browse the repository at this point in the history
  • Loading branch information
ppkarwasz committed Feb 8, 2024
1 parent 6e01fa9 commit d34a4b0
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,25 @@
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.ast.Table;

class AbstractAsciidocTreeVisitor extends SimpleDocTreeVisitor<Void, AsciidocData> {
abstract class AbstractAsciidocTreeVisitor extends SimpleDocTreeVisitor<Void, AsciidocData> {

private static final String SOURCE_STYLE = "source";
// These are not supported by AsciiDoctor and are only used internally
private static final String CODE_STYLE = "code";
private static final String CODE_DELIM = "`";
private static final String EMPHASIS_STYLE = "em";
private static final String EMPHASIS_DELIM = "_";
private static final String STRONG_EMPHASIS_STYLE = "strong";
private static final String STRONG_EMPHASIS_DELIM = "*";

private static void appendSentences(final String text, final AsciidocData data) {
final String body = StringUtils.normalizeSpace(text);
final String[] sentences = body.split("(?<=\\w{2}[.!?])", -1);
final String[] sentences = text.split("(?<=\\w{2}[.!?])", -1);
// Full sentences
for (int i = 0; i < sentences.length - 1; i++) {
data.appendWords(sentences[i].strip());
data.newLine();
data.appendAdjustingSpace(sentences[i]).newLine();
}
// Partial sentence
data.appendWords(sentences[sentences.length - 1].strip());
data.appendAdjustingSpace(sentences[sentences.length - 1]);
}

@Override
Expand Down Expand Up @@ -112,9 +119,19 @@ public Void visitStartElement(final StartElementTree node, final AsciidocData da
break;
case "pre":
data.newParagraph();
final Block currentParagraph = data.getCurrentParagraph();
currentParagraph.setContext(BlockImpl.LISTING_CONTEXT);
currentParagraph.setStyle(BlockImpl.SOURCE_STYLE);
data.getCurrentParagraph().setContext(BlockImpl.LISTING_CONTEXT);
data.getCurrentParagraph().setStyle(SOURCE_STYLE);
break;
case "code":
data.newTextSpan(CODE_STYLE);
break;
case "em":
case "i":
data.newTextSpan(EMPHASIS_STYLE);
break;
case "strong":
case "b":
data.newTextSpan(STRONG_EMPHASIS_STYLE);
break;
default:
}
Expand Down Expand Up @@ -187,6 +204,17 @@ public Void visitEndElement(final EndElementTree node, final AsciidocData data)
table.getBody().add(row);
}
break;
case "code":
appendSpan(data, CODE_DELIM);
break;
case "em":
case "i":
appendSpan(data, EMPHASIS_DELIM);
break;
case "strong":
case "b":
appendSpan(data, STRONG_EMPHASIS_DELIM);
break;
default:
}
return super.visitEndElement(node, data);
Expand All @@ -210,16 +238,27 @@ public Void visitLink(final LinkTree node, final AsciidocData data) {
@Override
public Void visitLiteral(final LiteralTree node, final AsciidocData data) {
if (node.getKind() == DocTree.Kind.CODE) {
if (!data.getCurrentLine().isEmpty()) {
data.append(" ");
}
data.append("`").append(node.getBody().getBody()).append("`");
data.newTextSpan(CODE_STYLE);
node.getBody().accept(this, data);
appendSpan(data, "`");
} else {
node.getBody().accept(this, data);
}
return super.visitLiteral(node, data);
}

private void appendSpan(final AsciidocData data, final String delimiter) {
final String body = data.popTextSpan();
data.append(delimiter);
final boolean needsEscaping = body.contains(delimiter);
if (needsEscaping) {
data.append("++").append(body).append("++");
} else {
data.append(body);
}
data.append(delimiter);
}

@Override
public Void visitText(final TextTree node, final AsciidocData data) {
final Block currentParagraph = data.getCurrentParagraph();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
*/
package org.apache.logging.log4j.docgen.processor;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.EmptyStackException;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.apache.logging.log4j.docgen.processor.internal.BlockImpl;
import org.apache.logging.log4j.docgen.processor.internal.DocumentImpl;
import org.apache.logging.log4j.docgen.processor.internal.SectionImpl;
Expand All @@ -26,55 +31,90 @@
import org.asciidoctor.ast.StructuralNode;

final class AsciidocData {
private static final Pattern WHITESPACE_SEQUENCE = Pattern.compile("\\s+");
private static final String SPACE = " ";
private static final char SPACE_CHAR = ' ';
private static final char CODE_CHAR = '`';
private static final String NEW_LINE = "\n";

private final Document document;
private int currentSectionLevel;
private StructuralNode currentNode;
// not attached to the current node
private Block currentParagraph;
private final StringBuilder currentLine;
// A stack of nested text blocks. Each can have a different style.
private final Deque<Block> paragraphs = new ArrayDeque<>();
private final Deque<StringBuilder> lines = new ArrayDeque<>();

public AsciidocData() {
document = new DocumentImpl();
currentSectionLevel = 1;
currentNode = document;
currentParagraph = new BlockImpl(currentNode);
currentLine = new StringBuilder();
paragraphs.push(new BlockImpl(currentNode));
lines.push(new StringBuilder());
}

public void newLine() {
// Remove trailing space
final String line = currentLine.toString().stripTrailing();
final String line = getCurrentLine().toString().stripTrailing();
// Ignore leading empty lines
if (!currentParagraph.getLines().isEmpty() || !line.isEmpty()) {
currentParagraph.getLines().add(line);
if (!getCurrentParagraph().getLines().isEmpty() || !line.isEmpty()) {
getCurrentParagraph().getLines().add(line);
}
currentLine.setLength(0);
getCurrentLine().setLength(0);
}

public AsciidocData append(final String text) {
final String[] lines = text.split("\r?\n", -1);
for (int i = 0; i < lines.length; i++) {
currentLine.append(lines[i]);
getCurrentLine().append(lines[i]);
if (i != lines.length - 1) {
newLine();
}
}
return this;
}

public void appendWords(final String words) {
if (words.isBlank()) {
return;
public AsciidocData appendAdjustingSpace(final CharSequence text) {
final String normalized = WHITESPACE_SEQUENCE.matcher(text).replaceAll(SPACE);
if (!normalized.isEmpty()) {
final StringBuilder currentLine = getCurrentLine();
// Last char of current line or space
final char lineLastChar = currentLine.isEmpty() ? SPACE_CHAR : currentLine.charAt(currentLine.length() - 1);
// First char of test
final char textFirstChar = normalized.charAt(0);
if (lineLastChar == SPACE_CHAR && textFirstChar == SPACE_CHAR) {
// Merge spaces
currentLine.append(normalized, 1, normalized.length());
} else if (lineLastChar == CODE_CHAR && Character.isAlphabetic(textFirstChar)) {
currentLine.append(SPACE_CHAR).append(normalized);
} else {
currentLine.append(normalized);
}
}
// Separate text from previous words
if (!currentLine.isEmpty() && Character.isAlphabetic(words.codePointAt(0))) {
currentLine.append(" ");
return this;
}

public void newTextSpan(final String style) {
paragraphs.push(new BlockImpl(paragraphs.peek()));
lines.push(new StringBuilder());
}

public String popTextSpan() {
// Flush the paragraph
final StringBuilder line = lines.peek();
if (line != null && !line.isEmpty()) {
newLine();
}
currentLine.append(words);
lines.pop();
return String.join(SPACE, paragraphs.pop().getLines());
}

public void newParagraph() {
newParagraph(currentNode);
}

private void newParagraph(final StructuralNode parent) {
newLine();
final Block currentParagraph = paragraphs.pop();
final java.util.List<String> lines = currentParagraph.getLines();
// Remove trailing empty lines
for (int i = lines.size() - 1; i >= 0; i--) {
Expand All @@ -84,20 +124,20 @@ public void newParagraph() {
}
if (!currentParagraph.getLines().isEmpty()) {
currentNode.append(currentParagraph);
currentParagraph = new BlockImpl(currentNode);
}
paragraphs.push(new BlockImpl(parent));
}

public StructuralNode getCurrentNode() {
return currentNode;
}

public Block getCurrentParagraph() {
return currentParagraph;
return paragraphs.peek();
}

public StringBuilder getCurrentLine() {
return currentLine;
return lines.peek();
}

public Document getDocument() {
Expand All @@ -121,28 +161,24 @@ public void setCurrentSectionLevel(final int sectionLevel) {
* @param supplier a function to create a new node that takes its parent node a parameter.
*/
public StructuralNode pushChildNode(final Function<? super StructuralNode, ? extends StructuralNode> supplier) {
// Flushes the current paragraph
newParagraph();

final StructuralNode child = supplier.apply(currentNode);
// Creates a new current paragraph
currentParagraph = new BlockImpl(child);

// Flushes and reparents the current paragraph
newParagraph(child);

currentNode.append(child);
return currentNode = child;
}

public void popNode() {
final StructuralNode currentNode = this.currentNode;
// Flushes the current paragraph
newParagraph();

final StructuralNode parent = (StructuralNode) currentNode.getParent();
if (parent == null) {
throw new EmptyStackException();
}
// Creates a new current paragraph
currentParagraph = new BlockImpl(parent);
// Flushes and creates a new current paragraph
newParagraph(parent);

this.currentNode = parent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ public class BlockImpl extends StructuralNodeImpl implements Block {

public static final String PARAGRAPH_CONTEXT = "paragraph";
public static final String LISTING_CONTEXT = "listing";
public static final String SOURCE_STYLE = "source";

private List<String> lines = new ArrayList<>();

Expand Down
7 changes: 7 additions & 0 deletions log4j-docgen/src/test/it/example/JavadocExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
* paragraph has two sentences.
* </p>
* <p>
* A sentence with <code>foo</code>, <code>foo`</code>, <code>foo</code>bar. Another sentence with {@code foo},
* {@code foo`}, {@code foo}bar.
* </p>
* <p>
* We can use <strong>strong</strong> <em>emphasis</em> too, or we can use <b>bold</b> and <i>italic</i>.
* </p>
* <p>
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum blandit dictum sem, ornare posuere lorem
* convallis sit amet. Sed dui augue, faucibus ut nisi id, mollis euismod nibh. Donec lobortis luctus viverra. In
* orci ante, pretium et fringilla at, sagittis nec justo. Cras finibus lorem vel volutpat interdum. Sed laoreet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ Example of JavaDoc to AsciiDoc conversion
We run the `javadoc` tool on this class to test conversion of JavaDoc comments to AsciiDoc.
This paragraph has two sentences.
A sentence with `foo`, `++foo`++`, `foo` bar.
Another sentence with `foo`, `++foo`++`, `foo` bar.
We can use *strong* _emphasis_ too, or we can use *bold* and _italic_.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Vestibulum blandit dictum sem, ornare posuere lorem convallis sit amet.
Sed dui augue, faucibus ut nisi id, mollis euismod nibh.
Expand Down

0 comments on commit d34a4b0

Please sign in to comment.