Skip to content

Commit e870c8c

Browse files
authored
Support "Captioned titles" in asciidoctor-parser-doxia-module (#938)
* Add support for Example (inline and blocks) * Add support for captioned titles in appendixes, tables, listing, figure, and examples. * Fix empty table generating <table> element * Refactor Section tests for readability Fixes #749
1 parent 912de34 commit e870c8c

26 files changed

+685
-214
lines changed

CHANGELOG.adoc

+7-3
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ Bug Fixes::
1717

1818
* Fix open IMG tags in parser-doxia-module (#930)
1919
* Fix naming in Asciidoctor Converter Doxia Module pom (#934)
20+
* Fix empty table generating <table> element (asciidoctor-parser-doxia-module) (#938)
2021

2122
Improvements::
2223

2324
* Added support for AsciidoctorJ v3.0.0 (#651)
2425
* Add compatibility with maven-site-plugin v3.20.0 and Doxia v2.0.0 (#933)
25-
* Add support for code blocks titles in asciidoctor-parser-doxia-module (#935)
26-
* Refactor AST traversal method in asciidoctor-parser-doxia-module (#944)
26+
* Add support for code blocks titles (asciidoctor-parser-doxia-module) (#935)
27+
* Refactor AST traversal method (asciidoctor-parser-doxia-module) (#944)
2728
* Empty titles in document or empty literals no longer generate <h1> or <pre> in asciidoctor-parser-doxia-module (#944)
28-
* Sections are now wrapped in <div> in asciidoctor-parser-doxia-module (#944)
29+
* Sections are now wrapped in <div> in (asciidoctor-parser-doxia-module) (#944)
30+
* Add support for inline and Example blocks (asciidoctor-parser-doxia-module) (#938)
31+
* Add support for captioned titles in appendixes, tables, listing, figure, and examples (asciidoctor-parser-doxia-module) (#938)
32+
2933

3034
Build / Infrastructure::
3135

asciidoctor-parser-doxia-module/src/it/maven-site-plugin/src/site/asciidoc/sample.adoc

+13
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,16 @@ Linux:::
9191
BSD:::
9292
. FreeBSD
9393
. NetBSD
94+
95+
=== Examples
96+
97+
.Optional title (1)
98+
====
99+
This is an example of an example block (1).
100+
====
101+
102+
.Optional title (2)
103+
[example]
104+
This is an example of an example block (2).
105+
*dadsas* https://dasd.com
106+

asciidoctor-parser-doxia-module/src/it/maven-site-plugin/validate.groovy

+19-8
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ new HtmlAsserter(htmlContent).with { asserter ->
5858
asserter.containsUnorderedList("Desktop", "Server")
5959
asserter.descriptionListTerm("BSD")
6060
asserter.containsOrderedList("FreeBSD", "NetBSD")
61+
62+
asserter.containsSectionTitle("Examples", 3)
63+
asserter.containsExampleDiv()
64+
asserter.containsExampleDiv()
6165
}
6266

6367
String strong(String text) {
@@ -224,8 +228,15 @@ class HtmlAsserter {
224228
}
225229
}
226230

231+
void containsExampleDiv() {
232+
final def key = "<div style=\"background: #fffef7;"
233+
def found = find(key)
234+
assertFound("Example <div>", key, found)
235+
}
236+
227237
void assertTableCaption(String htmlBlock, String caption) {
228-
def start = htmlBlock.indexOf("<caption>") + "<caption>".length()
238+
def start = htmlBlock.indexOf("<caption") + "<caption".length()
239+
start = htmlBlock.indexOf(">", start) + 1
229240
def end = htmlBlock.indexOf("</caption>")
230241
if (start < 0 || end < 0)
231242
fail("Caption not found ($start, $end)")
@@ -239,11 +250,11 @@ class HtmlAsserter {
239250

240251
void assertTableHeaders(String htmlBlock, List<String> headers) {
241252
def actualHeaders = Arrays.stream(htmlBlock.split("<"))
242-
.filter(line -> line.startsWith("th>"))
243-
.map(line -> {
244-
return line.substring("th>".length())
245-
})
246-
.collect(Collectors.toList())
253+
.filter(line -> line.startsWith("th>"))
254+
.map(line -> {
255+
return line.substring("th>".length())
256+
})
257+
.collect(Collectors.toList())
247258

248259
if (actualHeaders != headers)
249260
fail("Table headers not valid. Found: $actualHeaders, expected: $headers")
@@ -266,8 +277,8 @@ class HtmlAsserter {
266277
// Removes linebreaks to validate to avoid OS dependant issues.
267278
private String clean(String value) {
268279
return value.replaceAll("\r\n", "")
269-
.replaceAll("\n", "")
270-
.trim();
280+
.replaceAll("\n", "")
281+
.trim();
271282
}
272283
}
273284

asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/NodeSinker.java

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.asciidoctor.ast.StructuralNode;
88
import org.asciidoctor.maven.site.parser.processors.DescriptionListNodeProcessor;
99
import org.asciidoctor.maven.site.parser.processors.DocumentNodeProcessor;
10+
import org.asciidoctor.maven.site.parser.processors.ExampleNodeProcessor;
1011
import org.asciidoctor.maven.site.parser.processors.ImageNodeProcessor;
1112
import org.asciidoctor.maven.site.parser.processors.ListItemNodeProcessor;
1213
import org.asciidoctor.maven.site.parser.processors.ListingNodeProcessor;
@@ -35,6 +36,7 @@ public NodeSinker(Sink sink) {
3536
nodeProcessors = Arrays.asList(
3637
new DescriptionListNodeProcessor(sink, this),
3738
new DocumentNodeProcessor(sink, this),
39+
new ExampleNodeProcessor(sink, this),
3840
new ImageNodeProcessor(sink, this),
3941
new ListItemNodeProcessor(sink, this),
4042
new ListingNodeProcessor(sink, this),

asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/DocumentNodeProcessor.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public class DocumentNodeProcessor extends AbstractSinkNodeProcessor implements
1818
/**
1919
* Constructor.
2020
*
21-
* @param sink Doxia {@link Sink}
21+
* @param sink Doxia {@link Sink}
22+
* @param nodeSinker
2223
*/
2324
public DocumentNodeProcessor(Sink sink, NodeSinker nodeSinker) {
2425
super(sink, nodeSinker);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.asciidoctor.maven.site.parser.processors;
2+
3+
import java.util.List;
4+
5+
import org.apache.maven.doxia.sink.Sink;
6+
import org.asciidoctor.ast.StructuralNode;
7+
import org.asciidoctor.maven.site.parser.NodeProcessor;
8+
import org.asciidoctor.maven.site.parser.NodeSinker;
9+
10+
import static javax.swing.text.html.HTML.Attribute.STYLE;
11+
import static org.asciidoctor.maven.commons.StringUtils.isNotBlank;
12+
13+
/**
14+
* Inline images are processed as paragraphs.
15+
*
16+
* @author abelsromero
17+
* @since 3.1.0
18+
*/
19+
public class ExampleNodeProcessor extends AbstractSinkNodeProcessor implements NodeProcessor {
20+
21+
/**
22+
* Constructor.
23+
*
24+
* @param sink Doxia {@link Sink}
25+
* @param nodeSinker
26+
*/
27+
public ExampleNodeProcessor(Sink sink, NodeSinker nodeSinker) {
28+
super(sink, nodeSinker);
29+
}
30+
31+
@Override
32+
public boolean applies(StructuralNode node) {
33+
return "example".equals(node.getNodeName());
34+
}
35+
36+
@Override
37+
public void process(StructuralNode node) {
38+
// Add caption as a div (same as Asciidoctor):
39+
// - For consistency
40+
// - Using `figureCaption` requires wrapping the image in <figure> which adds indentation
41+
final Sink sink = getSink();
42+
43+
sink.division();
44+
final String title = TitleCaptionExtractor.getText(node);
45+
if (isNotBlank(title)) {
46+
sink.division(SinkAttributes.of(STYLE, Styles.CAPTION));
47+
sink.text(title);
48+
sink.division_();
49+
}
50+
51+
final List<StructuralNode> blocks = node.getBlocks();
52+
if (!blocks.isEmpty()) {
53+
divWrap(sink, node, () -> blocks.forEach(this::sink));
54+
} else {
55+
// For :content_model: simple (inline)
56+
// https://docs.asciidoctor.org/asciidoc/latest/blocks/example-blocks/#example-style-syntax
57+
final String content = (String) node.getContent();
58+
if (isNotBlank(content)) {
59+
divWrap(sink, node, () -> sink.rawText(content));
60+
}
61+
}
62+
63+
sink.division_();
64+
}
65+
66+
void divWrap(Sink sink, StructuralNode node, Runnable consumer) {
67+
sink.division(SinkAttributes.of(STYLE, Styles.EXAMPLE));
68+
consumer.run();
69+
sink.division_();
70+
}
71+
}

asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ImageNodeProcessor.java

+17-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package org.asciidoctor.maven.site.parser.processors;
22

3-
import javax.swing.text.html.HTML.Attribute;
43
import java.nio.file.FileSystems;
54

65
import org.apache.maven.doxia.sink.Sink;
7-
import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
86
import org.asciidoctor.ast.StructuralNode;
97
import org.asciidoctor.maven.site.parser.NodeProcessor;
108
import org.asciidoctor.maven.site.parser.NodeSinker;
119

10+
import static javax.swing.text.html.HTML.Attribute.ALT;
11+
import static javax.swing.text.html.HTML.Attribute.STYLE;
1212
import static org.asciidoctor.maven.commons.StringUtils.isBlank;
13+
import static org.asciidoctor.maven.commons.StringUtils.isNotBlank;
1314

1415
/**
1516
* Inline images are processed as paragraphs.
@@ -40,12 +41,21 @@ public void process(StructuralNode node) {
4041
final String alt = (String) node.getAttribute("alt");
4142

4243
final String imagesdir = (String) node.getAttribute("imagesdir");
43-
String imagePath = isBlank(imagesdir) ? target : formatPath(imagesdir, target);
44-
final SinkEventAttributeSet attributes = new SinkEventAttributeSet();
45-
if (!isBlank(alt))
46-
attributes.addAttribute(Attribute.ALT, alt);
44+
final String imagePath = isBlank(imagesdir) ? target : formatPath(imagesdir, target);
4745

48-
getSink().figureGraphics(imagePath, attributes);
46+
// Add caption as a div (same as Asciidoctor):
47+
// - For consistency
48+
// - Using `figureCaption` requires wrapping the image in <figure> which adds indentation
49+
final Sink sink = getSink();
50+
sink.division();
51+
sink.figureGraphics(imagePath, !isBlank(alt) ? SinkAttributes.of(ALT, alt) : null);
52+
final String title = TitleCaptionExtractor.getText(node);
53+
if (isNotBlank(title)) {
54+
sink.division(SinkAttributes.of(STYLE, Styles.CAPTION));
55+
sink.text(title);
56+
sink.division_();
57+
}
58+
sink.division_();
4959
}
5060

5161
private String formatPath(String imagesdir, String target) {

asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/ListingNodeProcessor.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import org.apache.maven.doxia.sink.Sink;
44
import org.asciidoctor.ast.StructuralNode;
5-
import org.asciidoctor.maven.commons.StringUtils;
65
import org.asciidoctor.maven.site.parser.NodeProcessor;
76
import org.asciidoctor.maven.site.parser.NodeSinker;
87

@@ -39,20 +38,15 @@ public boolean applies(StructuralNode node) {
3938
@Override
4039
public void process(StructuralNode node) {
4140
final StringBuilder contentBuilder = new StringBuilder();
42-
String language = (String) node.getAttribute("language");
43-
String style = node.getStyle();
41+
final String language = (String) node.getAttribute("language");
42+
final String style = node.getStyle();
4443

4544
boolean isSourceBlock = isSourceBlock(language, style);
4645

4746
if (isSourceBlock) {
4847
// source class triggers prettify auto-detection
4948
contentBuilder.append("<div class=\"source\">");
50-
51-
final String title = node.getTitle();
52-
if (StringUtils.isNotBlank(title)) {
53-
contentBuilder.append("<div style=\"color: #7a2518; margin-bottom: .25em;\" >" + title + "</div>");
54-
}
55-
49+
processTitle(node, contentBuilder);
5650
contentBuilder.append("<pre class=\"")
5751
.append(FLUIDO_SKIN_SOURCE_HIGHLIGHTER);
5852
if (isLinenumsEnabled(node))
@@ -77,6 +71,13 @@ public void process(StructuralNode node) {
7771
getSink().rawText(contentBuilder.toString());
7872
}
7973

74+
private static void processTitle(StructuralNode node, StringBuilder contentBuilder) {
75+
final String title = TitleCaptionExtractor.getText(node);
76+
if (isNotBlank(title)) {
77+
contentBuilder.append("<div style=\"" + Styles.CAPTION + "\" >" + title + "</div>");
78+
}
79+
}
80+
8081
private boolean isLinenumsEnabled(StructuralNode node) {
8182
// linenums attribute can be set with empty string value
8283
return LINENUMS_ATTRIBUTE.equals(node.getAttribute("linenums"))

asciidoctor-parser-doxia-module/src/main/java/org/asciidoctor/maven/site/parser/processors/SectionNodeProcessor.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import org.apache.maven.doxia.sink.Sink;
44
import org.asciidoctor.ast.Section;
55
import org.asciidoctor.ast.StructuralNode;
6+
import org.asciidoctor.jruby.ast.impl.SectionImpl;
7+
import org.asciidoctor.maven.commons.StringUtils;
68
import org.asciidoctor.maven.site.parser.NodeProcessor;
79
import org.asciidoctor.maven.site.parser.NodeSinker;
810
import org.slf4j.Logger;
@@ -77,7 +79,14 @@ private String formatTitle(String title, Section node) {
7779
final Long sectnumlevels = getSectnumlevels(node);
7880
final int level = node.getLevel();
7981
if (numbered && level <= sectnumlevels) {
80-
return String.format("%s %s", node.getSectnum(), title);
82+
// Use 'getString' instead of method to support pre-3.0.0 AsciidoctorJ
83+
final String caption = node.getCaption();
84+
final String sectnum = ((SectionImpl) node).getString("sectnum");
85+
if (StringUtils.isBlank(caption)) {
86+
return String.format("%s %s", sectnum, title);
87+
} else {
88+
return String.format("%s %s", caption.trim(), title);
89+
}
8190
}
8291
return title;
8392
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.asciidoctor.maven.site.parser.processors;
2+
3+
import javax.swing.text.html.HTML.Attribute;
4+
5+
import org.apache.maven.doxia.sink.SinkEventAttributes;
6+
import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
7+
8+
class SinkAttributes {
9+
10+
static SinkEventAttributes of(Attribute name, String value) {
11+
final var attributes = new SinkEventAttributeSet();
12+
attributes.addAttribute(name, value);
13+
return attributes;
14+
}
15+
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.asciidoctor.maven.site.parser.processors;
2+
3+
import java.util.stream.Collectors;
4+
import java.util.stream.Stream;
5+
6+
class Styles {
7+
8+
public static final String CAPTION = Stream.of(
9+
"color: #7a2518",
10+
"margin-bottom: .25em"
11+
).collect(Collectors.joining("; "));
12+
13+
14+
public static final String EXAMPLE = Stream.of(
15+
"background: #fffef7",
16+
"border-color: #e0e0dc",
17+
"border: 1px solid #e6e6e6",
18+
"box-shadow: 0 1px 4px #e0e0dc",
19+
"margin-bottom: 1.25em",
20+
"padding: 1.25em"
21+
).collect(Collectors.joining("; "));
22+
}

0 commit comments

Comments
 (0)