This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-doxia.git
The following commit(s) were added to refs/heads/master by this push: new 4ff3e3dd [DOXIA-749] Correctly indent and separate blocks inside list items (#238) 4ff3e3dd is described below commit 4ff3e3dd24aa79e2b222e0a488f4f9426ac86faf Author: Konrad Windszus <k...@apache.org> AuthorDate: Sun Oct 20 17:27:36 2024 +0200 [DOXIA-749] Correctly indent and separate blocks inside list items (#238) Block elements should be surrounded by blank lines inside list items to be compliant with more MD parsers Do not prefix standalone linebreaks (as often emitted from HTML parsers without any semantical meaning) --- .../maven/doxia/sink/impl/SinkEventElement.java | 10 ++++- .../doxia/sink/impl/SinkEventTestingSink.java | 2 +- .../maven/doxia/module/markdown/MarkdownSink.java | 46 ++++++++++++++++------ .../doxia/module/markdown/MarkdownSinkTest.java | 34 ++++++++++++---- 4 files changed, 70 insertions(+), 22 deletions(-) diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventElement.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventElement.java index 764e0c4b..97e12bf1 100644 --- a/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventElement.java +++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventElement.java @@ -35,6 +35,9 @@ public class SinkEventElement { /** The array of arguments to the sink method. */ private final Object[] args; + /** The line number of the source which emitted the event (-1 if unknown) */ + private final int lineNumber; + /** * A SinkEventElement is characterized by the method name and associated array of arguments. * @@ -42,11 +45,12 @@ public class SinkEventElement { * @param arguments The array of arguments to the sink method. * For a no-arg element this may be null or an empty array. */ - public SinkEventElement(String name, Object[] arguments) { + public SinkEventElement(String name, Object[] arguments, int lineNumber) { Objects.requireNonNull(name, "name cannot be null"); this.methodName = name; this.args = arguments; + this.lineNumber = lineNumber; } /** @@ -77,6 +81,10 @@ public class SinkEventElement { builder.append(this.getClass().getSimpleName()).append('['); builder.append("methodName: ").append(methodName).append(", "); builder.append("args: ").append(Arrays.deepToString(args)); + if (lineNumber != -1) { + builder.append(", "); + builder.append("lineNumber: ").append(lineNumber); + } builder.append(']'); return builder.toString(); } diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventTestingSink.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventTestingSink.java index 17bd4884..3a35f8fd 100644 --- a/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventTestingSink.java +++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/SinkEventTestingSink.java @@ -558,6 +558,6 @@ public class SinkEventTestingSink extends AbstractSink { * @param arguments The array of arguments to the sink method. */ private void addEvent(String string, Object[] arguments) { - events.add(new SinkEventElement(string, arguments)); + events.add(new SinkEventElement(string, arguments, getDocumentLocator().getLineNumber())); } } diff --git a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java index 9d95224a..fa07703f 100644 --- a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java +++ b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java @@ -152,7 +152,7 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { final boolean requiresBuffering; /** - * prefix to be used for a (nested) block elements inside the current container context (only not empty for {@link #type} being {@link Type#CONTAINER_BLOCK}) + * prefix to be used for each line of (nested) block elements inside the current container context (only not empty for {@link #type} being {@link Type#CONTAINER_BLOCK}) */ final String prefix; @@ -248,13 +248,18 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { throw new IllegalStateException("Unexpected context " + removedContext + ", expected " + expectedContext); } if (removedContext.isBlock()) { - endBlock(removedContext.requiresSurroundingByBlankLines); + endBlock(removedContext.requiresSurroundingByBlankLines + || (isInListItem() && (removedContext == ElementContext.BLOCKQUOTE) + || (removedContext == ElementContext.CODE_BLOCK))); } } private void startContext(ElementContext newContext) { if (newContext.isBlock()) { - startBlock(newContext.requiresSurroundingByBlankLines); + // every block element within a list item must + startBlock(newContext.requiresSurroundingByBlankLines + || (isInListItem() && (newContext == ElementContext.BLOCKQUOTE) + || (newContext == ElementContext.CODE_BLOCK))); } elementContextStack.add(newContext); } @@ -291,7 +296,7 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { } else { ensureBeginningOfLine(); } - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); } private void endBlock(boolean requireBlankLine) { @@ -302,12 +307,21 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { } } - private String getContainerLinePrefixes() { + /** + * @return the prefix to be used for each line in the current context (i.e. the prefix of the current container context and all its ancestors), may be empty + */ + private String getLinePrefix() { StringBuilder prefix = new StringBuilder(); elementContextStack.stream().filter(c -> c.prefix.length() > 0).forEachOrdered(c -> prefix.insert(0, c.prefix)); return prefix.toString(); } + private boolean isInListItem() { + return elementContextStack.stream() + .filter(c -> c == ElementContext.LIST_ITEM) + .findFirst() + .isPresent(); + } /** * Returns the buffer that holds the current text. * @@ -512,7 +526,7 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { // ignore paragraphs outside container contexts if (elementContextStack.element().isContainer()) { ensureBlankLine(); - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); } } @@ -529,13 +543,13 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { // always assume is supposed to be monospaced (i.e. emitted inside a <pre><code>...</code></pre>) startContext(ElementContext.CODE_BLOCK); writeUnescaped(VERBATIM_START_MARKUP + EOL); - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); } @Override public void verbatim_() { ensureBeginningOfLine(); - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); writeUnescaped(VERBATIM_END_MARKUP + BLANK_LINE); endContext(ElementContext.CODE_BLOCK); } @@ -555,13 +569,13 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { public void horizontalRule(SinkEventAttributes attributes) { ensureBeginningOfLine(); writeUnescaped(HORIZONTAL_RULE_MARKUP + BLANK_LINE); - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); } @Override public void table(SinkEventAttributes attributes) { ensureBlankLine(); - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); } @Override @@ -621,7 +635,7 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { writeUnescaped(StringUtils.repeat(String.valueOf(SPACE), 3) + TABLE_CELL_SEPARATOR_MARKUP); } writeUnescaped(EOL); - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); } /** Emit the delimiter row which determines the alignment */ @@ -868,7 +882,7 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { } else { writeUnescaped("" + SPACE + SPACE + EOL); } - writeUnescaped(getContainerLinePrefixes()); + writeUnescaped(getLinePrefix()); } @Override @@ -887,6 +901,14 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { LOGGER.warn("{}Ignoring unsupported table caption in Markdown", getLocationLogPrefix()); } else { String unifiedText = currentContext.escape(unifyEOLs(text)); + // ignore newlines only, because those are emitted often coming from linebreaks in HTML with no semantical + // meaning + if (!unifiedText.equals(EOL)) { + String prefix = getLinePrefix(); + if (prefix.length() > 0) { + unifiedText = unifiedText.replaceAll(EOL, EOL + prefix); + } + } writeUnescaped(unifiedText); } if (attributes != null) { diff --git a/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java b/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java index d9d29345..e3c3a89a 100644 --- a/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java +++ b/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java @@ -33,11 +33,10 @@ import org.apache.maven.doxia.sink.impl.AbstractSinkTest; import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet; import org.apache.maven.doxia.sink.impl.SinkEventTestingSink; import org.apache.maven.doxia.util.HtmlTools; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; /** * Test the <code>MarkdownSink</code> class @@ -377,12 +376,11 @@ public class MarkdownSinkTest extends AbstractSinkTest { final SinkEventTestingSink originalSink = new SinkEventTestingSink(); parseFile(parser, "test", originalSink); + // strip empty lines from sink content // compare sink events from parsing original markdown with sink events from re-generated markdown try { - MatcherAssert.assertThat( - regeneratedSink.getEventList(), - Matchers.contains(originalSink.getEventList().toArray())); + assertIterableEquals(originalSink.getEventList(), regeneratedSink.getEventList()); } catch (AssertionError e) { // emit generated markdown to ease debugging System.err.println(getSinkContent()); @@ -488,13 +486,13 @@ public class MarkdownSinkTest extends AbstractSinkTest { String expected = "- item1" + EOL + " - item1a" + EOL + EOL - + "- " + EOL - + " > blockquote" + EOL + + "- " + EOL + EOL + + " > blockquote" + EOL + EOL + "- item3" + EOL + EOL + " item3paragraph2" + EOL + EOL - + "- item4" + EOL + + "- item4" + EOL + EOL + " ```" + EOL + " item4verbatim" + EOL + " item4verbatimline2" + EOL @@ -519,6 +517,26 @@ public class MarkdownSinkTest extends AbstractSinkTest { assertEquals(expected, getSinkContent(), "Wrong heading after inline element!"); } + @Test + public void testMultilineVerbatimSourceAfterListItem() { + try (final Sink sink = getSink()) { + sink.list(); + sink.listItem(); + sink.text("Before"); + sink.verbatim(SinkEventAttributeSet.SOURCE); + sink.text("codeline1" + EOL + "codeline2"); + sink.verbatim_(); + sink.text("After"); + sink.listItem_(); + sink.list_(); + } + + String expected = "- Before" + EOL + EOL + MarkdownMarkup.INDENT + MarkdownMarkup.VERBATIM_START_MARKUP + EOL + + MarkdownMarkup.INDENT + "codeline1" + EOL + MarkdownMarkup.INDENT + "codeline2" + EOL + + MarkdownMarkup.INDENT + MarkdownMarkup.VERBATIM_END_MARKUP + EOL + EOL + "After" + EOL + EOL; + assertEquals(expected, getSinkContent(), "Wrong verbatim!"); + } + @Test public void testDefinitionListWithInlineStyles() { try (final Sink sink = getSink()) {