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 <[email protected]>
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()) {