Skip to content

Commit

Permalink
[DOXIA-749] Correctly indent and separate blocks inside list items
Browse files Browse the repository at this point in the history
Block elements should be surrounded by blank lines inside list items to
be compliant with more MD parsers
  • Loading branch information
kwin committed Oct 15, 2024
1 parent b054e54 commit 6631e16
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,22 @@ public class SinkEventElement {
/** The array of arguments to the sink method. */
private final Object[] args;

/** The line number of the source which emitted the source (-1 if unknown) */
private final int lineNumber;

/**
* A SinkEventElement is characterized by the method name and associated array of arguments.
*
* @param name The name of the sink event, ie the sink method name.
* @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;
}

/**
Expand Down Expand Up @@ -77,6 +81,10 @@ public String toString() {
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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,6 @@ private void addEvent(String string) {
* @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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ enum Type {
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;

Expand Down Expand Up @@ -246,13 +246,18 @@ private void endContext(ElementContext expectedContext) {
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);
}
Expand Down Expand Up @@ -289,7 +294,7 @@ private void startBlock(boolean requireBlankLine) {
} else {
ensureBeginningOfLine();
}
writeUnescaped(getContainerLinePrefixes());
writeUnescaped(getLinePrefix());
}

private void endBlock(boolean requireBlankLine) {
Expand All @@ -300,12 +305,21 @@ private void endBlock(boolean requireBlankLine) {
}
}

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.
*
Expand Down Expand Up @@ -509,7 +523,7 @@ public void paragraph(SinkEventAttributes attributes) {
// ignore paragraphs outside container contexts
if (elementContextStack.element().isContainer()) {
ensureBlankLine();
writeUnescaped(getContainerLinePrefixes());
writeUnescaped(getLinePrefix());
}
}

Expand All @@ -526,13 +540,13 @@ public void verbatim(SinkEventAttributes attributes) {
// 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);
}
Expand All @@ -552,13 +566,13 @@ public void blockquote_() {
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
Expand Down Expand Up @@ -618,7 +632,7 @@ private void writeEmptyTableHeader() {
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 */
Expand Down Expand Up @@ -849,7 +863,7 @@ public void lineBreak(SinkEventAttributes attributes) {
} else {
writeUnescaped("" + SPACE + SPACE + EOL);
}
writeUnescaped(getContainerLinePrefixes());
writeUnescaped(getLinePrefix());
}

@Override
Expand All @@ -859,6 +873,9 @@ public void nonBreakingSpace() {

@Override
public void text(String text, SinkEventAttributes attributes) {
if (text.equals("\n")) {
return;
}
if (attributes != null) {
inline(attributes);
}
Expand All @@ -868,6 +885,11 @@ public void text(String text, SinkEventAttributes attributes) {
LOGGER.warn("{}Ignoring unsupported table caption in Markdown", getLocationLogPrefix());
} else {
String unifiedText = currentContext.escape(unifyEOLs(text));
// each line must potentially be prefixed
String prefix = getLinePrefix();
if (prefix.length() > 0) {
unifiedText = unifiedText.replaceAll(EOL, EOL + prefix);
}
writeUnescaped(unifiedText);
}
if (attributes != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@
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
Expand Down Expand Up @@ -375,9 +374,7 @@ public void testRoundtrip() throws IOException, ParseException {

// 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());
Expand Down Expand Up @@ -483,13 +480,13 @@ public void testNestedListWithBlockquotesParagraphsAndCode() {
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
Expand All @@ -513,4 +510,24 @@ public void testHeadingAfterInlineElement() {
String expected = "Text" + EOL + "# Section1" + EOL + EOL;
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\ncodeline2");
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!");
}
}

0 comments on commit 6631e16

Please sign in to comment.