Skip to content

Commit 65fe78b

Browse files
authored
Merge pull request #3709 from lewisbirks/fix-xref-links
Fix anchor links being overwritten
2 parents 5e345b5 + b48e72b commit 65fe78b

File tree

4 files changed

+137
-147
lines changed

4 files changed

+137
-147
lines changed

docs/asciidoc/packaging/packaging.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ application.
99

1010
[TIP]
1111
====
12-
The https://jooby.io/#getting-started[jooby-cli] takes care of configures everything for single jar
12+
The link:/#getting-started[jooby-cli] takes care of configures everything for single jar
1313
distribution. Next example shows how to do it in case you created your application manually.
1414
====
1515

docs/src/main/java/io/jooby/adoc/DocGenerator.java

Lines changed: 10 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import java.io.File;
1212
import java.io.IOException;
13-
import java.nio.charset.StandardCharsets;
1413
import java.nio.file.Files;
1514
import java.nio.file.Path;
1615
import java.nio.file.Paths;
@@ -19,11 +18,8 @@
1918
import java.time.format.DateTimeFormatter;
2019
import java.util.Arrays;
2120
import java.util.Collection;
22-
import java.util.LinkedHashSet;
2321
import java.util.List;
2422
import java.util.UUID;
25-
import java.util.concurrent.atomic.AtomicInteger;
26-
import java.util.stream.Collectors;
2723
import java.util.stream.Stream;
2824

2925
import org.apache.commons.io.FileUtils;
@@ -35,9 +31,7 @@
3531
import org.asciidoctor.SafeMode;
3632
import org.jsoup.Jsoup;
3733
import org.jsoup.nodes.Document;
38-
import org.jsoup.nodes.Element;
3934

40-
import me.tongfei.progressbar.ProgressBar;
4135
import me.tongfei.progressbar.ProgressBarBuilder;
4236
import me.tongfei.progressbar.ProgressBarStyle;
4337

@@ -53,17 +47,17 @@ public static void generate(Path basedir, boolean publish, boolean v1, boolean d
5347

5448
Path asciidoc = basedir.resolve("asciidoc");
5549

56-
/**
57-
* Tree dir. The .adoc file became a directory
58-
* modules/hikari.adoc => modules/hikari/index.html
50+
/*
51+
Tree dir. The .adoc file became a directory
52+
modules/hikari.adoc => modules/hikari/index.html
5953
*/
6054
String[] treeDirs = {"modules", "packaging", "usage", "migration"};
6155

6256
int adocCount =
6357
Stream.of(treeDirs)
6458
.map(throwingFunction(dir -> countAdoc(asciidoc.resolve(dir))))
6559
.reduce(1, Integer::sum);
66-
int steps = 7 + (doAscii ? adocCount : 0);
60+
int steps = 6 + (doAscii ? adocCount : 0);
6761

6862
ProgressBarBuilder pbb =
6963
new ProgressBarBuilder()
@@ -79,11 +73,11 @@ public static void generate(Path basedir, boolean publish, boolean v1, boolean d
7973
}
8074
pb.step();
8175

82-
/** Wipe out directory: */
76+
/* Wipe out directory: */
8377
FileUtils.cleanDirectory(outdir.toFile());
8478
pb.step();
8579

86-
/** Copy /images and /js: */
80+
/* Copy /images and /js: */
8781
copyFile(
8882
outdir,
8983
// images
@@ -100,7 +94,6 @@ public static void generate(Path basedir, boolean publish, boolean v1, boolean d
10094
createOptions(asciidoc, outdir, version, null));
10195
pb.step();
10296

103-
AtomicInteger m = new AtomicInteger();
10497
Stream.of(treeDirs)
10598
.forEach(
10699
throwingConsumer(
@@ -113,21 +106,10 @@ public static void generate(Path basedir, boolean publish, boolean v1, boolean d
113106
module -> {
114107
processModule(asciidoctor, asciidoc, module, outdir, name, version);
115108
pb.step();
116-
m.incrementAndGet();
117109
});
118110
}));
119111
}
120112

121-
// post process
122-
Files.walk(outdir)
123-
.filter(it -> it.getFileName().toString().endsWith("index.html"))
124-
.forEach(
125-
throwingConsumer(
126-
it -> {
127-
Files.write(it, document(it).getBytes(StandardCharsets.UTF_8));
128-
}));
129-
pb.step();
130-
131113
// LICENSE
132114
Files.copy(
133115
basedir.getParent().resolve("LICENSE"),
@@ -149,7 +131,7 @@ public static void generate(Path basedir, boolean publish, boolean v1, boolean d
149131
Git git = new Git("jooby-project", "jooby.io", website);
150132
git.clone();
151133

152-
/** Clean: */
134+
/* Clean: */
153135
FileUtils.deleteDirectory(website.resolve("images").toFile());
154136
FileUtils.deleteDirectory(website.resolve("js").toFile());
155137
FileUtils.deleteQuietly(website.resolve("index.html").toFile());
@@ -239,11 +221,10 @@ private static void processModule(
239221
indexlike = indexlike.resolve("index.html");
240222
Files.createDirectories(indexlike.getParent());
241223
Files.move(output, indexlike);
242-
String content =
243-
new String(Files.readAllBytes(indexlike), StandardCharsets.UTF_8)
224+
String content = Files.readString(indexlike)
244225
.replace("js/", "../../js/")
245226
.replace("images/", "../../images/");
246-
Files.write(indexlike, content.getBytes(StandardCharsets.UTF_8));
227+
Files.writeString(indexlike, content);
247228
} catch (IOException x) {
248229
throw new IllegalStateException(x);
249230
}
@@ -321,122 +302,6 @@ private static String toJavaName(String tagName) {
321302
return name.toString();
322303
}
323304

324-
private static String document(Path index) {
325-
try {
326-
Document doc = Jsoup.parse(index.toFile(), "UTF-8");
327-
tocItems(doc);
328-
languageTab(doc);
329-
clipboard(doc);
330-
externalLink(doc);
331-
Document.OutputSettings settings = new Document.OutputSettings();
332-
settings.prettyPrint(false);
333-
settings.indentAmount(0);
334-
settings.outline(false);
335-
return doc.outputSettings(settings).toString();
336-
} catch (NullPointerException x) {
337-
throw new IllegalStateException("File: " + index, x);
338-
} catch (IOException x) {
339-
throw new IllegalStateException("File: " + index, x);
340-
}
341-
}
342-
343-
private static void externalLink(Document doc) {
344-
for (Element a : doc.select("a")) {
345-
String href = a.attr("href");
346-
if (href.startsWith("http://") || href.startsWith("https://")) {
347-
a.attr("target", "_blank");
348-
}
349-
}
350-
}
351-
352-
private static void languageTab(Document doc) {
353-
for (Element primary : doc.select(".listingblock.primary")) {
354-
Element secondary = primary.nextElementSibling();
355-
String secondaryTitle = secondary.selectFirst(".title").text().trim();
356-
Element primaryContent = primary.selectFirst(".content");
357-
Element secondaryContent = secondary.selectFirst(".content");
358-
secondary.remove();
359-
secondaryContent.remove();
360-
361-
Element title = primary.selectFirst(".title");
362-
363-
Element tabs = doc.createElement("div").attr("class", "switch");
364-
Element tab1 = tabs.appendElement("div");
365-
tab1.attr("class", "switch--item option-1 selected");
366-
if (secondaryTitle.equalsIgnoreCase("Kotlin")) {
367-
tab1.text("Java");
368-
} else {
369-
tab1.text(title.text());
370-
}
371-
372-
if (title.text().trim().equalsIgnoreCase(tab1.text().trim())) {
373-
title.remove();
374-
}
375-
376-
Element tab2 = tabs.appendElement("div");
377-
tab2.attr("class", "switch--item option-2");
378-
tab2.text(secondaryTitle);
379-
tabs.appendTo(primary);
380-
primaryContent.addClass("option-1");
381-
primaryContent.appendTo(primary);
382-
secondaryContent.appendTo(primary);
383-
secondaryContent.addClass("hidden").addClass("option-2");
384-
;
385-
}
386-
}
387-
388-
private static void tocItems(Document doc) {
389-
tocItems(doc, 2);
390-
tocItems(doc, 3);
391-
tocItems(doc, 4);
392-
}
393-
394-
private static void tocItems(Document doc, int level) {
395-
doc.select("h" + level)
396-
.forEach(
397-
h -> {
398-
if (!h.hasClass("discrete")) {
399-
String id = h.attr("id");
400-
LinkedHashSet<String> name = new LinkedHashSet<>();
401-
int parent = level - 1;
402-
Element p = h.parents().select("h" + parent).first();
403-
if (p != null && !p.hasClass("discrete")) {
404-
String parentId = p.attr("id");
405-
if (parentId != null && parentId.length() > 0) {
406-
name.add(parentId);
407-
}
408-
}
409-
name.add(id.replaceAll("([a-zA-Z0-9-]+)-\\d+$", "$1"));
410-
String newId = name.stream().collect(Collectors.joining("-"));
411-
if (!id.equals(newId)) {
412-
h.attr("id", newId);
413-
doc.select("a")
414-
.forEach(
415-
a -> {
416-
if (a.attr("href").equals("#" + id) && a.attr("class").length() > 0) {
417-
a.attr("href", "#" + newId);
418-
}
419-
});
420-
}
421-
}
422-
});
423-
}
424-
425-
private static void clipboard(Document doc) {
426-
for (Element code : doc.select("code.hljs")) {
427-
String id = "x" + Long.toHexString(UUID.randomUUID().getMostSignificantBits());
428-
code.attr("id", id);
429-
Element button = code.parent().appendElement("button");
430-
button.addClass("clipboard");
431-
button.attr("data-clipboard-target", "#" + id);
432-
Element img = button.appendElement("img");
433-
img.attr("src", "/images/clippy.svg");
434-
img.attr("class", "clippy");
435-
img.attr("width", "13");
436-
img.attr("alt", "Copy to clipboard");
437-
}
438-
}
439-
440305
public static Path basedir() {
441306
Path basedir = Paths.get(System.getProperty("user.dir"));
442307
if (!basedir.toString().endsWith("docs")) {
@@ -449,8 +314,7 @@ public static Path basedir() {
449314
public static String version() {
450315
try {
451316
Document doc = Jsoup.parse(basedir().getParent().resolve("pom.xml").toFile(), "utf-8");
452-
String version = doc.selectFirst("version").text().trim();
453-
return version;
317+
return doc.selectFirst("version").text().trim();
454318
} catch (IOException x) {
455319
throw new IllegalStateException(x);
456320
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package io.jooby.adoc;
2+
3+
import static java.util.function.Predicate.not;
4+
5+
import java.util.LinkedHashSet;
6+
import java.util.UUID;
7+
import org.asciidoctor.extension.Postprocessor;
8+
import org.jsoup.Jsoup;
9+
import org.jsoup.nodes.Document;
10+
import org.jsoup.nodes.Document.OutputSettings;
11+
import org.jsoup.nodes.Element;
12+
13+
public class DocPostprocessor extends Postprocessor {
14+
15+
@Override
16+
public String process(org.asciidoctor.ast.Document document, String output) {
17+
try {
18+
Document doc = Jsoup.parse(output, "UTF-8");
19+
20+
headerIds(doc);
21+
languageTab(doc);
22+
clipboard(doc);
23+
externalLink(doc);
24+
25+
OutputSettings settings = new OutputSettings();
26+
settings.prettyPrint(false);
27+
settings.indentAmount(0);
28+
settings.outline(false);
29+
return doc.outputSettings(settings).outerHtml();
30+
} catch (NullPointerException x) {
31+
throw new IllegalStateException("File: " + document.getDoctitle(), x);
32+
}
33+
}
34+
35+
private static void externalLink(Document doc) {
36+
doc.select("a[href^=http://], a[href^=https://]")
37+
.forEach(a -> a.attr("target", "_blank"));
38+
}
39+
40+
private static void languageTab(Document doc) {
41+
for (Element primary : doc.select(".listingblock.primary")) {
42+
Element secondary = primary.nextElementSibling();
43+
String secondaryTitle = secondary.selectFirst(".title").text().trim();
44+
Element primaryContent = primary.selectFirst(".content");
45+
Element secondaryContent = secondary.selectFirst(".content");
46+
secondary.remove();
47+
secondaryContent.remove();
48+
49+
Element title = primary.selectFirst(".title");
50+
51+
Element tabs = doc.createElement("div").attr("class", "switch");
52+
Element tab1 = tabs.appendElement("div");
53+
tab1.attr("class", "switch--item option-1 selected");
54+
if (secondaryTitle.equalsIgnoreCase("Kotlin")) {
55+
tab1.text("Java");
56+
} else {
57+
tab1.text(title.text());
58+
}
59+
60+
if (title.text().trim().equalsIgnoreCase(tab1.text().trim())) {
61+
title.remove();
62+
}
63+
64+
Element tab2 = tabs.appendElement("div");
65+
tab2.attr("class", "switch--item option-2");
66+
tab2.text(secondaryTitle);
67+
tabs.appendTo(primary);
68+
primaryContent.addClass("option-1");
69+
primaryContent.appendTo(primary);
70+
secondaryContent.appendTo(primary);
71+
secondaryContent.addClass("hidden").addClass("option-2");
72+
}
73+
}
74+
75+
private static void headerIds(Document doc) {
76+
headerIds(doc, 2);
77+
headerIds(doc, 3);
78+
headerIds(doc, 4);
79+
headerIds(doc, 5);
80+
}
81+
82+
private static void headerIds(Document doc, int level) {
83+
doc.select("h" + level).stream()
84+
.filter(not(DocPostprocessor::isDiscrete))
85+
.forEach(h -> {
86+
String id = h.attr("id");
87+
LinkedHashSet<String> name = new LinkedHashSet<>();
88+
int parent = level - 1;
89+
Element p = h.parents().select("h" + parent).first();
90+
if (p != null && !isDiscrete(p)) {
91+
String parentId = p.attr("id");
92+
if (!parentId.isEmpty()) {
93+
name.add(parentId);
94+
}
95+
}
96+
name.add(id.replaceAll("([a-zA-Z0-9-]+)-\\d+$", "$1"));
97+
String newId = String.join("-", name);
98+
if (!id.equals(newId)) {
99+
h.attr("id", newId);
100+
h.select("a").stream()
101+
.filter(a -> a.attr("href").equals("#" + id) && !a.attr("class").isEmpty())
102+
.forEach(a -> a.attr("href", "#" + newId));
103+
}
104+
});
105+
}
106+
107+
private static boolean isDiscrete(Element e) {
108+
return e.hasClass("discrete");
109+
}
110+
111+
private static void clipboard(Document doc) {
112+
for (Element code : doc.select("code.hljs")) {
113+
String id = "x" + Long.toHexString(UUID.randomUUID().getMostSignificantBits());
114+
code.attr("id", id);
115+
Element button = code.parent().appendElement("button");
116+
button.addClass("clipboard");
117+
button.attr("data-clipboard-target", "#" + id);
118+
Element img = button.appendElement("img");
119+
img.attr("src", "/images/clippy.svg");
120+
img.attr("class", "clippy");
121+
img.attr("width", "13");
122+
img.attr("alt", "Copy to clipboard");
123+
}
124+
}
125+
}

docs/src/main/java/io/jooby/adoc/JoobyExtensionRegistry.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ public class JoobyExtensionRegistry implements ExtensionRegistry {
1313
public void register(Asciidoctor asciidoctor) {
1414
asciidoctor.javaExtensionRegistry().block("dependency", DependencyProcessor.class);
1515
asciidoctor.javaExtensionRegistry().inlineMacro("javadoc", JavadocProcessor.class);
16+
asciidoctor.javaExtensionRegistry().postprocessor(DocPostprocessor.class);
1617
}
1718
}

0 commit comments

Comments
 (0)