diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java index 6f5d8bf6c23a8..5ef48f8b6fd30 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/JavaDocParser.java @@ -52,6 +52,7 @@ final class JavaDocParser { private static final String ORDERED_LIST_NODE = "ol"; private static final String SUPER_SCRIPT_NODE = "sup"; private static final String UN_ORDERED_LIST_NODE = "ul"; + private static final String PREFORMATED_NODE = "pre"; private static final String BIG_ASCIDOC_STYLE = "[.big]"; private static final String LINK_ATTRIBUTE_FORMAT = "[%s]"; @@ -62,6 +63,8 @@ final class JavaDocParser { private static final String UNORDERED_LIST_ITEM_ASCIDOC_STYLE = " - "; private static final String UNDERLINE_ASCIDOC_STYLE = "[.underline]"; private static final String LINE_THROUGH_ASCIDOC_STYLE = "[.line-through]"; + private static final String HARD_LINE_BREAK_ASCIDOC_STYLE = " +\n"; + private static final String CODE_BLOCK_ASCIDOC_STYLE = "```"; private final boolean inlineMacroMode; @@ -185,25 +188,37 @@ private String htmlJavadocToAsciidoc(JavadocDescription javadocDescription) { } } - return sb.toString().trim(); + return trim(sb); } private void appendHtml(StringBuilder sb, Node node) { for (Node childNode : node.childNodes()) { switch (childNode.nodeName()) { case PARAGRAPH_NODE: - sb.append(NEW_LINE); + newLine(sb); + newLine(sb); appendHtml(sb, childNode); break; + case PREFORMATED_NODE: + newLine(sb); + newLine(sb); + sb.append(CODE_BLOCK_ASCIDOC_STYLE); + newLine(sb); + for (Node grandChildNode : childNode.childNodes()) { + sb.append(grandChildNode.toString()); + } + sb.append(CODE_BLOCK_ASCIDOC_STYLE); + break; case ORDERED_LIST_NODE: case UN_ORDERED_LIST_NODE: + newLine(sb); appendHtml(sb, childNode); break; case LIST_ITEM_NODE: final String marker = childNode.parent().nodeName().equals(ORDERED_LIST_NODE) ? ORDERED_LIST_ITEM_ASCIDOC_STYLE : UNORDERED_LIST_ITEM_ASCIDOC_STYLE; - sb.append(NEW_LINE); + newLine(sb); sb.append(marker); appendHtml(sb, childNode); break; @@ -213,7 +228,7 @@ private void appendHtml(StringBuilder sb, Node node) { sb.append(link); final StringBuilder caption = new StringBuilder(); appendHtml(caption, childNode); - sb.append(String.format(LINK_ATTRIBUTE_FORMAT, caption.toString().trim())); + sb.append(String.format(LINK_ATTRIBUTE_FORMAT, trim(caption))); break; case CODE_NODE: sb.append(BACKTICK); @@ -269,7 +284,7 @@ private void appendHtml(StringBuilder sb, Node node) { sb.append(HASH); break; case NEW_LINE_NODE: - sb.append(NEW_LINE); + sb.append(HARD_LINE_BREAK_ASCIDOC_STYLE); break; case TEXT_NODE: String text = ((TextNode) childNode).text(); @@ -295,6 +310,66 @@ private void appendHtml(StringBuilder sb, Node node) { } } + /** + * Trim the content of the given {@link StringBuilder} holding also AsciiDoc had line break {@code " +\n"} + * for whitespace in addition to characters <= {@code ' '}. + * + * @param sb the {@link StringBuilder} to trim + * @return the trimmed content of the given {@link StringBuilder} + */ + static String trim(StringBuilder sb) { + int length = sb.length(); + int offset = 0; + while (offset < length) { + final char ch = sb.charAt(offset); + if (ch == ' ' + && offset + 2 < length + && sb.charAt(offset + 1) == '+' + && sb.charAt(offset + 2) == '\n') { + /* Space followed by + and newline is AsciiDoc hard break that we consider whitespace */ + offset += 3; + continue; + } else if (ch > ' ') { + /* Non-whitespace as defined by String.trim() */ + break; + } + offset++; + } + if (offset > 0) { + sb.delete(0, offset); + } + if (sb.length() > 0) { + offset = sb.length() - 1; + while (offset >= 0) { + final char ch = sb.charAt(offset); + if (ch == '\n' + && offset - 2 >= 0 + && sb.charAt(offset - 1) == '+' + && sb.charAt(offset - 2) == ' ') { + /* Space followed by + is AsciiDoc hard break that we consider whitespace */ + offset -= 3; + continue; + } else if (ch > ' ') { + /* Non-whitespace as defined by String.trim() */ + break; + } + offset--; + } + if (offset < sb.length() - 1) { + sb.setLength(offset + 1); + } + } + return sb.toString(); + } + + private static StringBuilder newLine(StringBuilder sb) { + /* Trim trailing spaces and tabs at the end of line */ + while (sb.length() > 0 && " \t".indexOf(sb.charAt(sb.length() - 1)) >= 0) { + sb.setLength(sb.length() - 1); + } + return sb.append(NEW_LINE); + } + private StringBuilder appendEscapedAsciiDoc(StringBuilder sb, String text) { boolean escaping = false; for (int i = 0; i < text.length(); i++) { diff --git a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java index 5410b7f7bdc91..34f59f195b853 100644 --- a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java +++ b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/JavaDocConfigDescriptionParserTest.java @@ -28,7 +28,7 @@ public void parseNullJavaDoc() { @Test public void removeParagraphIndentation() { String parsed = parser.parseConfigDescription("First paragraph

Second Paragraph"); - assertEquals("First paragraph\n\nSecond Paragraph", parsed); + assertEquals("First paragraph +\n +\nSecond Paragraph", parsed); } @Test @@ -50,13 +50,13 @@ public void parseSimpleJavaDoc() { @Test public void parseJavaDocWithParagraph() { String javaDoc = "hello

world

"; - String expectedOutput = "hello\nworld"; + String expectedOutput = "hello\n\nworld"; String parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); javaDoc = "hello world

bonjour

le monde

"; - expectedOutput = "hello world\nbonjour \nle monde"; + expectedOutput = "hello world\n\nbonjour\n\nle monde"; parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); @@ -118,21 +118,6 @@ public void parseJavaDocWithStyles() { assertEquals(expectedOutput, parsed); } - @Test - public void parseJavaDocWithUlTags() { - String javaDoc = "hello "; - String expectedOutput = "hello world"; - String parsed = parser.parseConfigDescription(javaDoc); - - assertEquals(expectedOutput, parsed); - - javaDoc = "hello world"; - expectedOutput = "hello world bonjour le monde"; - parsed = parser.parseConfigDescription(javaDoc); - - assertEquals(expectedOutput, parsed); - } - @Test public void parseJavaDocWithLiTagsInsideUlTag() { String javaDoc = "List:" + @@ -141,7 +126,7 @@ public void parseJavaDocWithLiTagsInsideUlTag() { "
  • 2
  • \n" + "" + ""; - String expectedOutput = "List: \n - 1 \n - 2"; + String expectedOutput = "List:\n\n - 1\n - 2"; String parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); @@ -155,7 +140,7 @@ public void parseJavaDocWithLiTagsInsideOlTag() { "
  • 2
  • \n" + "" + ""; - String expectedOutput = "List: \n . 1 \n . 2"; + String expectedOutput = "List:\n\n . 1\n . 2"; String parsed = parser.parseConfigDescription(javaDoc); assertEquals(expectedOutput, parsed); @@ -224,6 +209,15 @@ public void parseJavaDocWithUnknownNode() { assertEquals(expectedOutput, parsed); } + @Test + public void parseJavaDocWithCodeBlock() { + assertEquals("Example:\n\n```\nfoo\nbar\n```", parser.parseConfigDescription("Example:\n\n
    \nfoo\nbar\n
    ")); + + // TODO + // assertEquals("Example:\n\n```\nfoo\nbar\n```", + // parser.parseConfigDescription("Example:\n\n
    {@code\nfoo\nbar\n}
    ")); + } + @Test public void asciidoc() { String asciidoc = "== My Asciidoc\n" + @@ -308,4 +302,25 @@ public void escapeBrackets(String ch) { assertEquals(expected, actual); } + @Test + void trim() { + assertEquals("+ \nfoo", JavaDocParser.trim(new StringBuilder("+ \nfoo"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder(" +"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder(" +\nfoo"))); + assertEquals("foo +", JavaDocParser.trim(new StringBuilder("foo +"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder("foo"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder("+ \n"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder(" +\n+ \n"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" +\n"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder(" \n\tfoo"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder("foo \n\t"))); + assertEquals("foo", JavaDocParser.trim(new StringBuilder(" \n\tfoo \n\t"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(""))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" \n\t"))); + assertEquals("+", JavaDocParser.trim(new StringBuilder(" +"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" +\n"))); + assertEquals("", JavaDocParser.trim(new StringBuilder(" +\n +\n"))); + assertEquals("foo +\nbar", JavaDocParser.trim(new StringBuilder(" foo +\nbar +\n"))); + } + }