Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DOXIA-749] Correctly indent and separate blocks inside list items #238

Merged
merged 3 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 event (-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 @@ -151,7 +151,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 @@ -247,13 +247,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 @@ -290,7 +295,7 @@ private void startBlock(boolean requireBlankLine) {
} else {
ensureBeginningOfLine();
}
writeUnescaped(getContainerLinePrefixes());
writeUnescaped(getLinePrefix());
}

private void endBlock(boolean requireBlankLine) {
Expand All @@ -301,12 +306,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 @@ -511,7 +525,7 @@ public void paragraph(SinkEventAttributes attributes) {
// ignore paragraphs outside container contexts
if (elementContextStack.element().isContainer()) {
ensureBlankLine();
writeUnescaped(getContainerLinePrefixes());
writeUnescaped(getLinePrefix());
}
}

Expand All @@ -528,13 +542,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 @@ -554,13 +568,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 @@ -620,7 +634,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 @@ -867,7 +881,7 @@ public void lineBreak(SinkEventAttributes attributes) {
} else {
writeUnescaped("" + SPACE + SPACE + EOL);
}
writeUnescaped(getContainerLinePrefixes());
writeUnescaped(getLinePrefix());
}

@Override
Expand All @@ -886,6 +900,14 @@ public void text(String text, SinkEventAttributes attributes) {
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) {
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 @@ -372,12 +371,11 @@ public void testRoundtrip() throws IOException, ParseException {

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());
Expand Down Expand Up @@ -483,13 +481,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 @@ -514,6 +512,26 @@ public void testHeadingAfterInlineElement() {
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()) {
Expand Down