` elements with CSS classes:
+
+```html
+
+```
+
+Basic CSS example:
+
+```css
+.markdown-alert {
+ padding: 0.5rem 1rem;
+ margin-bottom: 1rem;
+ border-left: 4px solid;
+}
+
+.markdown-alert-note { border-color: #0969da; background-color: #ddf4ff; }
+.markdown-alert-tip { border-color: #1a7f37; background-color: #dcffe4; }
+.markdown-alert-important { border-color: #8250df; background-color: #f6f0ff; }
+.markdown-alert-warning { border-color: #9a6700; background-color: #fff8c5; }
+.markdown-alert-caution { border-color: #cf222e; background-color: #ffebe9; }
+```
+
+
+
+Icons can be added using GitHub's [Octicons](https://primer.style/octicons/):
+
+
+
+## License
+
+See the main commonmark-java project for license information.
diff --git a/commonmark-ext-gfm-alerts/pom.xml b/commonmark-ext-gfm-alerts/pom.xml
new file mode 100644
index 000000000..02ecbf802
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ org.commonmark
+ commonmark-parent
+ 0.28.1-SNAPSHOT
+
+
+ commonmark-ext-gfm-alerts
+ commonmark-java extension for alerts
+ commonmark-java extension for GFM alerts (admonition blocks) using [!TYPE] syntax (GitHub Flavored Markdown)
+
+
+
+ org.commonmark
+ commonmark
+
+
+
+ org.commonmark
+ commonmark-test-util
+ test
+
+
+
+
diff --git a/commonmark-ext-gfm-alerts/screenshots/alerts-with-icons.png b/commonmark-ext-gfm-alerts/screenshots/alerts-with-icons.png
new file mode 100644
index 000000000..47da9402b
Binary files /dev/null and b/commonmark-ext-gfm-alerts/screenshots/alerts-with-icons.png differ
diff --git a/commonmark-ext-gfm-alerts/screenshots/alerts.png b/commonmark-ext-gfm-alerts/screenshots/alerts.png
new file mode 100644
index 000000000..83d4009f0
Binary files /dev/null and b/commonmark-ext-gfm-alerts/screenshots/alerts.png differ
diff --git a/commonmark-ext-gfm-alerts/src/main/java/module-info.java b/commonmark-ext-gfm-alerts/src/main/java/module-info.java
new file mode 100644
index 000000000..e8b5aecb7
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/module-info.java
@@ -0,0 +1,5 @@
+module org.commonmark.ext.gfm.alerts {
+ exports org.commonmark.ext.gfm.alerts;
+
+ requires transitive org.commonmark;
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/Alert.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/Alert.java
new file mode 100644
index 000000000..bb28e7344
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/Alert.java
@@ -0,0 +1,19 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.node.CustomBlock;
+
+/**
+ * Alert block for highlighting important information using {@code [!TYPE]} syntax.
+ */
+public class Alert extends CustomBlock {
+
+ private final String type;
+
+ public Alert(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java
new file mode 100644
index 000000000..3990034d2
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java
@@ -0,0 +1,118 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.ext.gfm.alerts.internal.AlertPostProcessor;
+import org.commonmark.ext.gfm.alerts.internal.AlertHtmlNodeRenderer;
+import org.commonmark.ext.gfm.alerts.internal.AlertMarkdownNodeRenderer;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.NodeRenderer;
+import org.commonmark.renderer.html.HtmlNodeRendererContext;
+import org.commonmark.renderer.html.HtmlNodeRendererFactory;
+import org.commonmark.renderer.html.HtmlRenderer;
+import org.commonmark.renderer.markdown.MarkdownNodeRendererContext;
+import org.commonmark.renderer.markdown.MarkdownNodeRendererFactory;
+import org.commonmark.renderer.markdown.MarkdownRenderer;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Extension for GFM alerts using {@code [!TYPE]} syntax (GitHub Flavored Markdown).
+ *
+ * Create with {@link #create()} or {@link #builder()} and configure on builders
+ * ({@link org.commonmark.parser.Parser.Builder#extensions(Iterable)},
+ * {@link HtmlRenderer.Builder#extensions(Iterable)}).
+ * Parsed alerts become {@link Alert} blocks.
+ */
+public class AlertsExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension,
+ MarkdownRenderer.MarkdownRendererExtension {
+
+ static final Set STANDARD_TYPES = Set.of("NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION");
+
+ private final Map customTypes;
+
+ private AlertsExtension(Builder builder) {
+ this.customTypes = new HashMap<>(builder.customTypes);
+ }
+
+ public static Extension create() {
+ return builder().build();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void extend(Parser.Builder parserBuilder) {
+ var allowedTypes = new HashSet<>(STANDARD_TYPES);
+ allowedTypes.addAll(customTypes.keySet());
+ parserBuilder.postProcessor(new AlertPostProcessor(allowedTypes));
+ }
+
+ @Override
+ public void extend(HtmlRenderer.Builder rendererBuilder) {
+ rendererBuilder.nodeRendererFactory(new HtmlNodeRendererFactory() {
+ @Override
+ public NodeRenderer create(HtmlNodeRendererContext context) {
+ return new AlertHtmlNodeRenderer(context, customTypes);
+ }
+ });
+ }
+
+ @Override
+ public void extend(MarkdownRenderer.Builder rendererBuilder) {
+ rendererBuilder.nodeRendererFactory(new MarkdownNodeRendererFactory() {
+ @Override
+ public NodeRenderer create(MarkdownNodeRendererContext context) {
+ return new AlertMarkdownNodeRenderer(context);
+ }
+
+ @Override
+ public Set getSpecialCharacters() {
+ return Set.of();
+ }
+ });
+ }
+
+ /**
+ * Builder for configuring the alerts extension.
+ */
+ public static class Builder {
+ private final Map customTypes = new HashMap<>();
+
+ /**
+ * Adds a custom alert type with a display title.
+ *
+ * This can also be used to override the display title of standard GFM types
+ * (e.g., for localization).
+ *
+ * @param type the alert type (must be uppercase)
+ * @param title the display title for this alert type
+ * @return {@code this}
+ */
+ public Builder addCustomType(String type, String title) {
+ if (type == null || type.isEmpty()) {
+ throw new IllegalArgumentException("Type must not be null or empty");
+ }
+ if (title == null || title.isEmpty()) {
+ throw new IllegalArgumentException("Title must not be null or empty");
+ }
+ if (!type.equals(type.toUpperCase(Locale.ROOT))) {
+ throw new IllegalArgumentException("Type must be uppercase: " + type);
+ }
+ customTypes.put(type, title);
+ return this;
+ }
+
+ /**
+ * @return a configured {@link Extension}
+ */
+ public Extension build() {
+ return new AlertsExtension(this);
+ }
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java
new file mode 100644
index 000000000..ca562ba33
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java
@@ -0,0 +1,78 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.Node;
+import org.commonmark.renderer.html.HtmlNodeRendererContext;
+import org.commonmark.renderer.html.HtmlWriter;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class AlertHtmlNodeRenderer extends AlertNodeRenderer {
+
+ private final HtmlWriter htmlWriter;
+ private final HtmlNodeRendererContext context;
+ private final Map customTypeTitles;
+
+ public AlertHtmlNodeRenderer(HtmlNodeRendererContext context, Map customTypeTitles) {
+ this.htmlWriter = context.getWriter();
+ this.context = context;
+ this.customTypeTitles = customTypeTitles;
+ }
+
+ @Override
+ protected void renderAlert(Alert alert) {
+ var type = alert.getType();
+ var cssClass = type.toLowerCase();
+
+ htmlWriter.line();
+ var attributes = new LinkedHashMap();
+ attributes.put("class", "markdown-alert markdown-alert-" + cssClass);
+ attributes.put("data-alert-type", cssClass);
+
+ htmlWriter.tag("div", context.extendAttributes(alert, "div", attributes));
+ htmlWriter.line();
+
+ // Render alert title
+ htmlWriter.tag("p", context.extendAttributes(alert, "p", Map.of("class", "markdown-alert-title")));
+ htmlWriter.text(getAlertTitle(type));
+ htmlWriter.tag("/p");
+ htmlWriter.line();
+
+ // Render children (the alert content)
+ renderChildren(alert);
+
+ htmlWriter.tag("/div");
+ htmlWriter.line();
+ }
+
+ private String getAlertTitle(String type) {
+ var customTypeTitle = customTypeTitles.get(type);
+ if (customTypeTitle != null) {
+ return customTypeTitle;
+ }
+ switch (type) {
+ case "NOTE":
+ return "Note";
+ case "TIP":
+ return "Tip";
+ case "IMPORTANT":
+ return "Important";
+ case "WARNING":
+ return "Warning";
+ case "CAUTION":
+ return "Caution";
+ default:
+ throw new IllegalStateException("Unknown alert type: " + type);
+ }
+ }
+
+ private void renderChildren(Node parent) {
+ var node = parent.getFirstChild();
+ while (node != null) {
+ var next = node.getNext();
+ context.render(node);
+ node = next;
+ }
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertMarkdownNodeRenderer.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertMarkdownNodeRenderer.java
new file mode 100644
index 000000000..e3da62aea
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertMarkdownNodeRenderer.java
@@ -0,0 +1,38 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.Node;
+import org.commonmark.renderer.markdown.MarkdownNodeRendererContext;
+import org.commonmark.renderer.markdown.MarkdownWriter;
+
+public class AlertMarkdownNodeRenderer extends AlertNodeRenderer {
+
+ private final MarkdownWriter writer;
+ private final MarkdownNodeRendererContext context;
+
+ public AlertMarkdownNodeRenderer(MarkdownNodeRendererContext context) {
+ this.writer = context.getWriter();
+ this.context = context;
+ }
+
+ @Override
+ protected void renderAlert(Alert alert) {
+ // First line: > [!TYPE]
+ writer.writePrefix("> ");
+ writer.pushPrefix("> ");
+ writer.raw("[!" + alert.getType() + "]");
+ writer.line();
+ renderChildren(alert);
+ writer.popPrefix();
+ writer.block();
+ }
+
+ private void renderChildren(Node parent) {
+ var node = parent.getFirstChild();
+ while (node != null) {
+ var next = node.getNext();
+ context.render(node);
+ node = next;
+ }
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java
new file mode 100644
index 000000000..45b34bb46
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java
@@ -0,0 +1,23 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.Node;
+import org.commonmark.renderer.NodeRenderer;
+
+import java.util.Set;
+
+public abstract class AlertNodeRenderer implements NodeRenderer {
+
+ @Override
+ public Set> getNodeTypes() {
+ return Set.of(Alert.class);
+ }
+
+ @Override
+ public void render(Node node) {
+ var alert = (Alert) node;
+ renderAlert(alert);
+ }
+
+ protected abstract void renderAlert(Alert alert);
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java
new file mode 100644
index 000000000..8008fc8dd
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java
@@ -0,0 +1,111 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.BlockQuote;
+import org.commonmark.node.HardLineBreak;
+import org.commonmark.node.Node;
+import org.commonmark.node.Paragraph;
+import org.commonmark.node.SoftLineBreak;
+import org.commonmark.node.Text;
+import org.commonmark.parser.PostProcessor;
+
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class AlertPostProcessor implements PostProcessor {
+
+ // Alert type marker, matching any case (GitHub supports lowercase, mixed, and uppercase)
+ private static final Pattern ALERT_PATTERN = Pattern.compile("^\\[!([a-zA-Z]+)]\\s*$");
+
+ private final Set allowedTypes;
+
+ public AlertPostProcessor(Set allowedTypes) {
+ this.allowedTypes = allowedTypes;
+ }
+
+ @Override
+ public Node process(Node document) {
+ // Only look at direct children of Document — GitHub only detects alerts at the top level.
+ var child = document.getFirstChild();
+ while (child != null) {
+ var next = child.getNext();
+ if (child instanceof BlockQuote) {
+ tryConvertToAlert((BlockQuote) child);
+ }
+ child = next;
+ }
+ return document;
+ }
+
+ private void tryConvertToAlert(BlockQuote blockQuote) {
+ var firstChild = blockQuote.getFirstChild();
+ if (!(firstChild instanceof Paragraph)) {
+ return;
+ }
+
+ var paragraph = (Paragraph) firstChild;
+ var firstInline = paragraph.getFirstChild();
+ if (!(firstInline instanceof Text)) {
+ return;
+ }
+
+ var textNode = (Text) firstInline;
+
+ // The alert marker can be the entire text node content, or just the first line
+ // before a line break (trailing spaces create a HardLineBreak instead of SoftLineBreak).
+ var afterMarker = firstInline.getNext();
+ if (afterMarker != null && !(afterMarker instanceof SoftLineBreak) && !(afterMarker instanceof HardLineBreak)) {
+ // Text followed by something other than a line break - not an alert
+ return;
+ }
+
+ var matcher = ALERT_PATTERN.matcher(textNode.getLiteral());
+ if (!matcher.matches()) {
+ return;
+ }
+
+ var type = matcher.group(1).toUpperCase(Locale.ROOT);
+ if (!allowedTypes.contains(type)) {
+ return;
+ }
+
+ // Must have content after the marker line. An alert with ONLY the marker
+ // and no content is a normal blockquote on GitHub.
+ if (afterMarker != null) {
+ // There's a line break after marker - check if there's content after it
+ if (afterMarker.getNext() == null && paragraph.getNext() == null) {
+ return;
+ }
+ afterMarker.unlink();
+ } else {
+ // Marker is the only thing in the paragraph
+ if (paragraph.getNext() == null) {
+ return;
+ }
+ }
+
+ // Valid alert. Create Alert node and transfer children.
+ var alert = new Alert(type);
+ alert.setSourceSpans(blockQuote.getSourceSpans());
+ blockQuote.insertAfter(alert);
+
+ // Remove the marker text from the first paragraph
+ firstInline.unlink();
+
+ // If paragraph is now empty, remove it
+ if (paragraph.getFirstChild() == null) {
+ paragraph.unlink();
+ }
+
+ // Move remaining children from blockquote to alert
+ var child = blockQuote.getFirstChild();
+ while (child != null) {
+ var next = child.getNext();
+ alert.appendChild(child);
+ child = next;
+ }
+
+ blockQuote.unlink();
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/javadoc/overview.html b/commonmark-ext-gfm-alerts/src/main/javadoc/overview.html
new file mode 100644
index 000000000..145232a87
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/javadoc/overview.html
@@ -0,0 +1,6 @@
+
+
+Extension for GitHub Flavored Markdown (GFM) alerts using blockquote syntax
+See {@link org.commonmark.ext.gfm.alerts.AlertsExtension}
+
+
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsMarkdownRendererTest.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsMarkdownRendererTest.java
new file mode 100644
index 000000000..aca90e2df
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsMarkdownRendererTest.java
@@ -0,0 +1,73 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.markdown.MarkdownRenderer;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AlertsMarkdownRendererTest {
+
+ private static final Set EXTENSIONS = Set.of(AlertsExtension.create());
+ private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
+ private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(EXTENSIONS).build();
+
+ @Test
+ public void alertRoundTrip() {
+ assertRoundTrip("> [!WARNING]\n> Be careful\n");
+ }
+
+ @Test
+ public void allStandardTypesRoundTrip() {
+ assertRoundTrip("> [!NOTE]\n> Note\n");
+ assertRoundTrip("> [!TIP]\n> Tip\n");
+ assertRoundTrip("> [!IMPORTANT]\n> Important\n");
+ assertRoundTrip("> [!WARNING]\n> Warning\n");
+ assertRoundTrip("> [!CAUTION]\n> Caution\n");
+ }
+
+ @Test
+ public void lowercaseTypeRendersAsUppercase() {
+ // Lowercase input gets normalized to uppercase type
+ String rendered = RENDERER.render(PARSER.parse("> [!note]\n> Content\n"));
+ assertThat(rendered).isEqualTo("> [!NOTE]\n> Content\n");
+ }
+
+ @Test
+ public void alertWithMultipleParagraphs() {
+ String input = "> [!NOTE]\n> First paragraph\n>\n> Second paragraph\n";
+ // MarkdownWriter always writes the prefix including trailing space
+ String expected = "> [!NOTE]\n> First paragraph\n> \n> Second paragraph\n";
+ String rendered = RENDERER.render(PARSER.parse(input));
+ assertThat(rendered).isEqualTo(expected);
+ }
+
+ @Test
+ public void customTypeRoundTrip() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ MarkdownRenderer renderer = MarkdownRenderer.builder().extensions(Set.of(extension)).build();
+
+ String input = "> [!INFO]\n> Custom type\n";
+ String rendered = renderer.render(parser.parse(input));
+ assertThat(rendered).isEqualTo(input);
+ }
+
+ @Test
+ public void alertWithList() {
+ String input = "> [!NOTE]\n> Items:\n> \n> - First\n> - Second\n";
+ String rendered = RENDERER.render(PARSER.parse(input));
+ assertThat(rendered).isEqualTo(input);
+ }
+
+ private void assertRoundTrip(String input) {
+ String rendered = RENDERER.render(PARSER.parse(input));
+ assertThat(rendered).isEqualTo(input);
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java
new file mode 100644
index 000000000..8155d8009
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java
@@ -0,0 +1,44 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+import org.commonmark.testutil.RenderingTestCase;
+import org.commonmark.testutil.example.Example;
+import org.commonmark.testutil.example.ExampleReader;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.net.URL;
+import java.util.List;
+import java.util.Set;
+
+@ParameterizedClass
+@MethodSource("data")
+public class AlertsSpecTest extends RenderingTestCase {
+
+ private static final Set EXTENSIONS = Set.of(AlertsExtension.create());
+ private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
+ // Use softbreak(" ") to match GitHub's rendering for easier comparison with GitHub API output.
+ private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).softbreak(" \n").build();
+
+ @Parameter
+ Example example;
+
+ static List data() {
+ URL spec = AlertsSpecTest.class.getResource("/alerts-spec.txt");
+ return ExampleReader.readExamples(spec, "alert");
+ }
+
+ @Test
+ public void testHtmlRendering() {
+ assertRendering(example.getSource(), example.getHtml());
+ }
+
+ @Override
+ protected String render(String source) {
+ return RENDERER.render(PARSER.parse(source));
+ }
+}
\ No newline at end of file
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java
new file mode 100644
index 000000000..c46c532fe
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java
@@ -0,0 +1,140 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.node.Node;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class AlertsTest {
+
+ private static final Set EXTENSIONS = Set.of(AlertsExtension.create());
+ private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
+
+ // Custom types
+
+ @Test
+ public void customType() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!INFO]\n> Custom alert"))).isEqualTo(
+ "\n" +
+ "
Information
\n" +
+ "
Custom alert
\n" +
+ "
\n");
+ }
+
+ @Test
+ public void multipleCustomTypes() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .addCustomType("SUCCESS", "Success!")
+ .addCustomType("DANGER", "Danger!")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!INFO]\n> Info content\n\n> [!SUCCESS]\n> Success content\n\n> [!DANGER]\n> Danger content"))).isEqualTo(
+ "\n" +
+ "
Information
\n" +
+ "
Info content
\n" +
+ "
\n" +
+ "\n" +
+ "
Success!
\n" +
+ "
Success content
\n" +
+ "
\n" +
+ "\n" +
+ "
Danger!
\n" +
+ "
Danger content
\n" +
+ "
\n");
+ }
+
+ @Test
+ public void standardTypesWithCustomConfigured() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!NOTE]\n> Standard type"))).isEqualTo(
+ "\n" +
+ "
Note
\n" +
+ "
Standard type
\n" +
+ "
\n");
+ }
+
+ @Test
+ public void overrideStandardTypeTitle() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("NOTE", "Nota")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!NOTE]\n> Localized title"))).isEqualTo(
+ "\n" +
+ "
Nota
\n" +
+ "
Localized title
\n" +
+ "
\n");
+ }
+
+ // Custom type validation
+
+ @Test
+ public void customTypeMustBeUppercase() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AlertsExtension.builder().addCustomType("info", "Information").build());
+ }
+
+ @Test
+ public void customTypeMustNotBeEmpty() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AlertsExtension.builder().addCustomType("", "Title").build());
+ }
+
+ @Test
+ public void customTypeTitleMustNotBeEmpty() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AlertsExtension.builder().addCustomType("INFO", "").build());
+ }
+
+ // AST
+
+ @Test
+ public void alertParsedAsAlertNode() {
+ Node document = PARSER.parse("> [!NOTE]\n> This is a note");
+ Node firstChild = document.getFirstChild();
+ assertThat(firstChild).isInstanceOf(Alert.class);
+ Alert alert = (Alert) firstChild;
+ assertThat(alert.getType()).isEqualTo("NOTE");
+ }
+
+ @Test
+ public void customTypeParsedAsAlertNode() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+
+ Node document = parser.parse("> [!INFO]\n> Custom alert");
+ Alert alert = (Alert) document.getFirstChild();
+
+ assertThat(alert.getType()).isEqualTo("INFO");
+ }
+
+}
\ No newline at end of file
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java
new file mode 100644
index 000000000..34b78385c
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java
@@ -0,0 +1,85 @@
+package org.commonmark.ext.gfm.alerts.examples;
+
+import org.commonmark.ext.gfm.alerts.AlertsExtension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+
+import java.util.List;
+
+/**
+ * Example demonstrating the use of the GFM Alerts extension.
+ */
+public class AlertsExample {
+
+ public static void main(String[] args) {
+ standardTypesExample();
+ System.out.println("\n" + "=".repeat(60) + "\n");
+ customTypesExample();
+ }
+
+ private static void standardTypesExample() {
+ System.out.println("STANDARD GFM ALERT TYPES");
+ System.out.println("=".repeat(60));
+
+ var extension = AlertsExtension.create();
+
+ var parser = Parser.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var renderer = HtmlRenderer.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var markdown = "# GFM Alerts Demo\n\n" +
+ "> [!NOTE]\n" +
+ "> Highlights information that users should take into account.\n\n" +
+ "> [!TIP]\n" +
+ "> Helpful advice for doing things better.\n\n" +
+ "> [!IMPORTANT]\n" +
+ "> Key information users need to know.\n\n" +
+ "> [!WARNING]\n" +
+ "> Urgent info that needs immediate attention.\n\n" +
+ "> [!CAUTION]\n" +
+ "> Advises about risks or negative outcomes.\n";
+
+ var html = renderer.render(parser.parse(markdown));
+
+ System.out.println("Markdown Input:");
+ System.out.println(markdown);
+ System.out.println("\nHTML Output:");
+ System.out.println(html);
+ }
+
+ private static void customTypesExample() {
+ System.out.println("CUSTOM ALERT TYPES");
+ System.out.println("=".repeat(60));
+
+ var extension = AlertsExtension.builder()
+ .addCustomType("BUG", "Known Bug")
+ .build();
+
+ var parser = Parser.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var renderer = HtmlRenderer.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var markdown = "# Custom Alert Types\n\n" +
+ "> [!NOTE]\n" +
+ "> Useful information that users should know.\n\n" +
+ "> [!TIP]\n" +
+ "> Helpful advice for doing things better.\n\n" +
+ "> [!BUG]\n" +
+ "> This feature has a known issue with large files (see #42).\n";
+
+ var html = renderer.render(parser.parse(markdown));
+
+ System.out.println("Markdown Input:");
+ System.out.println(markdown);
+ System.out.println("\nHTML Output:");
+ System.out.println(html);
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec-template.md b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec-template.md
new file mode 100644
index 000000000..9c1cf117b
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec-template.md
@@ -0,0 +1,280 @@
+# Alerts
+
+## Standard types
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is a tip
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> This is important
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!WARNING]
+> This is a warning
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!CAUTION]
+> This is a caution
+````````````````````````````````
+
+## Case insensitivity
+
+Alert type matching is case-insensitive.
+
+```````````````````````````````` example alert
+> [!note]
+> Content
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!Note]
+> Content
+````````````````````````````````
+
+## Alert content
+
+Marker alone in first paragraph, blank line, then content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+>
+> Content
+````````````````````````````````
+
+Multiple paragraphs:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First paragraph
+>
+> Second paragraph
+````````````````````````````````
+
+Inline formatting:
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is **bold** and *italic*
+````````````````````````````````
+
+Code block inside alert:
+
+```````````````````````````````` example alert
+> [!TIP]
+> Code:
+>
+> function() { }
+>
+> End
+````````````````````````````````
+
+List inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> Items:
+> - First item
+> - Second item
+````````````````````````````````
+
+Links inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> Check out [this link](https://example.com) for more info
+````````````````````````````````
+
+Heading inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> ## Heading
+> Content below heading
+````````````````````````````````
+
+Empty lines in middle of alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First
+>
+>
+> After empty lines
+````````````````````````````````
+
+## Not an alert
+
+Text after marker on the same line:
+
+```````````````````````````````` example alert
+> [!NOTE] Some text
+````````````````````````````````
+
+Unknown type:
+
+```````````````````````````````` example alert
+> [!INVALID]
+> Some text
+````````````````````````````````
+
+Unconfigured custom type is not an alert:
+
+```````````````````````````````` example alert
+> [!INFO]
+> Should be blockquote
+````````````````````````````````
+
+Marker with no content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+````````````````````````````````
+
+Whitespace-only content after marker:
+
+```````````````````````````````` example alert
+> [!TIP]
+>
+>
+````````````````````````````````
+
+Extra space inside marker:
+
+```````````````````````````````` example alert
+> [! NOTE]
+> Should be blockquote
+````````````````````````````````
+
+Missing brackets:
+
+```````````````````````````````` example alert
+> !NOTE
+> Should be blockquote
+````````````````````````````````
+
+Missing exclamation mark:
+
+```````````````````````````````` example alert
+> [NOTE]
+> Should be blockquote
+````````````````````````````````
+
+Regular blockquote is not affected:
+
+```````````````````````````````` example alert
+> This is a regular blockquote
+````````````````````````````````
+
+## Boundaries
+
+Trailing spaces after marker:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+````````````````````````````````
+
+Trailing tabs after marker:
+
+```````````````````````````````` example alert
+> [!WARNING]→→
+> Be careful
+````````````````````````````````
+
+Leading spaces before blockquote marker:
+
+```````````````````````````````` example alert
+ > [!IMPORTANT]
+ > Content
+````````````````````````````````
+
+Blank line after marker ends the blockquote (not an alert):
+
+```````````````````````````````` example alert
+> [!NOTE]
+
+Some text
+````````````````````````````````
+
+Alert followed by blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is an alert
+
+> This is a blockquote
+````````````````````````````````
+
+Adjacent alerts:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First alert
+
+> [!WARNING]
+> Second alert
+````````````````````````````````
+
+## Nesting and containers
+
+Nested alert inside alert renders as blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> [!WARNING]
+>> Nested content
+````````````````````````````````
+
+Nested blockquote inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> Nested blockquote
+````````````````````````````````
+
+Alert inside list item stays as blockquote:
+
+```````````````````````````````` example alert
+- > [!NOTE]
+ > Test
+````````````````````````````````
+
+Alert marker in content is treated as text:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+> [!WARNING]
+> This is still part of the note
+````````````````````````````````
+
+## Continuation and interruption
+
+Lazy continuation:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First line
+Lazy continuation
+> Continues alert
+````````````````````````````````
+
+Alert type after regular blockquote content is not an alert:
+
+```````````````````````````````` example alert
+> Regular blockquote
+> [!NOTE]
+> More text
+````````````````````````````````
\ No newline at end of file
diff --git a/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt
new file mode 100644
index 000000000..6f041fee4
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt
@@ -0,0 +1,492 @@
+# Alerts
+
+Expectations verified against GitHub Markdown API (gh api markdown -f mode=gfm).
+Our HTML omits GitHub's SVG icons and uses a `data-alert-type` attribute instead.
+
+## Standard types
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+.
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is a tip
+.
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> This is important
+.
+
+
Important
+
This is important
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!WARNING]
+> This is a warning
+.
+
+
Warning
+
This is a warning
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!CAUTION]
+> This is a caution
+.
+
+
Caution
+
This is a caution
+
+````````````````````````````````
+
+## Case insensitivity
+
+Alert type matching is case-insensitive.
+
+```````````````````````````````` example alert
+> [!note]
+> Content
+.
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!Note]
+> Content
+.
+
+````````````````````````````````
+
+## Alert content
+
+Marker alone in first paragraph, blank line, then content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+>
+> Content
+.
+
+````````````````````````````````
+
+Multiple paragraphs:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First paragraph
+>
+> Second paragraph
+.
+
+
Note
+
First paragraph
+
Second paragraph
+
+````````````````````````````````
+
+Inline formatting:
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is **bold** and *italic*
+.
+
+
Tip
+
This is bold and italic
+
+````````````````````````````````
+
+Code block inside alert:
+
+```````````````````````````````` example alert
+> [!TIP]
+> Code:
+>
+> function() { }
+>
+> End
+.
+
+
Tip
+
Code:
+
function() { }
+
+
End
+
+````````````````````````````````
+
+List inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> Items:
+> - First item
+> - Second item
+.
+
+
Important
+
Items:
+
+First item
+Second item
+
+
+````````````````````````````````
+
+Links inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> Check out [this link](https://example.com) for more info
+.
+
+````````````````````````````````
+
+Heading inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> ## Heading
+> Content below heading
+.
+
+
Important
+
Heading
+
Content below heading
+
+````````````````````````````````
+
+Empty lines in middle of alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First
+>
+>
+> After empty lines
+.
+
+
Note
+
First
+
After empty lines
+
+````````````````````````````````
+
+## Not an alert
+
+Text after marker on the same line:
+
+```````````````````````````````` example alert
+> [!NOTE] Some text
+.
+
+[!NOTE] Some text
+
+````````````````````````````````
+
+Unknown type:
+
+```````````````````````````````` example alert
+> [!INVALID]
+> Some text
+.
+
+[!INVALID]
+Some text
+
+````````````````````````````````
+
+Unconfigured custom type is not an alert:
+
+```````````````````````````````` example alert
+> [!INFO]
+> Should be blockquote
+.
+
+[!INFO]
+Should be blockquote
+
+````````````````````````````````
+
+Marker with no content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+.
+
+[!NOTE]
+
+````````````````````````````````
+
+Whitespace-only content after marker:
+
+```````````````````````````````` example alert
+> [!TIP]
+>
+>
+.
+
+[!TIP]
+
+````````````````````````````````
+
+Extra space inside marker:
+
+```````````````````````````````` example alert
+> [! NOTE]
+> Should be blockquote
+.
+
+[! NOTE]
+Should be blockquote
+
+````````````````````````````````
+
+Missing brackets:
+
+```````````````````````````````` example alert
+> !NOTE
+> Should be blockquote
+.
+
+!NOTE
+Should be blockquote
+
+````````````````````````````````
+
+Missing exclamation mark:
+
+```````````````````````````````` example alert
+> [NOTE]
+> Should be blockquote
+.
+
+[NOTE]
+Should be blockquote
+
+````````````````````````````````
+
+Regular blockquote is not affected:
+
+```````````````````````````````` example alert
+> This is a regular blockquote
+.
+
+This is a regular blockquote
+
+````````````````````````````````
+
+## Boundaries
+
+Trailing spaces after marker:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+.
+
+````````````````````````````````
+
+Trailing tabs after marker:
+
+```````````````````````````````` example alert
+> [!WARNING]→→
+> Be careful
+.
+
+````````````````````````````````
+
+Leading spaces before blockquote marker:
+
+```````````````````````````````` example alert
+ > [!IMPORTANT]
+ > Content
+.
+
+````````````````````````````````
+
+Blank line after marker ends the blockquote (not an alert):
+
+```````````````````````````````` example alert
+> [!NOTE]
+
+Some text
+.
+
+[!NOTE]
+
+Some text
+````````````````````````````````
+
+Alert followed by blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is an alert
+
+> This is a blockquote
+.
+
+
Note
+
This is an alert
+
+
+This is a blockquote
+
+````````````````````````````````
+
+Adjacent alerts:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First alert
+
+> [!WARNING]
+> Second alert
+.
+
+
+````````````````````````````````
+
+## Nesting and containers
+
+Nested alert inside alert renders as blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> [!WARNING]
+>> Nested content
+.
+
+
Note
+
This is a note
+
+[!WARNING]
+Nested content
+
+
+````````````````````````````````
+
+Nested blockquote inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> Nested blockquote
+.
+
+
Note
+
This is a note
+
+Nested blockquote
+
+
+````````````````````````````````
+
+Alert inside list item stays as blockquote:
+
+```````````````````````````````` example alert
+- > [!NOTE]
+ > Test
+.
+
+````````````````````````````````
+
+Alert marker in content is treated as text:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+> [!WARNING]
+> This is still part of the note
+.
+
+
Note
+
This is a note
+[!WARNING]
+This is still part of the note
+
+````````````````````````````````
+
+## Continuation and interruption
+
+Lazy continuation:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First line
+Lazy continuation
+> Continues alert
+.
+
+
Note
+
First line
+Lazy continuation
+Continues alert
+
+````````````````````````````````
+
+Alert type after regular blockquote content is not an alert:
+
+```````````````````````````````` example alert
+> Regular blockquote
+> [!NOTE]
+> More text
+.
+
+Regular blockquote
+[!NOTE]
+More text
+
+````````````````````````````````
diff --git a/commonmark-ext-gfm-alerts/src/test/resources/generate-alerts-spec.java b/commonmark-ext-gfm-alerts/src/test/resources/generate-alerts-spec.java
new file mode 100644
index 000000000..06192f107
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/resources/generate-alerts-spec.java
@@ -0,0 +1,111 @@
+///usr/bin/env jbang "$0" "$@" ; exit $?
+
+// Generates alerts-spec.txt from alerts-spec-template.md by rendering each example
+// through the GitHub Markdown API and inserting the normalized HTML expectation.
+//
+// Prerequisites: gh CLI installed and authenticated (gh auth login)
+// Usage: cd commonmark-ext-gfm-alerts/src/test/resources && jbang generate-alerts-spec.java
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+class GenerateAlertsSpec {
+
+ private static final String FENCE = "````````````````````````````````";
+ private static final String EXAMPLE_OPEN = FENCE + " example alert";
+
+ public static void main(String[] args) throws Exception {
+ var templatePath = Path.of("alerts-spec-template.md");
+ if (!Files.exists(templatePath)) {
+ System.err.println("Run from the directory containing alerts-spec-template.md");
+ System.exit(1);
+ }
+
+ var lines = Files.readAllLines(templatePath);
+ var output = new ArrayList();
+ var header = "Expectations verified against GitHub Markdown API (gh api markdown -f mode=gfm).\n" +
+ "Our HTML omits GitHub's SVG icons and uses a `data-alert-type` attribute instead.";
+
+ int exampleCount = 0;
+ int i = 0;
+ while (i < lines.size()) {
+ var line = lines.get(i);
+
+ // Insert header after the first heading
+ if (i == 0 && line.startsWith("# ")) {
+ output.add(line);
+ output.add("");
+ output.add(header);
+ i++;
+ continue;
+ }
+
+ if (line.equals(EXAMPLE_OPEN)) {
+ // Collect source lines until closing fence
+ output.add(line);
+ i++;
+ var sourceLines = new ArrayList();
+ while (i < lines.size() && !lines.get(i).equals(FENCE)) {
+ sourceLines.add(lines.get(i));
+ output.add(lines.get(i));
+ i++;
+ }
+
+ // Render via GitHub API (→ represents tabs in the spec format)
+ var source = String.join("\n", sourceLines).replace("\u2192", "\t");
+ exampleCount++;
+ System.out.printf("%d: %s%n", exampleCount,
+ source.substring(0, Math.min(40, source.length())).replace("\n", "\\n"));
+
+ var ghHtml = normalizeHtml(renderViaGh(source));
+
+ // Insert separator and HTML expectation
+ output.add(".");
+ output.add(ghHtml);
+ output.add(FENCE);
+ i++; // skip closing fence from template
+ } else {
+ output.add(line);
+ i++;
+ }
+ }
+
+ var specPath = Path.of("alerts-spec.txt");
+ Files.writeString(specPath, String.join("\n", output) + "\n");
+ System.out.println("Done — " + exampleCount + " examples written to alerts-spec.txt");
+ }
+
+ static String renderViaGh(String markdown) throws Exception {
+ var process = new ProcessBuilder("gh", "api", "markdown", "-f", "mode=gfm", "-f", "text=" + markdown)
+ .redirectErrorStream(true)
+ .start();
+ var output = new String(process.getInputStream().readAllBytes());
+ if (process.waitFor() != 0) {
+ throw new RuntimeException("gh api failed: " + output);
+ }
+ return output;
+ }
+
+ // Normalize GitHub API HTML to match our renderer output.
+ static String normalizeHtml(String html) {
+ // Strip GitHub-specific elements and attributes
+ html = Pattern.compile("]*>.*? ", Pattern.DOTALL).matcher(html).replaceAll("");
+ html = html.replaceAll(" (dir=\"auto\"|rel=\"nofollow\"|class=\"notranslate\")", "");
+ // Add data-alert-type and insert newlines to match our renderer's formatting
+ html = Pattern.compile("class=\"markdown-alert markdown-alert-(\\w+)\"")
+ .matcher(html)
+ .replaceAll("class=\"markdown-alert markdown-alert-$1\" data-alert-type=\"$1\"");
+ html = Pattern.compile("(data-alert-type=\"\\w+\">)(
", "
\n");
+ return html.replace("\r\n", "\n").lines()
+ .map(String::stripTrailing)
+ .reduce((a, b) -> a + "\n" + b)
+ .orElse("")
+ .strip();
+ }
+}
\ No newline at end of file
diff --git a/commonmark-ext-gfm-strikethrough/.settings/org.eclipse.core.runtime.prefs b/commonmark-ext-gfm-strikethrough/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 5a0ad22d2..000000000
--- a/commonmark-ext-gfm-strikethrough/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/commonmark-ext-gfm-strikethrough/.settings/org.eclipse.jdt.core.prefs b/commonmark-ext-gfm-strikethrough/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 3c0d27c8f..000000000
--- a/commonmark-ext-gfm-strikethrough/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,290 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.7
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=0
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
-org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=true
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
-org.eclipse.jdt.core.formatter.comment.line_length=120
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=2
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
-org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=false
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=120
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
-org.eclipse.jdt.core.formatter.tabulation.char=space
-org.eclipse.jdt.core.formatter.tabulation.size=4
-org.eclipse.jdt.core.formatter.use_on_off_tags=false
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
-org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml
index b119b57f8..9d8f55e5f 100644
--- a/commonmark-ext-gfm-strikethrough/pom.xml
+++ b/commonmark-ext-gfm-strikethrough/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-gfm-strikethrough
diff --git a/commonmark-ext-gfm-strikethrough/src/main/java/module-info.java b/commonmark-ext-gfm-strikethrough/src/main/java/module-info.java
index 772710f00..b6204934b 100644
--- a/commonmark-ext-gfm-strikethrough/src/main/java/module-info.java
+++ b/commonmark-ext-gfm-strikethrough/src/main/java/module-info.java
@@ -1,5 +1,5 @@
module org.commonmark.ext.gfm.strikethrough {
exports org.commonmark.ext.gfm.strikethrough;
- requires org.commonmark;
+ requires transitive org.commonmark;
}
diff --git a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java
index f87f3e9c8..364205aed 100644
--- a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java
+++ b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java
@@ -17,7 +17,6 @@
import org.commonmark.renderer.text.TextContentNodeRendererFactory;
import org.commonmark.renderer.text.TextContentRenderer;
-import java.util.Collections;
import java.util.Set;
/**
@@ -106,7 +105,7 @@ public NodeRenderer create(MarkdownNodeRendererContext context) {
@Override
public Set getSpecialCharacters() {
- return Collections.singleton('~');
+ return Set.of('~');
}
});
}
diff --git a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughHtmlNodeRenderer.java b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughHtmlNodeRenderer.java
index 4dd0de39b..b1a82cb03 100644
--- a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughHtmlNodeRenderer.java
+++ b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughHtmlNodeRenderer.java
@@ -1,10 +1,9 @@
package org.commonmark.ext.gfm.strikethrough.internal;
-import org.commonmark.renderer.html.HtmlWriter;
-import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.node.Node;
+import org.commonmark.renderer.html.HtmlNodeRendererContext;
+import org.commonmark.renderer.html.HtmlWriter;
-import java.util.Collections;
import java.util.Map;
public class StrikethroughHtmlNodeRenderer extends StrikethroughNodeRenderer {
@@ -19,7 +18,7 @@ public StrikethroughHtmlNodeRenderer(HtmlNodeRendererContext context) {
@Override
public void render(Node node) {
- Map attributes = context.extendAttributes(node, "del", Collections.emptyMap());
+ Map attributes = context.extendAttributes(node, "del", Map.of());
html.tag("del", attributes);
renderChildren(node);
html.tag("/del");
diff --git a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughNodeRenderer.java b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughNodeRenderer.java
index 4f3a12618..18ed21887 100644
--- a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughNodeRenderer.java
+++ b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/internal/StrikethroughNodeRenderer.java
@@ -4,13 +4,12 @@
import org.commonmark.node.Node;
import org.commonmark.renderer.NodeRenderer;
-import java.util.Collections;
import java.util.Set;
abstract class StrikethroughNodeRenderer implements NodeRenderer {
@Override
public Set> getNodeTypes() {
- return Collections.>singleton(Strikethrough.class);
+ return Set.of(Strikethrough.class);
}
}
diff --git a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
index 96df48cec..c497a4db3 100644
--- a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
+++ b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
@@ -3,16 +3,15 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class StrikethroughMarkdownRendererTest {
- private static final Set EXTENSIONS = Collections.singleton(StrikethroughExtension.create());
+ private static final Set EXTENSIONS = Set.of(StrikethroughExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(EXTENSIONS).build();
@@ -31,6 +30,6 @@ protected String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java
index 4b907cf41..f1199b521 100644
--- a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java
+++ b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java
@@ -7,31 +7,27 @@
import org.commonmark.testutil.TestResources;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public class StrikethroughSpecTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(StrikethroughExtension.create());
+ private static final Set EXTENSIONS = Set.of(StrikethroughExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
- private final Example example;
+ @Parameter
+ Example example;
- public StrikethroughSpecTest(Example example) {
- this.example = example;
- }
-
- @Parameters(name = "{0}")
- public static List data() {
- return ExampleReader.readExampleObjects(TestResources.getGfmSpec(), "strikethrough");
+ static List data() {
+ return ExampleReader.readExamples(TestResources.getGfmSpec(), "strikethrough");
}
@Test
diff --git a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java
index d8a754c72..c29391cdd 100644
--- a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java
+++ b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java
@@ -12,17 +12,16 @@
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.renderer.text.TextContentRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Arrays;
+import java.util.List;
import java.util.Set;
-import static java.util.Collections.singleton;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class StrikethroughTest extends RenderingTestCase {
- private static final Set EXTENSIONS = singleton(StrikethroughExtension.create());
+ private static final Set EXTENSIONS = Set.of(StrikethroughExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer HTML_RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
private static final TextContentRenderer CONTENT_RENDERER = TextContentRenderer.builder()
@@ -85,27 +84,27 @@ public void insideBlockQuote() {
public void delimited() {
Node document = PARSER.parse("~~foo~~");
Strikethrough strikethrough = (Strikethrough) document.getFirstChild().getFirstChild();
- assertEquals("~~", strikethrough.getOpeningDelimiter());
- assertEquals("~~", strikethrough.getClosingDelimiter());
+ assertThat(strikethrough.getOpeningDelimiter()).isEqualTo("~~");
+ assertThat(strikethrough.getClosingDelimiter()).isEqualTo("~~");
}
@Test
public void textContentRenderer() {
Node document = PARSER.parse("~~foo~~");
- assertEquals("/foo/", CONTENT_RENDERER.render(document));
+ assertThat(CONTENT_RENDERER.render(document)).isEqualTo("/foo/");
}
@Test
public void requireTwoTildesOption() {
Parser parser = Parser.builder()
- .extensions(singleton(StrikethroughExtension.builder()
+ .extensions(Set.of(StrikethroughExtension.builder()
.requireTwoTildes(true)
.build()))
.customDelimiterProcessor(new SubscriptDelimiterProcessor())
.build();
Node document = parser.parse("~foo~ ~~bar~~");
- assertEquals("(sub)foo(/sub) /bar/", CONTENT_RENDERER.render(document));
+ assertThat(CONTENT_RENDERER.render(document)).isEqualTo("(sub)foo(/sub) /bar/");
}
@Test
@@ -118,8 +117,7 @@ public void sourceSpans() {
Node document = parser.parse("hey ~~there~~\n");
Paragraph block = (Paragraph) document.getFirstChild();
Node strikethrough = block.getLastChild();
- assertEquals(Arrays.asList(SourceSpan.of(0, 4, 9)),
- strikethrough.getSourceSpans());
+ assertThat(strikethrough.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 9)));
}
@Override
diff --git a/commonmark-ext-gfm-tables/.settings/org.eclipse.core.runtime.prefs b/commonmark-ext-gfm-tables/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 5a0ad22d2..000000000
--- a/commonmark-ext-gfm-tables/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/commonmark-ext-gfm-tables/.settings/org.eclipse.jdt.core.prefs b/commonmark-ext-gfm-tables/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 3c0d27c8f..000000000
--- a/commonmark-ext-gfm-tables/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,290 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.7
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=0
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
-org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=true
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
-org.eclipse.jdt.core.formatter.comment.line_length=120
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=2
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
-org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=false
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=120
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
-org.eclipse.jdt.core.formatter.tabulation.char=space
-org.eclipse.jdt.core.formatter.tabulation.size=4
-org.eclipse.jdt.core.formatter.use_on_off_tags=false
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
-org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml
index 5c031287e..5bd323168 100644
--- a/commonmark-ext-gfm-tables/pom.xml
+++ b/commonmark-ext-gfm-tables/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-gfm-tables
diff --git a/commonmark-ext-gfm-tables/src/main/java/module-info.java b/commonmark-ext-gfm-tables/src/main/java/module-info.java
index 472c84c3d..7e6d2629c 100644
--- a/commonmark-ext-gfm-tables/src/main/java/module-info.java
+++ b/commonmark-ext-gfm-tables/src/main/java/module-info.java
@@ -1,5 +1,5 @@
module org.commonmark.ext.gfm.tables {
exports org.commonmark.ext.gfm.tables;
- requires org.commonmark;
+ requires transitive org.commonmark;
}
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java
index d18c38283..f754b8276 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java
@@ -17,7 +17,6 @@
import org.commonmark.renderer.text.TextContentNodeRendererFactory;
import org.commonmark.renderer.text.TextContentRenderer;
-import java.util.Collections;
import java.util.Set;
/**
@@ -78,7 +77,7 @@ public NodeRenderer create(MarkdownNodeRendererContext context) {
@Override
public Set getSpecialCharacters() {
- return Collections.singleton('|');
+ return Set.of('|');
}
});
}
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java
index 0dfdd3159..57af128d8 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java
@@ -274,17 +274,17 @@ public static class Factory extends AbstractBlockParserFactory {
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
List paragraphLines = matchedBlockParser.getParagraphLines().getLines();
- if (paragraphLines.size() == 1 && Characters.find('|', paragraphLines.get(0).getContent(), 0) != -1) {
+ if (paragraphLines.size() >= 1 && Characters.find('|', paragraphLines.get(paragraphLines.size() - 1).getContent(), 0) != -1) {
SourceLine line = state.getLine();
SourceLine separatorLine = line.substring(state.getIndex(), line.getContent().length());
List columns = parseSeparator(separatorLine.getContent());
if (columns != null && !columns.isEmpty()) {
- SourceLine paragraph = paragraphLines.get(0);
+ SourceLine paragraph = paragraphLines.get(paragraphLines.size() - 1);
List headerCells = split(paragraph);
if (columns.size() >= headerCells.size()) {
return BlockStart.of(new TableBlockParser(columns, paragraph))
.atIndex(state.getIndex())
- .replaceActiveBlockParser();
+ .replaceParagraphLines(1);
}
}
}
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java
index a1de50aac..966c4c151 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java
@@ -5,7 +5,6 @@
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.renderer.html.HtmlWriter;
-import java.util.Collections;
import java.util.Map;
public class TableHtmlNodeRenderer extends TableNodeRenderer {
@@ -18,6 +17,7 @@ public TableHtmlNodeRenderer(HtmlNodeRendererContext context) {
this.context = context;
}
+ @Override
protected void renderBlock(TableBlock tableBlock) {
htmlWriter.line();
htmlWriter.tag("table", getAttributes(tableBlock, "table"));
@@ -26,6 +26,7 @@ protected void renderBlock(TableBlock tableBlock) {
htmlWriter.line();
}
+ @Override
protected void renderHead(TableHead tableHead) {
htmlWriter.line();
htmlWriter.tag("thead", getAttributes(tableHead, "thead"));
@@ -34,6 +35,7 @@ protected void renderHead(TableHead tableHead) {
htmlWriter.line();
}
+ @Override
protected void renderBody(TableBody tableBody) {
htmlWriter.line();
htmlWriter.tag("tbody", getAttributes(tableBody, "tbody"));
@@ -42,6 +44,7 @@ protected void renderBody(TableBody tableBody) {
htmlWriter.line();
}
+ @Override
protected void renderRow(TableRow tableRow) {
htmlWriter.line();
htmlWriter.tag("tr", getAttributes(tableRow, "tr"));
@@ -50,6 +53,7 @@ protected void renderRow(TableRow tableRow) {
htmlWriter.line();
}
+ @Override
protected void renderCell(TableCell tableCell) {
String tagName = tableCell.isHeader() ? "th" : "td";
htmlWriter.line();
@@ -60,14 +64,14 @@ protected void renderCell(TableCell tableCell) {
}
private Map getAttributes(Node node, String tagName) {
- return context.extendAttributes(node, tagName, Collections.emptyMap());
+ return context.extendAttributes(node, tagName, Map.of());
}
private Map getCellAttributes(TableCell tableCell, String tagName) {
if (tableCell.getAlignment() != null) {
- return context.extendAttributes(tableCell, tagName, Collections.singletonMap("align", getAlignValue(tableCell.getAlignment())));
+ return context.extendAttributes(tableCell, tagName, Map.of("align", getAlignValue(tableCell.getAlignment())));
} else {
- return context.extendAttributes(tableCell, tagName, Collections.emptyMap());
+ return context.extendAttributes(tableCell, tagName, Map.of());
}
}
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java
index 2fe60f80d..b0705f579 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java
@@ -2,7 +2,6 @@
import org.commonmark.ext.gfm.tables.*;
import org.commonmark.node.Node;
-import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.markdown.MarkdownNodeRendererContext;
import org.commonmark.renderer.markdown.MarkdownWriter;
import org.commonmark.text.AsciiMatcher;
@@ -13,7 +12,7 @@
/**
* The Table node renderer that is needed for rendering GFM tables (GitHub Flavored Markdown) to text content.
*/
-public class TableMarkdownNodeRenderer extends TableNodeRenderer implements NodeRenderer {
+public class TableMarkdownNodeRenderer extends TableNodeRenderer {
private final MarkdownWriter writer;
private final MarkdownNodeRendererContext context;
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java
index 93478a30b..2982e1518 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java
@@ -1,28 +1,22 @@
package org.commonmark.ext.gfm.tables.internal;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.commonmark.ext.gfm.tables.TableBlock;
-import org.commonmark.ext.gfm.tables.TableBody;
-import org.commonmark.ext.gfm.tables.TableCell;
-import org.commonmark.ext.gfm.tables.TableHead;
-import org.commonmark.ext.gfm.tables.TableRow;
+import org.commonmark.ext.gfm.tables.*;
import org.commonmark.node.Node;
import org.commonmark.renderer.NodeRenderer;
+import java.util.Set;
+
abstract class TableNodeRenderer implements NodeRenderer {
@Override
public Set> getNodeTypes() {
- return new HashSet<>(Arrays.asList(
+ return Set.of(
TableBlock.class,
TableHead.class,
TableBody.class,
TableRow.class,
TableCell.class
- ));
+ );
}
@Override
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java
index 94b0e8665..0ba6894b5 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java
@@ -22,49 +22,46 @@ public TableTextContentNodeRenderer(TextContentNodeRendererContext context) {
this.context = context;
}
+ @Override
protected void renderBlock(TableBlock tableBlock) {
+ // Render rows tight
+ textContentWriter.pushTight(true);
renderChildren(tableBlock);
- if (tableBlock.getNext() != null) {
- textContentWriter.write("\n");
- }
+ textContentWriter.popTight();
+ textContentWriter.block();
}
+ @Override
protected void renderHead(TableHead tableHead) {
renderChildren(tableHead);
}
+ @Override
protected void renderBody(TableBody tableBody) {
renderChildren(tableBody);
}
+ @Override
protected void renderRow(TableRow tableRow) {
- textContentWriter.line();
renderChildren(tableRow);
- textContentWriter.line();
+ textContentWriter.block();
}
+ @Override
protected void renderCell(TableCell tableCell) {
renderChildren(tableCell);
- textContentWriter.write('|');
- textContentWriter.whitespace();
- }
-
- private void renderLastCell(TableCell tableCell) {
- renderChildren(tableCell);
+ // For the last cell in row, don't render the delimiter
+ if (tableCell.getNext() != null) {
+ textContentWriter.write('|');
+ textContentWriter.whitespace();
+ }
}
private void renderChildren(Node parent) {
Node node = parent.getFirstChild();
while (node != null) {
Node next = node.getNext();
-
- // For last cell in row, we dont render the delimiter.
- if (node instanceof TableCell && next == null) {
- renderLastCell((TableCell) node);
- } else {
- context.render(node);
- }
-
+ context.render(node);
node = next;
}
}
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java
index 1db917d08..85c11206c 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java
@@ -3,16 +3,15 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class TableMarkdownRendererTest {
- private static final Set EXTENSIONS = Collections.singleton(TablesExtension.create());
+ private static final Set EXTENSIONS = Set.of(TablesExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(EXTENSIONS).build();
@@ -71,6 +70,6 @@ protected String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java
index 00fc61401..e7f3db4d1 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java
@@ -7,32 +7,27 @@
import org.commonmark.testutil.TestResources;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public class TablesSpecTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(TablesExtension.create());
+ private static final Set EXTENSIONS = Set.of(TablesExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
- private final Example example;
+ @Parameter
+ Example example;
- public TablesSpecTest(Example example) {
- this.example = example;
- }
-
- @Parameters(name = "{0}")
- public static List data() {
- return ExampleReader.readExampleObjects(TestResources.getGfmSpec(), "table");
+ static List data() {
+ return ExampleReader.readExamples(TestResources.getGfmSpec(), "table");
}
@Test
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java
index b03714d9a..3f4b37d54 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java
@@ -1,8 +1,7 @@
package org.commonmark.ext.gfm.tables;
import org.commonmark.Extension;
-import org.commonmark.node.Node;
-import org.commonmark.node.SourceSpan;
+import org.commonmark.node.*;
import org.commonmark.parser.IncludeSourceSpans;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
@@ -10,20 +9,17 @@
import org.commonmark.renderer.html.AttributeProviderFactory;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Set;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
+import static org.assertj.core.api.Assertions.assertThat;
public class TablesTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(TablesExtension.create());
+ private static final Set EXTENSIONS = Set.of(TablesExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
@@ -82,11 +78,6 @@ public void separatorNeedsPipes() {
assertRendering("Abc|Def\n|--- ---", "Abc|Def\n|--- ---
\n");
}
- @Test
- public void headerMustBeOneLine() {
- assertRendering("No\nAbc|Def\n---|---", "No\nAbc|Def\n---|---
\n");
- }
-
@Test
public void oneHeadNoBody() {
assertRendering("Abc|Def\n---|---", "\n" +
@@ -705,6 +696,26 @@ public void danglingPipe() {
"|
\n");
}
+ @Test
+ public void interruptsParagraph() {
+ assertRendering("text\n" +
+ "|a |\n" +
+ "|---|\n" +
+ "|b |", "text
\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "a \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ "b \n" +
+ " \n" +
+ " \n" +
+ "
\n");
+ }
+
@Test
public void attributeProviderIsApplied() {
AttributeProviderFactory factory = new AttributeProviderFactory() {
@@ -733,7 +744,7 @@ public void setAttributes(Node node, String tagName, Map attribu
.extensions(EXTENSIONS)
.build();
String rendered = renderer.render(PARSER.parse("Abc|Def\n---|---\n1|2"));
- assertThat(rendered, is("\n" +
+ assertThat(rendered).isEqualTo("\n" +
"\n" +
"\n" +
"Abc \n" +
@@ -746,7 +757,7 @@ public void setAttributes(Node node, String tagName, Map attribu
"2 \n" +
" \n" +
"\n" +
- "
\n"));
+ "
\n");
}
@Test
@@ -769,7 +780,7 @@ public void setAttributes(Node node, String tagName, Map attribu
.extensions(EXTENSIONS)
.build();
String rendered = renderer.render(PARSER.parse("Abc|Def\n-----|---\n1|2"));
- assertThat(rendered, is("\n" +
+ assertThat(rendered).isEqualTo("\n" +
"\n" +
"\n" +
"Abc \n" +
@@ -782,7 +793,7 @@ public void setAttributes(Node node, String tagName, Map attribu
"2 \n" +
" \n" +
"\n" +
- "
\n"));
+ "
\n");
}
@Test
@@ -794,49 +805,78 @@ public void sourceSpans() {
Node document = parser.parse("Abc|Def\n---|---\n|1|2\n 3|four|\n|||\n");
TableBlock block = (TableBlock) document.getFirstChild();
- assertEquals(Arrays.asList(SourceSpan.of(0, 0, 7), SourceSpan.of(1, 0, 7),
- SourceSpan.of(2, 0, 4), SourceSpan.of(3, 0, 8), SourceSpan.of(4, 0, 3)),
- block.getSourceSpans());
+ assertThat(block.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7), SourceSpan.of(1, 0, 8, 7),
+ SourceSpan.of(2, 0, 16, 4), SourceSpan.of(3, 0, 21, 8), SourceSpan.of(4, 0, 30, 3)));
TableHead head = (TableHead) block.getFirstChild();
- assertEquals(Arrays.asList(SourceSpan.of(0, 0, 7)), head.getSourceSpans());
+ assertThat(head.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7)));
TableRow headRow = (TableRow) head.getFirstChild();
- assertEquals(Arrays.asList(SourceSpan.of(0, 0, 7)), headRow.getSourceSpans());
+ assertThat(headRow.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7)));
TableCell headRowCell1 = (TableCell) headRow.getFirstChild();
TableCell headRowCell2 = (TableCell) headRow.getLastChild();
- assertEquals(Arrays.asList(SourceSpan.of(0, 0, 3)), headRowCell1.getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(0, 0, 3)), headRowCell1.getFirstChild().getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(0, 4, 3)), headRowCell2.getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(0, 4, 3)), headRowCell2.getFirstChild().getSourceSpans());
+ assertThat(headRowCell1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 3)));
+ assertThat(headRowCell1.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 3)));
+ assertThat(headRowCell2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 3)));
+ assertThat(headRowCell2.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 3)));
TableBody body = (TableBody) block.getLastChild();
- assertEquals(Arrays.asList(SourceSpan.of(2, 0, 4), SourceSpan.of(3, 0, 8), SourceSpan.of(4, 0, 3)), body.getSourceSpans());
+ assertThat(body.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 0, 16, 4), SourceSpan.of(3, 0, 21, 8), SourceSpan.of(4, 0, 30, 3)));
TableRow bodyRow1 = (TableRow) body.getFirstChild();
- assertEquals(Arrays.asList(SourceSpan.of(2, 0, 4)), bodyRow1.getSourceSpans());
+ assertThat(bodyRow1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 0, 16, 4)));
TableCell bodyRow1Cell1 = (TableCell) bodyRow1.getFirstChild();
TableCell bodyRow1Cell2 = (TableCell) bodyRow1.getLastChild();
- assertEquals(Arrays.asList(SourceSpan.of(2, 1, 1)), bodyRow1Cell1.getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(2, 1, 1)), bodyRow1Cell1.getFirstChild().getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(2, 3, 1)), bodyRow1Cell2.getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(2, 3, 1)), bodyRow1Cell2.getFirstChild().getSourceSpans());
+ assertThat(bodyRow1Cell1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 1, 17, 1)));
+ assertThat(bodyRow1Cell1.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 1, 17, 1)));
+ assertThat(bodyRow1Cell2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 3, 19, 1)));
+ assertThat(bodyRow1Cell2.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 3, 19, 1)));
TableRow bodyRow2 = (TableRow) body.getFirstChild().getNext();
- assertEquals(Arrays.asList(SourceSpan.of(3, 0, 8)), bodyRow2.getSourceSpans());
+ assertThat(bodyRow2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 0, 21, 8)));
TableCell bodyRow2Cell1 = (TableCell) bodyRow2.getFirstChild();
TableCell bodyRow2Cell2 = (TableCell) bodyRow2.getLastChild();
- assertEquals(Arrays.asList(SourceSpan.of(3, 1, 1)), bodyRow2Cell1.getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(3, 1, 1)), bodyRow2Cell1.getFirstChild().getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(3, 3, 4)), bodyRow2Cell2.getSourceSpans());
- assertEquals(Arrays.asList(SourceSpan.of(3, 3, 4)), bodyRow2Cell2.getFirstChild().getSourceSpans());
+ assertThat(bodyRow2Cell1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 1, 22, 1)));
+ assertThat(bodyRow2Cell1.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 1, 22, 1)));
+ assertThat(bodyRow2Cell2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 3, 24, 4)));
+ assertThat(bodyRow2Cell2.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 3, 24, 4)));
TableRow bodyRow3 = (TableRow) body.getLastChild();
- assertEquals(Arrays.asList(SourceSpan.of(4, 0, 3)), bodyRow3.getSourceSpans());
+ assertThat(bodyRow3.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(4, 0, 30, 3)));
TableCell bodyRow3Cell1 = (TableCell) bodyRow3.getFirstChild();
TableCell bodyRow3Cell2 = (TableCell) bodyRow3.getLastChild();
- assertEquals(Collections.emptyList(), bodyRow3Cell1.getSourceSpans());
- assertEquals(Collections.emptyList(), bodyRow3Cell2.getSourceSpans());
+ assertThat(bodyRow3Cell1.getSourceSpans()).isEqualTo(List.of());
+ assertThat(bodyRow3Cell2.getSourceSpans()).isEqualTo(List.of());
+ }
+
+ @Test
+ public void sourceSpansWhenInterrupting() {
+ var parser = Parser.builder()
+ .extensions(EXTENSIONS)
+ .includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES)
+ .build();
+ var document = parser.parse("a\n" +
+ "bc\n" +
+ "|de|\n" +
+ "|---|\n" +
+ "|fg|");
+
+ var paragraph = (Paragraph) document.getFirstChild();
+ var text = (Text) paragraph.getFirstChild();
+ assertThat(text.getLiteral()).isEqualTo("a");
+ assertThat(text.getNext()).isInstanceOf(SoftLineBreak.class);
+ var text2 = (Text) text.getNext().getNext();
+ assertThat(text2.getLiteral()).isEqualTo("bc");
+
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(
+ SourceSpan.of(0, 0, 0, 1),
+ SourceSpan.of(1, 0, 2, 2)));
+
+ var table = (TableBlock) document.getLastChild();
+ assertThat(table.getSourceSpans()).isEqualTo(List.of(
+ SourceSpan.of(2, 0, 5, 4),
+ SourceSpan.of(3, 0, 10, 5),
+ SourceSpan.of(4, 0, 16, 4)));
}
@Override
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java
index 6d859f1c9..966f097fd 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java
@@ -2,138 +2,165 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
+import org.commonmark.renderer.text.LineBreakRendering;
import org.commonmark.renderer.text.TextContentRenderer;
-import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.commonmark.testutil.Asserts;
+import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.Set;
-public class TablesTextContentTest extends RenderingTestCase {
+public class TablesTextContentTest {
- private static final Set EXTENSIONS = Collections.singleton(TablesExtension.create());
+ private static final Set EXTENSIONS = Set.of(TablesExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final TextContentRenderer RENDERER = TextContentRenderer.builder().extensions(EXTENSIONS).build();
+ private static final TextContentRenderer COMPACT_RENDERER = TextContentRenderer.builder().extensions(EXTENSIONS).build();
+ private static final TextContentRenderer SEPARATE_RENDERER = TextContentRenderer.builder().extensions(EXTENSIONS)
+ .lineBreakRendering(LineBreakRendering.SEPARATE_BLOCKS).build();
+ private static final TextContentRenderer STRIPPED_RENDERER = TextContentRenderer.builder().extensions(EXTENSIONS)
+ .lineBreakRendering(LineBreakRendering.STRIP).build();
+
@Test
public void oneHeadNoBody() {
- assertRendering("Abc|Def\n---|---", "Abc| Def\n");
+ assertCompact("Abc|Def\n---|---", "Abc| Def");
}
@Test
public void oneColumnOneHeadNoBody() {
- String expected = "Abc\n";
- assertRendering("|Abc\n|---\n", expected);
- assertRendering("|Abc|\n|---|\n", expected);
- assertRendering("Abc|\n---|\n", expected);
+ String expected = "Abc";
+ assertCompact("|Abc\n|---\n", expected);
+ assertCompact("|Abc|\n|---|\n", expected);
+ assertCompact("Abc|\n---|\n", expected);
// Pipe required on separator
- assertRendering("|Abc\n---\n", "|Abc");
+ assertCompact("|Abc\n---\n", "|Abc");
// Pipe required on head
- assertRendering("Abc\n|---\n", "Abc\n|---");
+ assertCompact("Abc\n|---\n", "Abc\n|---");
}
@Test
public void oneColumnOneHeadOneBody() {
- String expected = "Abc\n1\n";
- assertRendering("|Abc\n|---\n|1", expected);
- assertRendering("|Abc|\n|---|\n|1|", expected);
- assertRendering("Abc|\n---|\n1|", expected);
+ String expected = "Abc\n1";
+ assertCompact("|Abc\n|---\n|1", expected);
+ assertCompact("|Abc|\n|---|\n|1|", expected);
+ assertCompact("Abc|\n---|\n1|", expected);
// Pipe required on separator
- assertRendering("|Abc\n---\n|1", "|Abc\n|1");
+ assertCompact("|Abc\n---\n|1", "|Abc\n|1");
}
@Test
public void oneHeadOneBody() {
- assertRendering("Abc|Def\n---|---\n1|2", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n---|---\n1|2", "Abc| Def\n1| 2");
}
@Test
public void separatorMustNotHaveLessPartsThanHead() {
- assertRendering("Abc|Def|Ghi\n---|---\n1|2|3", "Abc|Def|Ghi\n---|---\n1|2|3");
+ assertCompact("Abc|Def|Ghi\n---|---\n1|2|3", "Abc|Def|Ghi\n---|---\n1|2|3");
}
@Test
public void padding() {
- assertRendering(" Abc | Def \n --- | --- \n 1 | 2 ", "Abc| Def\n1| 2\n");
+ assertCompact(" Abc | Def \n --- | --- \n 1 | 2 ", "Abc| Def\n1| 2");
}
@Test
public void paddingWithCodeBlockIndentation() {
- assertRendering("Abc|Def\n---|---\n 1|2", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n---|---\n 1|2", "Abc| Def\n1| 2");
}
@Test
public void pipesOnOutside() {
- assertRendering("|Abc|Def|\n|---|---|\n|1|2|", "Abc| Def\n1| 2\n");
+ assertCompact("|Abc|Def|\n|---|---|\n|1|2|", "Abc| Def\n1| 2");
}
@Test
public void inlineElements() {
- assertRendering("*Abc*|Def\n---|---\n1|2", "Abc| Def\n1| 2\n");
+ assertCompact("*Abc*|Def\n---|---\n1|2", "Abc| Def\n1| 2");
}
@Test
public void escapedPipe() {
- assertRendering("Abc|Def\n---|---\n1\\|2|20", "Abc| Def\n1|2| 20\n");
+ assertCompact("Abc|Def\n---|---\n1\\|2|20", "Abc| Def\n1|2| 20");
}
@Test
public void alignLeft() {
- assertRendering("Abc|Def\n:---|---\n1|2", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n:---|---\n1|2", "Abc| Def\n1| 2");
}
@Test
public void alignRight() {
- assertRendering("Abc|Def\n---:|---\n1|2", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n---:|---\n1|2", "Abc| Def\n1| 2");
}
@Test
public void alignCenter() {
- assertRendering("Abc|Def\n:---:|---\n1|2", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n:---:|---\n1|2", "Abc| Def\n1| 2");
}
@Test
public void alignCenterSecond() {
- assertRendering("Abc|Def\n---|:---:\n1|2", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n---|:---:\n1|2", "Abc| Def\n1| 2");
}
@Test
public void alignLeftWithSpaces() {
- assertRendering("Abc|Def\n :--- |---\n1|2", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n :--- |---\n1|2", "Abc| Def\n1| 2");
}
@Test
public void alignmentMarkerMustBeNextToDashes() {
- assertRendering("Abc|Def\n: ---|---", "Abc|Def\n: ---|---");
- assertRendering("Abc|Def\n--- :|---", "Abc|Def\n--- :|---");
- assertRendering("Abc|Def\n---|: ---", "Abc|Def\n---|: ---");
- assertRendering("Abc|Def\n---|--- :", "Abc|Def\n---|--- :");
+ assertCompact("Abc|Def\n: ---|---", "Abc|Def\n: ---|---");
+ assertCompact("Abc|Def\n--- :|---", "Abc|Def\n--- :|---");
+ assertCompact("Abc|Def\n---|: ---", "Abc|Def\n---|: ---");
+ assertCompact("Abc|Def\n---|--- :", "Abc|Def\n---|--- :");
}
@Test
public void bodyCanNotHaveMoreColumnsThanHead() {
- assertRendering("Abc|Def\n---|---\n1|2|3", "Abc| Def\n1| 2\n");
+ assertCompact("Abc|Def\n---|---\n1|2|3", "Abc| Def\n1| 2");
}
@Test
public void bodyWithFewerColumnsThanHeadResultsInEmptyCells() {
- assertRendering("Abc|Def|Ghi\n---|---|---\n1|2", "Abc| Def| Ghi\n1| 2| \n");
+ assertCompact("Abc|Def|Ghi\n---|---|---\n1|2", "Abc| Def| Ghi\n1| 2| ");
}
@Test
public void insideBlockQuote() {
- assertRendering("> Abc|Def\n> ---|---\n> 1|2", "«\nAbc| Def\n1| 2\n»");
+ assertCompact("> Abc|Def\n> ---|---\n> 1|2", "«Abc| Def\n1| 2»");
}
@Test
public void tableWithLazyContinuationLine() {
- assertRendering("Abc|Def\n---|---\n1|2\nlazy", "Abc| Def\n1| 2\nlazy| \n");
+ assertCompact("Abc|Def\n---|---\n1|2\nlazy", "Abc| Def\n1| 2\nlazy| ");
+ }
+
+ @Test
+ public void tableBetweenOtherBlocks() {
+ var s = "Foo\n\nAbc|Def\n---|---\n1|2\n\nBar";
+ assertCompact(s, "Foo\nAbc| Def\n1| 2\nBar");
+ assertSeparate(s, "Foo\n\nAbc| Def\n1| 2\n\nBar");
+ assertStripped(s, "Foo Abc| Def 1| 2 Bar");
+ }
+
+ private void assertCompact(String source, String expected) {
+ var doc = PARSER.parse(source);
+ var actualRendering = COMPACT_RENDERER.render(doc);
+ Asserts.assertRendering(source, expected, actualRendering);
+ }
+
+ private void assertSeparate(String source, String expected) {
+ var doc = PARSER.parse(source);
+ var actualRendering = SEPARATE_RENDERER.render(doc);
+ Asserts.assertRendering(source, expected, actualRendering);
}
- @Override
- protected String render(String source) {
- return RENDERER.render(PARSER.parse(source));
+ private void assertStripped(String source, String expected) {
+ var doc = PARSER.parse(source);
+ var actualRendering = STRIPPED_RENDERER.render(doc);
+ Asserts.assertRendering(source, expected, actualRendering);
}
}
diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml
index a7b16ef1c..26d2d19b1 100644
--- a/commonmark-ext-heading-anchor/pom.xml
+++ b/commonmark-ext-heading-anchor/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-heading-anchor
diff --git a/commonmark-ext-heading-anchor/src/main/java/module-info.java b/commonmark-ext-heading-anchor/src/main/java/module-info.java
index 3b94c75ec..2369323a6 100644
--- a/commonmark-ext-heading-anchor/src/main/java/module-info.java
+++ b/commonmark-ext-heading-anchor/src/main/java/module-info.java
@@ -1,5 +1,5 @@
module org.commonmark.ext.heading.anchor {
exports org.commonmark.ext.heading.anchor;
- requires org.commonmark;
+ requires transitive org.commonmark;
}
diff --git a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java
index 5a7f47cd3..438a3a9bd 100644
--- a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java
+++ b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java
@@ -3,12 +3,11 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Arrays;
+import java.util.List;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.assertj.core.api.Assertions.assertThat;
public class HeadingAnchorConfigurationTest {
@@ -21,34 +20,34 @@ private HtmlRenderer buildRenderer(String defaultId, String prefix, String suffi
.idSuffix(suffix)
.build();
return HtmlRenderer.builder()
- .extensions(Arrays.asList(ext))
+ .extensions(List.of(ext))
.build();
}
@Test
public void testDefaultConfigurationHasNoAdditions() {
HtmlRenderer renderer = HtmlRenderer.builder()
- .extensions(Arrays.asList(HeadingAnchorExtension.create()))
+ .extensions(List.of(HeadingAnchorExtension.create()))
.build();
- assertThat(doRender(renderer, "# "), equalTo(" \n"));
+ assertThat(doRender(renderer, "# ")).isEqualTo(" \n");
}
@Test
public void testDefaultIdWhenNoTextOnHeader() {
HtmlRenderer renderer = buildRenderer("defid", "", "");
- assertThat(doRender(renderer, "# "), equalTo(" \n"));
+ assertThat(doRender(renderer, "# ")).isEqualTo(" \n");
}
@Test
public void testPrefixAddedToHeader() {
HtmlRenderer renderer = buildRenderer("", "pre-", "");
- assertThat(doRender(renderer, "# text"), equalTo("text \n"));
+ assertThat(doRender(renderer, "# text")).isEqualTo("text \n");
}
@Test
public void testSuffixAddedToHeader() {
HtmlRenderer renderer = buildRenderer("", "", "-post");
- assertThat(doRender(renderer, "# text"), equalTo("text \n"));
+ assertThat(doRender(renderer, "# text")).isEqualTo("text \n");
}
private String doRender(HtmlRenderer renderer, String text) {
diff --git a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java
index 821aa9a84..3149542e3 100644
--- a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java
+++ b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java
@@ -4,14 +4,13 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.Set;
public class HeadingAnchorTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(HeadingAnchorExtension.create());
+ private static final Set EXTENSIONS = Set.of(HeadingAnchorExtension.create());
private static final Parser PARSER = Parser.builder().build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml
index ed654760f..e646bc3fd 100644
--- a/commonmark-ext-image-attributes/pom.xml
+++ b/commonmark-ext-image-attributes/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-image-attributes
diff --git a/commonmark-ext-image-attributes/src/main/java/module-info.java b/commonmark-ext-image-attributes/src/main/java/module-info.java
index 171281091..42d04a358 100644
--- a/commonmark-ext-image-attributes/src/main/java/module-info.java
+++ b/commonmark-ext-image-attributes/src/main/java/module-info.java
@@ -1,5 +1,5 @@
module org.commonmark.ext.image.attributes {
exports org.commonmark.ext.image.attributes;
- requires org.commonmark;
+ requires transitive org.commonmark;
}
diff --git a/commonmark-ext-image-attributes/src/main/java/org/commonmark/ext/image/attributes/internal/ImageAttributesDelimiterProcessor.java b/commonmark-ext-image-attributes/src/main/java/org/commonmark/ext/image/attributes/internal/ImageAttributesDelimiterProcessor.java
index a584948e3..a335ccadc 100644
--- a/commonmark-ext-image-attributes/src/main/java/org/commonmark/ext/image/attributes/internal/ImageAttributesDelimiterProcessor.java
+++ b/commonmark-ext-image-attributes/src/main/java/org/commonmark/ext/image/attributes/internal/ImageAttributesDelimiterProcessor.java
@@ -13,8 +13,7 @@
public class ImageAttributesDelimiterProcessor implements DelimiterProcessor {
// Only allow a defined set of attributes to be used.
- private static final Set SUPPORTED_ATTRIBUTES = Collections.unmodifiableSet(
- new HashSet<>(Arrays.asList("width", "height")));
+ private static final Set SUPPORTED_ATTRIBUTES = Set.of("width", "height");
@Override
public char getOpeningCharacter() {
diff --git a/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java b/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java
index b7d8a84c3..3edf8497e 100644
--- a/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java
+++ b/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java
@@ -8,17 +8,16 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class ImageAttributesTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(ImageAttributesExtension.create());
+ private static final Set EXTENSIONS = Set.of(ImageAttributesExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
@@ -132,8 +131,7 @@ public void sourceSpans() {
Node document = parser.parse("x{height=3 width=4}\n");
Paragraph block = (Paragraph) document.getFirstChild();
Node text = block.getFirstChild();
- assertEquals(Arrays.asList(SourceSpan.of(0, 0, 19)),
- text.getSourceSpans());
+ assertThat(text.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 19)));
}
@Override
diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml
index 2e00c6c16..48481c073 100644
--- a/commonmark-ext-ins/pom.xml
+++ b/commonmark-ext-ins/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-ins
diff --git a/commonmark-ext-ins/src/main/java/module-info.java b/commonmark-ext-ins/src/main/java/module-info.java
index aa5c5e84c..fb96ea598 100644
--- a/commonmark-ext-ins/src/main/java/module-info.java
+++ b/commonmark-ext-ins/src/main/java/module-info.java
@@ -1,5 +1,5 @@
module org.commonmark.ext.ins {
exports org.commonmark.ext.ins;
- requires org.commonmark;
+ requires transitive org.commonmark;
}
diff --git a/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/InsExtension.java b/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/InsExtension.java
index 065719558..e8a53e59a 100644
--- a/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/InsExtension.java
+++ b/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/InsExtension.java
@@ -17,7 +17,6 @@
import org.commonmark.renderer.text.TextContentNodeRendererFactory;
import org.commonmark.renderer.text.TextContentRenderer;
-import java.util.Collections;
import java.util.Set;
/**
@@ -77,7 +76,7 @@ public NodeRenderer create(MarkdownNodeRendererContext context) {
public Set getSpecialCharacters() {
// We technically don't need to escape single occurrences of +, but that's all the extension API
// exposes currently.
- return Collections.singleton('+');
+ return Set.of('+');
}
});
}
diff --git a/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsHtmlNodeRenderer.java b/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsHtmlNodeRenderer.java
index 139a0b2cd..dcd05fd59 100644
--- a/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsHtmlNodeRenderer.java
+++ b/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsHtmlNodeRenderer.java
@@ -4,7 +4,6 @@
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.renderer.html.HtmlWriter;
-import java.util.Collections;
import java.util.Map;
public class InsHtmlNodeRenderer extends InsNodeRenderer {
@@ -19,7 +18,7 @@ public InsHtmlNodeRenderer(HtmlNodeRendererContext context) {
@Override
public void render(Node node) {
- Map attributes = context.extendAttributes(node, "ins", Collections.emptyMap());
+ Map attributes = context.extendAttributes(node, "ins", Map.of());
html.tag("ins", attributes);
renderChildren(node);
html.tag("/ins");
diff --git a/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsNodeRenderer.java b/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsNodeRenderer.java
index 0a44a2826..31f0a64ec 100644
--- a/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsNodeRenderer.java
+++ b/commonmark-ext-ins/src/main/java/org/commonmark/ext/ins/internal/InsNodeRenderer.java
@@ -4,13 +4,12 @@
import org.commonmark.node.Node;
import org.commonmark.renderer.NodeRenderer;
-import java.util.Collections;
import java.util.Set;
abstract class InsNodeRenderer implements NodeRenderer {
@Override
public Set> getNodeTypes() {
- return Collections.>singleton(Ins.class);
+ return Set.of(Ins.class);
}
}
diff --git a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java
index 16cefc7f1..6fc9ead67 100644
--- a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java
+++ b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java
@@ -3,16 +3,15 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class InsMarkdownRendererTest {
- private static final Set EXTENSIONS = Collections.singleton(InsExtension.create());
+ private static final Set EXTENSIONS = Set.of(InsExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(EXTENSIONS).build();
@@ -29,6 +28,6 @@ protected String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java
index c9591af5e..a5c91a395 100644
--- a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java
+++ b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java
@@ -9,17 +9,16 @@
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.renderer.text.TextContentRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class InsTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(InsExtension.create());
+ private static final Set EXTENSIONS = Set.of(InsExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
private static final TextContentRenderer CONTENT_RENDERER = TextContentRenderer.builder()
@@ -83,14 +82,14 @@ public void insideBlockQuote() {
public void delimited() {
Node document = PARSER.parse("++foo++");
Ins ins = (Ins) document.getFirstChild().getFirstChild();
- assertEquals("++", ins.getOpeningDelimiter());
- assertEquals("++", ins.getClosingDelimiter());
+ assertThat(ins.getOpeningDelimiter()).isEqualTo("++");
+ assertThat(ins.getClosingDelimiter()).isEqualTo("++");
}
@Test
public void textContentRenderer() {
Node document = PARSER.parse("++foo++");
- assertEquals("foo", CONTENT_RENDERER.render(document));
+ assertThat(CONTENT_RENDERER.render(document)).isEqualTo("foo");
}
@Test
@@ -103,8 +102,7 @@ public void sourceSpans() {
Node document = parser.parse("hey ++there++\n");
Paragraph block = (Paragraph) document.getFirstChild();
Node ins = block.getLastChild();
- assertEquals(Arrays.asList(SourceSpan.of(0, 4, 9)),
- ins.getSourceSpans());
+ assertThat(ins.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 9)));
}
@Override
diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml
index 68b56e950..4359f8707 100644
--- a/commonmark-ext-task-list-items/pom.xml
+++ b/commonmark-ext-task-list-items/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-task-list-items
diff --git a/commonmark-ext-task-list-items/src/main/java/module-info.java b/commonmark-ext-task-list-items/src/main/java/module-info.java
index 30134c51b..9528323ea 100644
--- a/commonmark-ext-task-list-items/src/main/java/module-info.java
+++ b/commonmark-ext-task-list-items/src/main/java/module-info.java
@@ -1,5 +1,5 @@
module org.commonmark.ext.task.list.items {
exports org.commonmark.ext.task.list.items;
- requires org.commonmark;
+ requires transitive org.commonmark;
}
diff --git a/commonmark-ext-task-list-items/src/main/java/org/commonmark/ext/task/list/items/internal/TaskListItemHtmlNodeRenderer.java b/commonmark-ext-task-list-items/src/main/java/org/commonmark/ext/task/list/items/internal/TaskListItemHtmlNodeRenderer.java
index f2b2215f6..331b301e9 100644
--- a/commonmark-ext-task-list-items/src/main/java/org/commonmark/ext/task/list/items/internal/TaskListItemHtmlNodeRenderer.java
+++ b/commonmark-ext-task-list-items/src/main/java/org/commonmark/ext/task/list/items/internal/TaskListItemHtmlNodeRenderer.java
@@ -6,7 +6,6 @@
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.renderer.html.HtmlWriter;
-import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -23,7 +22,7 @@ public TaskListItemHtmlNodeRenderer(HtmlNodeRendererContext context) {
@Override
public Set> getNodeTypes() {
- return Collections.>singleton(TaskListItemMarker.class);
+ return Set.of(TaskListItemMarker.class);
}
@Override
diff --git a/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java b/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java
index 3e6c6a259..0adc615a7 100644
--- a/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java
+++ b/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java
@@ -4,14 +4,13 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.Set;
public class TaskListItemsTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(TaskListItemsExtension.create());
+ private static final Set EXTENSIONS = Set.of(TaskListItemsExtension.create());
private static final String HTML_CHECKED = " ";
private static final String HTML_UNCHECKED = " ";
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml
index 7ecf7336d..e6822f771 100644
--- a/commonmark-ext-yaml-front-matter/pom.xml
+++ b/commonmark-ext-yaml-front-matter/pom.xml
@@ -4,7 +4,7 @@
commonmark-parent
org.commonmark
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-yaml-front-matter
diff --git a/commonmark-ext-yaml-front-matter/src/main/java/module-info.java b/commonmark-ext-yaml-front-matter/src/main/java/module-info.java
index 20d38fe0a..5f96c14ad 100644
--- a/commonmark-ext-yaml-front-matter/src/main/java/module-info.java
+++ b/commonmark-ext-yaml-front-matter/src/main/java/module-info.java
@@ -1,5 +1,5 @@
module org.commonmark.ext.front.matter {
exports org.commonmark.ext.front.matter;
- requires org.commonmark;
+ requires transitive org.commonmark;
}
diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java
index a5f203b97..db17d4a4e 100644
--- a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java
+++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java
@@ -6,18 +6,16 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
public class YamlFrontMatterTest extends RenderingTestCase {
- private static final Set EXTENSIONS = Collections.singleton(YamlFrontMatterExtension.create());
+ private static final Set EXTENSIONS = Set.of(YamlFrontMatterExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
@@ -32,10 +30,10 @@ public void simpleValue() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertEquals("hello", data.keySet().iterator().next());
- assertEquals(1, data.get("hello").size());
- assertEquals("world", data.get("hello").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data.keySet().iterator().next()).isEqualTo("hello");
+ assertThat(data.get("hello")).hasSize(1);
+ assertThat(data.get("hello").get(0)).isEqualTo("world");
assertRendering(input, rendered);
}
@@ -51,9 +49,9 @@ public void emptyValue() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertEquals("key", data.keySet().iterator().next());
- assertEquals(0, data.get("key").size());
+ assertThat(data).hasSize(1);
+ assertThat(data.keySet().iterator().next()).isEqualTo("key");
+ assertThat(data.get("key")).hasSize(0);
assertRendering(input, rendered);
}
@@ -71,11 +69,11 @@ public void listValues() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertTrue(data.containsKey("list"));
- assertEquals(2, data.get("list").size());
- assertEquals("value1", data.get("list").get(0));
- assertEquals("value2", data.get("list").get(1));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("list");
+ assertThat(data.get("list")).hasSize(2);
+ assertThat(data.get("list").get(0)).isEqualTo("value1");
+ assertThat(data.get("list").get(1)).isEqualTo("value2");
assertRendering(input, rendered);
}
@@ -93,10 +91,10 @@ public void literalValue1() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertTrue(data.containsKey("literal"));
- assertEquals(1, data.get("literal").size());
- assertEquals("hello markdown!\nliteral thing...", data.get("literal").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("literal");
+ assertThat(data.get("literal")).hasSize(1);
+ assertThat(data.get("literal").get(0)).isEqualTo("hello markdown!\nliteral thing...");
assertRendering(input, rendered);
}
@@ -113,10 +111,10 @@ public void literalValue2() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertTrue(data.containsKey("literal"));
- assertEquals(1, data.get("literal").size());
- assertEquals("- hello markdown!", data.get("literal").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("literal");
+ assertThat(data.get("literal")).hasSize(1);
+ assertThat(data.get("literal").get(0)).isEqualTo("- hello markdown!");
assertRendering(input, rendered);
}
@@ -138,20 +136,20 @@ public void complexValues() {
Map> data = getFrontMatter(input);
- assertEquals(3, data.size());
+ assertThat(data).hasSize(3);
- assertTrue(data.containsKey("simple"));
- assertEquals(1, data.get("simple").size());
- assertEquals("value", data.get("simple").get(0));
+ assertThat(data).containsKey("simple");
+ assertThat(data.get("simple")).hasSize(1);
+ assertThat(data.get("simple").get(0)).isEqualTo("value");
- assertTrue(data.containsKey("literal"));
- assertEquals(1, data.get("literal").size());
- assertEquals("hello markdown!\n\nliteral literal", data.get("literal").get(0));
+ assertThat(data).containsKey("literal");
+ assertThat(data.get("literal")).hasSize(1);
+ assertThat(data.get("literal").get(0)).isEqualTo("hello markdown!\n\nliteral literal");
- assertTrue(data.containsKey("list"));
- assertEquals(2, data.get("list").size());
- assertEquals("value1", data.get("list").get(0));
- assertEquals("value2", data.get("list").get(1));
+ assertThat(data).containsKey("list");
+ assertThat(data.get("list")).hasSize(2);
+ assertThat(data.get("list").get(0)).isEqualTo("value1");
+ assertThat(data.get("list").get(1)).isEqualTo("value2");
assertRendering(input, rendered);
}
@@ -165,7 +163,7 @@ public void empty() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -181,7 +179,7 @@ public void yamlInParagraph() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -196,7 +194,7 @@ public void yamlOnSecondLine() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -209,7 +207,7 @@ public void nonMatchedStartTag() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -223,7 +221,7 @@ public void inList() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -241,9 +239,9 @@ public void visitorIgnoresOtherCustomNodes() {
document.accept(visitor);
Map> data = visitor.getData();
- assertEquals(1, data.size());
- assertTrue(data.containsKey("hello"));
- assertEquals(Collections.singletonList("world"), data.get("hello"));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("hello");
+ assertThat(data.get("hello")).isEqualTo(List.of("world"));
}
@Test
@@ -256,15 +254,15 @@ public void nodesCanBeModified() {
Node document = PARSER.parse(input);
YamlFrontMatterNode node = (YamlFrontMatterNode) document.getFirstChild().getFirstChild();
node.setKey("see");
- node.setValues(Collections.singletonList("you"));
+ node.setValues(List.of("you"));
YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor();
document.accept(visitor);
Map> data = visitor.getData();
- assertEquals(1, data.size());
- assertTrue(data.containsKey("see"));
- assertEquals(Collections.singletonList("you"), data.get("see"));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("see");
+ assertThat(data.get("see")).isEqualTo(List.of("you"));
}
@Test
@@ -276,10 +274,10 @@ public void dotInKeys() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertEquals("ms.author", data.keySet().iterator().next());
- assertEquals(1, data.get("ms.author").size());
- assertEquals("author", data.get("ms.author").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data.keySet().iterator().next()).isEqualTo("ms.author");
+ assertThat(data.get("ms.author")).hasSize(1);
+ assertThat(data.get("ms.author").get(0)).isEqualTo("author");
}
@Test
@@ -293,9 +291,9 @@ public void singleQuotedLiterals() {
Map> data = getFrontMatter(input);
- assertEquals(2, data.size());
- assertEquals("It's me", data.get("string").get(0));
- assertEquals("I'm here", data.get("list").get(0));
+ assertThat(data).hasSize(2);
+ assertThat(data.get("string").get(0)).isEqualTo("It's me");
+ assertThat(data.get("list").get(0)).isEqualTo("I'm here");
}
@Test
@@ -309,9 +307,9 @@ public void doubleQuotedLiteral() {
Map> data = getFrontMatter(input);
- assertEquals(2, data.size());
- assertEquals("backslash: \\ quote: \"", data.get("string").get(0));
- assertEquals("hey", data.get("list").get(0));
+ assertThat(data).hasSize(2);
+ assertThat(data.get("string").get(0)).isEqualTo("backslash: \\ quote: \"");
+ assertThat(data.get("list").get(0)).isEqualTo("hey");
}
@Override
diff --git a/commonmark-integration-test/.settings/org.eclipse.core.runtime.prefs b/commonmark-integration-test/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 5a0ad22d2..000000000
--- a/commonmark-integration-test/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/commonmark-integration-test/.settings/org.eclipse.jdt.core.prefs b/commonmark-integration-test/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 3c0d27c8f..000000000
--- a/commonmark-integration-test/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,290 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.7
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=0
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
-org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=true
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
-org.eclipse.jdt.core.formatter.comment.line_length=120
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=2
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
-org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=false
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=120
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
-org.eclipse.jdt.core.formatter.tabulation.char=space
-org.eclipse.jdt.core.formatter.tabulation.size=4
-org.eclipse.jdt.core.formatter.use_on_off_tags=false
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
-org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml
index bbd4c8a74..7e0048a73 100644
--- a/commonmark-integration-test/pom.xml
+++ b/commonmark-integration-test/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-integration-test
@@ -20,10 +20,18 @@
org.commonmark
commonmark-ext-autolink
+
+ org.commonmark
+ commonmark-ext-footnotes
+
org.commonmark
commonmark-ext-ins
+
+ org.commonmark
+ commonmark-ext-gfm-alerts
+
org.commonmark
commonmark-ext-gfm-strikethrough
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java
index 8ee15164a..f1259b825 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java
@@ -3,39 +3,30 @@
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.testutil.TestResources;
-import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
-import java.util.ArrayList;
import java.util.List;
-import static org.junit.Assert.assertNotNull;
+import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests various substrings of the spec examples to check for out of bounds exceptions.
*/
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public class BoundsIntegrationTest {
private static final Parser PARSER = Parser.builder().build();
- protected final String input;
+ @Parameter
+ String input;
- public BoundsIntegrationTest(String input) {
- this.input = input;
- }
-
- @Parameterized.Parameters(name = "{0}")
- public static List data() {
- List examples = ExampleReader.readExamples(TestResources.getSpec());
- List data = new ArrayList<>();
- for (Example example : examples) {
- data.add(new Object[]{example.getSource()});
- }
- return data;
+ static List data() {
+ return ExampleReader.readExampleSources(TestResources.getSpec());
}
@Test
@@ -54,7 +45,7 @@ private void parse(String input) {
try {
Node parsed = PARSER.parse(input);
// Parsing should always return a node
- assertNotNull(parsed);
+ assertThat(parsed).isNotNull();
} catch (Exception e) {
throw new AssertionError("Parsing failed, input: " + input, e);
}
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java
index 5eddcc57a..9090c797f 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java
@@ -2,22 +2,25 @@
import org.commonmark.Extension;
import org.commonmark.ext.autolink.AutolinkExtension;
+import org.commonmark.ext.footnotes.FootnotesExtension;
import org.commonmark.ext.front.matter.YamlFrontMatterExtension;
+import org.commonmark.ext.gfm.alerts.AlertsExtension;
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.ext.image.attributes.ImageAttributesExtension;
import org.commonmark.ext.ins.InsExtension;
import org.commonmark.ext.task.list.items.TaskListItemsExtension;
-import java.util.Arrays;
import java.util.List;
public class Extensions {
- static final List ALL_EXTENSIONS = Arrays.asList(
+ static final List ALL_EXTENSIONS = List.of(
AutolinkExtension.create(),
+ FootnotesExtension.create(),
ImageAttributesExtension.create(),
InsExtension.create(),
+ AlertsExtension.create(),
StrikethroughExtension.create(),
TablesExtension.create(),
TaskListItemsExtension.create(),
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java
index f5d84b5a9..523154d2c 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java
@@ -3,7 +3,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
/**
* Tests to ensure all extensions work well together.
@@ -31,6 +31,7 @@ public void testTaskListItems() {
}
+ @Override
protected String render(String source) {
return RENDERER.render(PARSER.parse(source));
}
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java
index 51f761cfd..fe14273ab 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java
@@ -10,25 +10,16 @@
import org.commonmark.ext.task.list.items.TaskListItemsExtension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.Arrays;
import java.util.List;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class MarkdownRendererIntegrationTest {
- private static final List EXTENSIONS = Arrays.asList(
- AutolinkExtension.create(),
- ImageAttributesExtension.create(),
- InsExtension.create(),
- StrikethroughExtension.create(),
- TablesExtension.create(),
- TaskListItemsExtension.create(),
- YamlFrontMatterExtension.create());
- private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
- private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(EXTENSIONS).build();
+ private static final Parser PARSER = Parser.builder().extensions(Extensions.ALL_EXTENSIONS).build();
+ private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(Extensions.ALL_EXTENSIONS).build();
@Test
public void testStrikethroughInTable() {
@@ -41,6 +32,6 @@ private String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/PegDownBenchmark.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/PegDownBenchmark.java
index 7b61242f4..ecc9c2cfd 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/PegDownBenchmark.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/PegDownBenchmark.java
@@ -12,7 +12,6 @@
import org.pegdown.Extensions;
import org.pegdown.PegDownProcessor;
-import java.util.Collections;
import java.util.List;
@State(Scope.Benchmark)
@@ -32,7 +31,7 @@ public static void main(String[] args) throws Exception {
@Benchmark
public long wholeSpec() {
- return parseAndRender(Collections.singletonList(SPEC));
+ return parseAndRender(List.of(SPEC));
}
@Benchmark
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java
index a0649f537..171cc51b1 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java
@@ -14,10 +14,6 @@ public class SourceSpanIntegrationTest extends SpecIntegrationTest {
.includeSourceSpans(IncludeSourceSpans.BLOCKS)
.build();
- public SourceSpanIntegrationTest(Example example) {
- super(example);
- }
-
@Override
protected String render(String source) {
return RENDERER.render(PARSER.parse(source));
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java
index 2b615aa41..07853d402 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java
@@ -4,7 +4,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.SpecTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.*;
@@ -20,10 +20,6 @@ public class SpecIntegrationTest extends SpecTestCase {
protected static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(Extensions.ALL_EXTENSIONS).percentEncodeUrls(true).build();
protected static final Map OVERRIDDEN_EXAMPLES = getOverriddenExamples();
- public SpecIntegrationTest(Example example) {
- super(example);
- }
-
@Test
public void testHtmlRendering() {
String expectedHtml = OVERRIDDEN_EXAMPLES.get(example.getSource());
diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml
index dd115fdad..6a9c342cc 100644
--- a/commonmark-test-util/pom.xml
+++ b/commonmark-test-util/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-test-util
@@ -13,8 +13,12 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupiter
+
+
+ org.assertj
+ assertj-core
diff --git a/commonmark-test-util/src/main/java/module-info.java b/commonmark-test-util/src/main/java/module-info.java
index ef983a513..12980d80a 100644
--- a/commonmark-test-util/src/main/java/module-info.java
+++ b/commonmark-test-util/src/main/java/module-info.java
@@ -2,5 +2,6 @@
exports org.commonmark.testutil;
exports org.commonmark.testutil.example;
- requires junit;
+ requires org.assertj.core;
+ requires org.junit.jupiter.params;
}
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java
index 64124b129..971a1b4ea 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java
@@ -1,13 +1,13 @@
package org.commonmark.testutil;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class Asserts {
public static void assertRendering(String source, String expectedRendering, String actualRendering) {
// include source for better assertion errors
String expected = showTabs(expectedRendering + "\n\n" + source);
String actual = showTabs(actualRendering + "\n\n" + source);
- assertEquals(expected, actual);
+ assertThat(actual).isEqualTo(expected);
}
private static String showTabs(String s) {
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java
index b585f4604..f7da4c008 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java
@@ -1,5 +1,7 @@
package org.commonmark.testutil;
+import static org.assertj.core.api.Assertions.assertThat;
+
public abstract class RenderingTestCase {
protected abstract String render(String source);
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java
index 3be768682..c29a6a69a 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java
@@ -2,33 +2,22 @@
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import java.util.ArrayList;
import java.util.List;
-import static org.commonmark.testutil.Asserts.assertRendering;
-
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public abstract class SpecTestCase {
- protected final Example example;
-
- public SpecTestCase(Example example) {
- this.example = example;
- }
+ @Parameter
+ protected Example example;
- @Parameters(name = "{0}")
- public static List data() {
- List examples = ExampleReader.readExamples(TestResources.getSpec());
- List data = new ArrayList<>();
- for (Example example : examples) {
- data.add(new Object[]{example});
- }
- return data;
+ static List data() {
+ return ExampleReader.readExamples(TestResources.getSpec());
}
-
}
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/TestResources.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/TestResources.java
index ac4d3f1c2..5af649a86 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/TestResources.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/TestResources.java
@@ -4,8 +4,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
-import java.nio.charset.Charset;
-import java.util.Arrays;
+import java.nio.charset.StandardCharsets;
import java.util.List;
public class TestResources {
@@ -19,7 +18,7 @@ public static URL getGfmSpec() {
}
public static List getRegressions() {
- return Arrays.asList(
+ return List.of(
TestResources.class.getResource("/cmark-regression.txt"),
TestResources.class.getResource("/commonmark.js-regression.txt")
);
@@ -27,7 +26,7 @@ public static List getRegressions() {
public static String readAsString(URL url) {
StringBuilder sb = new StringBuilder();
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Charset.forName("UTF-8")))) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java
index e93d2e07c..d40a10f63 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java
@@ -2,11 +2,12 @@
import java.io.*;
import java.net.URL;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* Reader for files containing examples of CommonMark source and the expected HTML rendering (e.g. spec.txt).
@@ -42,15 +43,13 @@ public static List readExamples(URL url) {
}
}
+ public static List readExamples(URL url, String info) {
+ var examples = readExamples(url);
+ return examples.stream().filter(e -> e.getInfo().contains(info)).collect(Collectors.toList());
+ }
+
public static List readExampleObjects(URL url, String info) {
- List examples = readExamples(url);
- List data = new ArrayList<>();
- for (Example example : examples) {
- if (example.getInfo().contains(info)) {
- data.add(new Object[]{example});
- }
- }
- return data;
+ return readExamples(url, info).stream().map(e -> new Object[]{e}).collect(Collectors.toList());
}
public static List readExampleSources(URL url) {
@@ -66,7 +65,7 @@ private List read() throws IOException {
resetContents();
try (BufferedReader reader = new BufferedReader(
- new InputStreamReader(inputStream, Charset.forName("UTF-8")))) {
+ new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
processLine(line);
diff --git a/commonmark/.settings/org.eclipse.core.runtime.prefs b/commonmark/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 5a0ad22d2..000000000
--- a/commonmark/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/commonmark/.settings/org.eclipse.jdt.core.prefs b/commonmark/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 3c0d27c8f..000000000
--- a/commonmark/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,290 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.7
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=0
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
-org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=true
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
-org.eclipse.jdt.core.formatter.comment.line_length=120
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=2
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
-org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=false
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=120
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
-org.eclipse.jdt.core.formatter.tabulation.char=space
-org.eclipse.jdt.core.formatter.tabulation.size=4
-org.eclipse.jdt.core.formatter.use_on_off_tags=false
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
-org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/commonmark/pom.xml b/commonmark/pom.xml
index bcb5b3bf0..4e060edaa 100644
--- a/commonmark/pom.xml
+++ b/commonmark/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.22.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark
@@ -38,7 +38,7 @@
org.codehaus.mojo
exec-maven-plugin
- 1.5.0
+ 3.2.0
java
test
@@ -54,4 +54,12 @@
+
+
+ BSD-2-Clause
+ https://opensource.org/licenses/BSD-2-Clause
+ repo
+
+
+
diff --git a/commonmark/src/main/java/org/commonmark/internal/BlockQuoteParser.java b/commonmark/src/main/java/org/commonmark/internal/BlockQuoteParser.java
index 87c923e06..572c491f8 100644
--- a/commonmark/src/main/java/org/commonmark/internal/BlockQuoteParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/BlockQuoteParser.java
@@ -46,6 +46,7 @@ private static boolean isMarker(ParserState state, int index) {
}
public static class Factory extends AbstractBlockParserFactory {
+ @Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
int nextNonSpace = state.getNextNonSpaceIndex();
if (isMarker(state, nextNonSpace)) {
diff --git a/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java b/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java
index c7e967d46..516f944b2 100644
--- a/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java
+++ b/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java
@@ -9,6 +9,7 @@ public class BlockStartImpl extends BlockStart {
private int newIndex = -1;
private int newColumn = -1;
private boolean replaceActiveBlockParser = false;
+ private int replaceParagraphLines = 0;
public BlockStartImpl(BlockParser... blockParsers) {
this.blockParsers = blockParsers;
@@ -30,6 +31,10 @@ public boolean isReplaceActiveBlockParser() {
return replaceActiveBlockParser;
}
+ int getReplaceParagraphLines() {
+ return replaceParagraphLines;
+ }
+
@Override
public BlockStart atIndex(int newIndex) {
this.newIndex = newIndex;
@@ -48,4 +53,12 @@ public BlockStart replaceActiveBlockParser() {
return this;
}
+ @Override
+ public BlockStart replaceParagraphLines(int lines) {
+ if (!(lines >= 1)) {
+ throw new IllegalArgumentException("Lines must be >= 1");
+ }
+ this.replaceParagraphLines = lines;
+ return this;
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/Bracket.java b/commonmark/src/main/java/org/commonmark/internal/Bracket.java
index 9c73a1327..c04b6ecda 100644
--- a/commonmark/src/main/java/org/commonmark/internal/Bracket.java
+++ b/commonmark/src/main/java/org/commonmark/internal/Bracket.java
@@ -4,26 +4,34 @@
import org.commonmark.parser.beta.Position;
/**
- * Opening bracket for links ([) or images (![).
+ * Opening bracket for links ({@code [}), images ({@code ![}), or links with other markers.
*/
public class Bracket {
- public final Text node;
+ /**
+ * The node of a marker such as {@code !} if present, null otherwise.
+ */
+ public final Text markerNode;
/**
- * The position of the marker for the bracket ([ or ![)
+ * The position of the marker if present, null otherwise.
*/
public final Position markerPosition;
/**
- * The position of the content (after the opening bracket)
+ * The node of {@code [}.
*/
- public final Position contentPosition;
+ public final Text bracketNode;
/**
- * Whether this is an image or link.
+ * The position of {@code [}.
*/
- public final boolean image;
+ public final Position bracketPosition;
+
+ /**
+ * The position of the content (after the opening bracket)
+ */
+ public final Position contentPosition;
/**
* Previous bracket.
@@ -41,23 +49,24 @@ public class Bracket {
public boolean allowed = true;
/**
- * Whether there is an unescaped bracket (opening or closing) anywhere after this opening bracket.
+ * Whether there is an unescaped bracket (opening or closing) after this opening bracket in the text parsed so far.
*/
public boolean bracketAfter = false;
- static public Bracket link(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) {
- return new Bracket(node, markerPosition, contentPosition, previous, previousDelimiter, false);
+ static public Bracket link(Text bracketNode, Position bracketPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) {
+ return new Bracket(null, null, bracketNode, bracketPosition, contentPosition, previous, previousDelimiter);
}
- static public Bracket image(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) {
- return new Bracket(node, markerPosition, contentPosition, previous, previousDelimiter, true);
+ static public Bracket withMarker(Text markerNode, Position markerPosition, Text bracketNode, Position bracketPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) {
+ return new Bracket(markerNode, markerPosition, bracketNode, bracketPosition, contentPosition, previous, previousDelimiter);
}
- private Bracket(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter, boolean image) {
- this.node = node;
+ private Bracket(Text markerNode, Position markerPosition, Text bracketNode, Position bracketPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) {
+ this.markerNode = markerNode;
this.markerPosition = markerPosition;
+ this.bracketNode = bracketNode;
+ this.bracketPosition = bracketPosition;
this.contentPosition = contentPosition;
- this.image = image;
this.previous = previous;
this.previousDelimiter = previousDelimiter;
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/Definitions.java b/commonmark/src/main/java/org/commonmark/internal/Definitions.java
new file mode 100644
index 000000000..0377842c9
--- /dev/null
+++ b/commonmark/src/main/java/org/commonmark/internal/Definitions.java
@@ -0,0 +1,33 @@
+package org.commonmark.internal;
+
+import org.commonmark.node.DefinitionMap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Definitions {
+
+ private final Map, DefinitionMap>> definitionsByType = new HashMap<>();
+
+ public void addDefinitions(DefinitionMap definitionMap) {
+ var existingMap = getMap(definitionMap.getType());
+ if (existingMap == null) {
+ definitionsByType.put(definitionMap.getType(), definitionMap);
+ } else {
+ existingMap.addAll(definitionMap);
+ }
+ }
+
+ public V getDefinition(Class type, String label) {
+ var definitionMap = getMap(type);
+ if (definitionMap == null) {
+ return null;
+ }
+ return definitionMap.get(label);
+ }
+
+ private DefinitionMap getMap(Class type) {
+ //noinspection unchecked
+ return (DefinitionMap) definitionsByType.get(type);
+ }
+}
diff --git a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java
index 2cc37e306..07d97296b 100644
--- a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java
@@ -1,20 +1,25 @@
package org.commonmark.internal;
+import org.commonmark.internal.util.LineReader;
import org.commonmark.internal.util.Parsing;
import org.commonmark.node.*;
-import org.commonmark.parser.*;
+import org.commonmark.parser.IncludeSourceSpans;
+import org.commonmark.parser.InlineParserFactory;
+import org.commonmark.parser.SourceLine;
+import org.commonmark.parser.SourceLines;
+import org.commonmark.parser.beta.LinkProcessor;
+import org.commonmark.parser.beta.InlineContentParserFactory;
import org.commonmark.parser.block.*;
import org.commonmark.parser.delimiter.DelimiterProcessor;
import org.commonmark.text.Characters;
-import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
public class DocumentParser implements ParserState {
- private static final Set> CORE_FACTORY_TYPES = new LinkedHashSet<>(Arrays.asList(
+ private static final Set> CORE_FACTORY_TYPES = new LinkedHashSet<>(List.of(
BlockQuote.class,
Heading.class,
FencedCodeBlock.class,
@@ -66,20 +71,30 @@ public class DocumentParser implements ParserState {
private final List blockParserFactories;
private final InlineParserFactory inlineParserFactory;
+ private final List inlineContentParserFactories;
private final List delimiterProcessors;
+ private final List linkProcessors;
+ private final Set linkMarkers;
private final IncludeSourceSpans includeSourceSpans;
+ private final int maxOpenBlockParsers;
private final DocumentBlockParser documentBlockParser;
- private final LinkReferenceDefinitions definitions = new LinkReferenceDefinitions();
+ private final Definitions definitions = new Definitions();
private final List openBlockParsers = new ArrayList<>();
private final List allBlockParsers = new ArrayList<>();
public DocumentParser(List blockParserFactories, InlineParserFactory inlineParserFactory,
- List delimiterProcessors, IncludeSourceSpans includeSourceSpans) {
+ List inlineContentParserFactories, List delimiterProcessors,
+ List linkProcessors, Set linkMarkers,
+ IncludeSourceSpans includeSourceSpans, int maxOpenBlockParsers) {
this.blockParserFactories = blockParserFactories;
this.inlineParserFactory = inlineParserFactory;
+ this.inlineContentParserFactories = inlineContentParserFactories;
this.delimiterProcessors = delimiterProcessors;
+ this.linkProcessors = linkProcessors;
+ this.linkMarkers = linkMarkers;
this.includeSourceSpans = includeSourceSpans;
+ this.maxOpenBlockParsers = maxOpenBlockParsers;
this.documentBlockParser = new DocumentBlockParser();
activateBlockParser(new OpenBlockParser(documentBlockParser, 0));
@@ -115,32 +130,32 @@ public Document parse(String input) {
int lineBreak;
while ((lineBreak = Characters.findLineBreak(input, lineStart)) != -1) {
String line = input.substring(lineStart, lineBreak);
- parseLine(line);
+ parseLine(line, lineStart);
if (lineBreak + 1 < input.length() && input.charAt(lineBreak) == '\r' && input.charAt(lineBreak + 1) == '\n') {
lineStart = lineBreak + 2;
} else {
lineStart = lineBreak + 1;
}
}
- if (input.length() > 0 && (lineStart == 0 || lineStart < input.length())) {
+ if (!input.isEmpty() && (lineStart == 0 || lineStart < input.length())) {
String line = input.substring(lineStart);
- parseLine(line);
+ parseLine(line, lineStart);
}
return finalizeAndProcess();
}
public Document parse(Reader input) throws IOException {
- BufferedReader bufferedReader;
- if (input instanceof BufferedReader) {
- bufferedReader = (BufferedReader) input;
- } else {
- bufferedReader = new BufferedReader(input);
- }
-
+ var lineReader = new LineReader(input);
+ int inputIndex = 0;
String line;
- while ((line = bufferedReader.readLine()) != null) {
- parseLine(line);
+ while ((line = lineReader.readLine()) != null) {
+ parseLine(line, inputIndex);
+ inputIndex += line.length();
+ var eol = lineReader.getLineTerminator();
+ if (eol != null) {
+ inputIndex += eol.length();
+ }
}
return finalizeAndProcess();
@@ -185,8 +200,8 @@ public BlockParser getActiveBlockParser() {
* Analyze a line of text and update the document appropriately. We parse markdown text by calling this on each
* line of input, then finalizing the document.
*/
- private void parseLine(CharSequence ln) {
- setLine(ln);
+ private void parseLine(String ln, int inputIndex) {
+ setLine(ln, inputIndex);
// For each containing block, try to parse the associated line start.
// The document will always match, so we can skip the first block parser and start at 1 matches
@@ -258,9 +273,15 @@ private void parseLine(CharSequence ln) {
}
List replacedSourceSpans = null;
- if (blockStart.isReplaceActiveBlockParser()) {
- Block replacedBlock = prepareActiveBlockParserForReplacement();
- replacedSourceSpans = replacedBlock.getSourceSpans();
+ if (blockStart.getReplaceParagraphLines() >= 1 || blockStart.isReplaceActiveBlockParser()) {
+ var activeBlockParser = getActiveBlockParser();
+ if (activeBlockParser instanceof ParagraphParser) {
+ var paragraphParser = (ParagraphParser) activeBlockParser;
+ var lines = blockStart.isReplaceActiveBlockParser() ? Integer.MAX_VALUE : blockStart.getReplaceParagraphLines();
+ replacedSourceSpans = replaceParagraphLines(lines, paragraphParser);
+ } else if (blockStart.isReplaceActiveBlockParser()) {
+ replacedSourceSpans = prepareActiveBlockParserForReplacement(activeBlockParser);
+ }
}
for (BlockParser newBlockParser : blockStart.getBlockParsers()) {
@@ -276,7 +297,7 @@ private void parseLine(CharSequence ln) {
// What remains at the offset is a text line. Add the text to the
// appropriate block.
- // First check for a lazy paragraph continuation:
+ // First check for a lazy continuation line
if (!startedNewBlock && !isBlank() &&
getActiveBlockParser().canHaveLazyContinuationLines()) {
openBlockParsers.get(openBlockParsers.size() - 1).sourceIndex = lastIndex;
@@ -310,16 +331,16 @@ private void parseLine(CharSequence ln) {
}
}
- private void setLine(CharSequence ln) {
+ private void setLine(String ln, int inputIndex) {
lineIndex++;
index = 0;
column = 0;
columnIsInTab = false;
- CharSequence lineContent = prepareLine(ln);
+ String lineContent = prepareLine(ln);
SourceSpan sourceSpan = null;
if (includeSourceSpans != IncludeSourceSpans.NONE) {
- sourceSpan = SourceSpan.of(lineIndex, 0, lineContent.length());
+ sourceSpan = SourceSpan.of(lineIndex, 0, inputIndex, lineContent.length());
}
this.line = SourceLine.of(lineContent, sourceSpan);
}
@@ -418,10 +439,9 @@ private void addLine() {
content = line.getContent().subSequence(index, line.getContent().length());
}
SourceSpan sourceSpan = null;
- if (includeSourceSpans == IncludeSourceSpans.BLOCKS_AND_INLINES) {
- // Note that if we're in a partially-consumed tab, the length here corresponds to the content but not to the
- // actual source length. That sounds like a problem, but I haven't found a test case where it matters (yet).
- sourceSpan = SourceSpan.of(lineIndex, index, content.length());
+ if (includeSourceSpans == IncludeSourceSpans.BLOCKS_AND_INLINES && index < line.getSourceSpan().getLength()) {
+ // Note that if we're in a partially-consumed tab the length of the source span and the content don't match.
+ sourceSpan = line.getSourceSpan().subSpan(index);
}
getActiveBlockParser().addLine(SourceLine.of(content, sourceSpan));
addSourceSpans();
@@ -429,19 +449,24 @@ private void addLine() {
private void addSourceSpans() {
if (includeSourceSpans != IncludeSourceSpans.NONE) {
- // Don't add source spans for Document itself (it would get the whole source text)
+ // Don't add source spans for Document itself (it would get the whole source text), so start at 1, not 0
for (int i = 1; i < openBlockParsers.size(); i++) {
- OpenBlockParser openBlockParser = openBlockParsers.get(i);
- int blockIndex = openBlockParser.sourceIndex;
+ var openBlockParser = openBlockParsers.get(i);
+ // In case of a lazy continuation line, the index is less than where the block parser would expect the
+ // contents to start, so let's use whichever is smaller.
+ int blockIndex = Math.min(openBlockParser.sourceIndex, index);
int length = line.getContent().length() - blockIndex;
if (length != 0) {
- openBlockParser.blockParser.addSourceSpan(SourceSpan.of(lineIndex, blockIndex, length));
+ openBlockParser.blockParser.addSourceSpan(line.getSourceSpan().subSpan(blockIndex));
}
}
}
}
private BlockStartImpl findBlockStart(BlockParser blockParser) {
+ if (openBlockParsers.size() > maxOpenBlockParsers) {
+ return null;
+ }
MatchedBlockParser matchedBlockParser = new MatchedBlockParserImpl(blockParser);
for (BlockParserFactory blockParserFactory : blockParserFactories) {
BlockStart result = blockParserFactory.tryStart(this, matchedBlockParser);
@@ -452,35 +477,14 @@ private BlockStartImpl findBlockStart(BlockParser blockParser) {
return null;
}
- /**
- * Finalize a block. Close it and do any necessary postprocessing, e.g. setting the content of blocks and
- * collecting link reference definitions from paragraphs.
- */
- private void finalize(BlockParser blockParser) {
- if (blockParser instanceof ParagraphParser) {
- addDefinitionsFrom((ParagraphParser) blockParser);
- }
-
- blockParser.closeBlock();
- }
-
- private void addDefinitionsFrom(ParagraphParser paragraphParser) {
- for (LinkReferenceDefinition definition : paragraphParser.getDefinitions()) {
- // Add nodes into document before paragraph.
- paragraphParser.getBlock().insertBefore(definition);
-
- definitions.add(definition);
- }
- }
-
/**
* Walk through a block & children recursively, parsing string content into inline content where appropriate.
*/
private void processInlines() {
- InlineParserContextImpl context = new InlineParserContextImpl(delimiterProcessors, definitions);
- InlineParser inlineParser = inlineParserFactory.create(context);
+ var context = new InlineParserContextImpl(inlineContentParserFactories, delimiterProcessors, linkProcessors, linkMarkers, definitions);
+ var inlineParser = inlineParserFactory.create(context);
- for (BlockParser blockParser : allBlockParsers) {
+ for (var blockParser : allBlockParsers) {
blockParser.parseInlines(inlineParser);
}
}
@@ -506,24 +510,23 @@ private OpenBlockParser deactivateBlockParser() {
return openBlockParsers.remove(openBlockParsers.size() - 1);
}
- private Block prepareActiveBlockParserForReplacement() {
- // Note that we don't want to parse inlines, as it's getting replaced.
- BlockParser old = deactivateBlockParser().blockParser;
+ private List replaceParagraphLines(int lines, ParagraphParser paragraphParser) {
+ // Remove lines from paragraph as the new block is using them.
+ // If all lines are used, this also unlinks the Paragraph block.
+ var sourceSpans = paragraphParser.removeLines(lines);
+ // Close the paragraph block parser, which will finalize it.
+ closeBlockParsers(1);
+ return sourceSpans;
+ }
- if (old instanceof ParagraphParser) {
- ParagraphParser paragraphParser = (ParagraphParser) old;
- // Collect any link reference definitions. Note that replacing the active block parser is done after a
- // block parser got the current paragraph content using MatchedBlockParser#getContentString. In case the
- // paragraph started with link reference definitions, we parse and strip them before the block parser gets
- // the content. We want to keep them.
- // If no replacement happens, we collect the definitions as part of finalizing paragraph blocks.
- addDefinitionsFrom(paragraphParser);
- }
+ private List prepareActiveBlockParserForReplacement(BlockParser blockParser) {
+ // Note that we don't want to parse inlines here, as it's getting replaced.
+ deactivateBlockParser();
// Do this so that source positions are calculated, which we will carry over to the replacing block.
- old.closeBlock();
- old.getBlock().unlink();
- return old.getBlock();
+ blockParser.closeBlock();
+ blockParser.getBlock().unlink();
+ return blockParser.getBlock().getSourceSpans();
}
private Document finalizeAndProcess() {
@@ -544,31 +547,28 @@ private void closeBlockParsers(int count) {
}
/**
- * Prepares the input line replacing {@code \0}
+ * Finalize a block. Close it and do any necessary postprocessing, e.g. setting the content of blocks and
+ * collecting link reference definitions from paragraphs.
*/
- private static CharSequence prepareLine(CharSequence line) {
- // Avoid building a new string in the majority of cases (no \0)
- StringBuilder sb = null;
- int length = line.length();
- for (int i = 0; i < length; i++) {
- char c = line.charAt(i);
- if (c == '\0') {
- if (sb == null) {
- sb = new StringBuilder(length);
- sb.append(line, 0, i);
- }
- sb.append('\uFFFD');
- } else {
- if (sb != null) {
- sb.append(c);
- }
- }
+ private void finalize(BlockParser blockParser) {
+ addDefinitionsFrom(blockParser);
+ blockParser.closeBlock();
+ }
+
+ private void addDefinitionsFrom(BlockParser blockParser) {
+ for (var definitionMap : blockParser.getDefinitions()) {
+ definitions.addDefinitions(definitionMap);
}
+ }
- if (sb != null) {
- return sb.toString();
- } else {
+ /**
+ * Prepares the input line replacing {@code \0}
+ */
+ private static String prepareLine(String line) {
+ if (line.indexOf('\0') == -1) {
return line;
+ } else {
+ return line.replace('\0', '\uFFFD');
}
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java b/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java
index d422c1241..05f070137 100644
--- a/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java
@@ -60,7 +60,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar
if (!paragraph.isEmpty()) {
return BlockStart.of(new HeadingParser(setextHeadingLevel, paragraph))
.atIndex(line.getContent().length())
- .replaceActiveBlockParser();
+ .replaceParagraphLines(paragraph.getLines().size());
}
}
@@ -69,7 +69,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar
}
// spec: An ATX heading consists of a string of characters, parsed as inline content, between an opening sequence of
- // 1–6 unescaped # characters and an optional closing sequence of any number of unescaped # characters. The opening
+ // 1-6 unescaped # characters and an optional closing sequence of any number of unescaped # characters. The opening
// sequence of # characters must be followed by a space or by the end of line. The optional closing sequence of #s
// must be preceded by a space and may be followed by spaces only.
private static HeadingParser getAtxHeading(SourceLine line) {
@@ -140,10 +140,12 @@ private static int getSetextHeadingLevel(CharSequence line, int index) {
if (isSetextHeadingRest(line, index + 1, '=')) {
return 1;
}
+ break;
case '-':
if (isSetextHeadingRest(line, index + 1, '-')) {
return 2;
}
+ break;
}
return 0;
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/InlineParserContextImpl.java b/commonmark/src/main/java/org/commonmark/internal/InlineParserContextImpl.java
index f485614d5..233041f62 100644
--- a/commonmark/src/main/java/org/commonmark/internal/InlineParserContextImpl.java
+++ b/commonmark/src/main/java/org/commonmark/internal/InlineParserContextImpl.java
@@ -2,20 +2,36 @@
import org.commonmark.node.LinkReferenceDefinition;
import org.commonmark.parser.InlineParserContext;
+import org.commonmark.parser.beta.LinkProcessor;
+import org.commonmark.parser.beta.InlineContentParserFactory;
import org.commonmark.parser.delimiter.DelimiterProcessor;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
public class InlineParserContextImpl implements InlineParserContext {
+ private final List inlineContentParserFactories;
private final List delimiterProcessors;
- private final LinkReferenceDefinitions linkReferenceDefinitions;
+ private final List linkProcessors;
+ private final Set linkMarkers;
+ private final Definitions definitions;
- public InlineParserContextImpl(List delimiterProcessors,
- LinkReferenceDefinitions linkReferenceDefinitions) {
+ public InlineParserContextImpl(List inlineContentParserFactories,
+ List delimiterProcessors,
+ List linkProcessors,
+ Set linkMarkers,
+ Definitions definitions) {
+ this.inlineContentParserFactories = inlineContentParserFactories;
this.delimiterProcessors = delimiterProcessors;
- this.linkReferenceDefinitions = linkReferenceDefinitions;
+ this.linkProcessors = linkProcessors;
+ this.linkMarkers = linkMarkers;
+ this.definitions = definitions;
+ }
+
+ @Override
+ public List getCustomInlineContentParserFactories() {
+ return inlineContentParserFactories;
}
@Override
@@ -23,8 +39,23 @@ public List getCustomDelimiterProcessors() {
return delimiterProcessors;
}
+ @Override
+ public List getCustomLinkProcessors() {
+ return linkProcessors;
+ }
+
+ @Override
+ public Set getCustomLinkMarkers() {
+ return linkMarkers;
+ }
+
@Override
public LinkReferenceDefinition getLinkReferenceDefinition(String label) {
- return linkReferenceDefinitions.get(label);
+ return definitions.getDefinition(LinkReferenceDefinition.class, label);
+ }
+
+ @Override
+ public D getDefinition(Class type, String label) {
+ return definitions.getDefinition(type, label);
}
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java
index 113e80db9..44422f421 100644
--- a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java
+++ b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java
@@ -7,8 +7,8 @@
import org.commonmark.parser.InlineParser;
import org.commonmark.parser.InlineParserContext;
import org.commonmark.parser.SourceLines;
-import org.commonmark.parser.beta.Position;
import org.commonmark.parser.beta.Scanner;
+import org.commonmark.parser.beta.*;
import org.commonmark.parser.delimiter.DelimiterProcessor;
import org.commonmark.text.Characters;
@@ -16,11 +16,14 @@
public class InlineParserImpl implements InlineParser, InlineParserState {
- private final BitSet specialCharacters;
- private final Map delimiterProcessors;
private final InlineParserContext context;
- private final Map> inlineParsers;
+ private final List inlineContentParserFactories;
+ private final Map delimiterProcessors;
+ private final List linkProcessors;
+ private final BitSet specialCharacters;
+ private final BitSet linkMarkers;
+ private Map> inlineParsers;
private Scanner scanner;
private boolean includeSourceSpans;
private int trailingSpaces;
@@ -36,46 +39,40 @@ public class InlineParserImpl implements InlineParser, InlineParserState {
*/
private Bracket lastBracket;
- public InlineParserImpl(InlineParserContext inlineParserContext) {
- this.delimiterProcessors = calculateDelimiterProcessors(inlineParserContext.getCustomDelimiterProcessors());
-
- this.context = inlineParserContext;
- this.inlineParsers = new HashMap<>();
- this.inlineParsers.put('\\', Collections.singletonList(new BackslashInlineParser()));
- this.inlineParsers.put('`', Collections.singletonList(new BackticksInlineParser()));
- this.inlineParsers.put('&', Collections.singletonList(new EntityInlineParser()));
- this.inlineParsers.put('<', Arrays.asList(new AutolinkInlineParser(), new HtmlInlineParser()));
+ public InlineParserImpl(InlineParserContext context) {
+ this.context = context;
+ this.inlineContentParserFactories = calculateInlineContentParserFactories(context.getCustomInlineContentParserFactories());
+ this.delimiterProcessors = calculateDelimiterProcessors(context.getCustomDelimiterProcessors());
+ this.linkProcessors = calculateLinkProcessors(context.getCustomLinkProcessors());
+ this.linkMarkers = calculateLinkMarkers(context.getCustomLinkMarkers());
+ this.specialCharacters = calculateSpecialCharacters(linkMarkers, this.delimiterProcessors.keySet(), this.inlineContentParserFactories);
+ }
- this.specialCharacters = calculateSpecialCharacters(this.delimiterProcessors.keySet(), inlineParsers.keySet());
+ private List calculateInlineContentParserFactories(List customFactories) {
+ // Custom parsers can override built-in parsers if they want, so make sure they are tried first
+ var list = new ArrayList<>(customFactories);
+ list.add(new BackslashInlineParser.Factory());
+ list.add(new BackticksInlineParser.Factory());
+ list.add(new EntityInlineParser.Factory());
+ list.add(new AutolinkInlineParser.Factory());
+ list.add(new HtmlInlineParser.Factory());
+ return list;
}
- public static BitSet calculateSpecialCharacters(Set delimiterCharacters, Set characters) {
- BitSet bitSet = new BitSet();
- for (Character c : delimiterCharacters) {
- bitSet.set(c);
- }
- for (Character c : characters) {
- bitSet.set(c);
- }
- bitSet.set('[');
- bitSet.set(']');
- bitSet.set('!');
- bitSet.set('\n');
- return bitSet;
+ private List calculateLinkProcessors(List linkProcessors) {
+ // Custom link processors can override the built-in behavior, so make sure they are tried first
+ var list = new ArrayList<>(linkProcessors);
+ list.add(new CoreLinkProcessor());
+ return list;
}
- public static Map calculateDelimiterProcessors(List delimiterProcessors) {
- Map map = new HashMap<>();
- addDelimiterProcessors(Arrays.asList(new AsteriskDelimiterProcessor(), new UnderscoreDelimiterProcessor()), map);
+ private static Map calculateDelimiterProcessors(List delimiterProcessors) {
+ var map = new HashMap();
+ addDelimiterProcessors(List.of(new AsteriskDelimiterProcessor(), new UnderscoreDelimiterProcessor()), map);
addDelimiterProcessors(delimiterProcessors, map);
return map;
}
- @Override
- public Scanner scanner() {
- return scanner;
- }
-
private static void addDelimiterProcessors(Iterable delimiterProcessors, Map map) {
for (DelimiterProcessor delimiterProcessor : delimiterProcessors) {
char opening = delimiterProcessor.getOpeningCharacter();
@@ -109,6 +106,50 @@ private static void addDelimiterProcessorForChar(char delimiterChar, DelimiterPr
}
}
+ private static BitSet calculateLinkMarkers(Set linkMarkers) {
+ var bitSet = new BitSet();
+ for (var c : linkMarkers) {
+ bitSet.set(c);
+ }
+ bitSet.set('!');
+ return bitSet;
+ }
+
+ private static BitSet calculateSpecialCharacters(BitSet linkMarkers,
+ Set delimiterCharacters,
+ List inlineContentParserFactories) {
+ BitSet bitSet = (BitSet) linkMarkers.clone();
+ for (Character c : delimiterCharacters) {
+ bitSet.set(c);
+ }
+ for (var factory : inlineContentParserFactories) {
+ for (var c : factory.getTriggerCharacters()) {
+ bitSet.set(c);
+ }
+ }
+ bitSet.set('[');
+ bitSet.set(']');
+ bitSet.set('!');
+ bitSet.set('\n');
+ return bitSet;
+ }
+
+ private Map> createInlineContentParsers() {
+ var map = new HashMap>();
+ for (var factory : inlineContentParserFactories) {
+ var parser = factory.create();
+ for (var c : factory.getTriggerCharacters()) {
+ map.computeIfAbsent(c, k -> new ArrayList<>()).add(parser);
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public Scanner scanner() {
+ return scanner;
+ }
+
/**
* Parse content in block into inline children, appending them to the block node.
*/
@@ -117,14 +158,13 @@ public void parse(SourceLines lines, Node block) {
reset(lines);
while (true) {
- List extends Node> nodes = parseInline();
- if (nodes != null) {
- for (Node node : nodes) {
- block.appendChild(node);
- }
- } else {
+ var nodes = parseInline();
+ if (nodes == null) {
break;
}
+ for (Node node : nodes) {
+ block.appendChild(node);
+ }
}
processDelimiters(null);
@@ -137,6 +177,7 @@ void reset(SourceLines lines) {
this.trailingSpaces = 0;
this.lastDelimiter = null;
this.lastBracket = null;
+ this.inlineParsers = createInlineContentParsers();
}
private Text text(SourceLines sourceLines) {
@@ -155,20 +196,28 @@ private List extends Node> parseInline() {
switch (c) {
case '[':
- return Collections.singletonList(parseOpenBracket());
- case '!':
- return Collections.singletonList(parseBang());
+ return List.of(parseOpenBracket());
case ']':
- return Collections.singletonList(parseCloseBracket());
+ return List.of(parseCloseBracket());
case '\n':
- return Collections.singletonList(parseLineBreak());
+ return List.of(parseLineBreak());
case Scanner.END:
return null;
}
+ if (linkMarkers.get(c)) {
+ var markerPosition = scanner.position();
+ var nodes = parseLinkMarker();
+ if (nodes != null) {
+ return nodes;
+ }
+ // Reset and try other things (e.g. inline parsers below)
+ scanner.setPosition(markerPosition);
+ }
+
// No inline parser, delimiter or other special handling.
if (!specialCharacters.get(c)) {
- return Collections.singletonList(parseText());
+ return List.of(parseText());
}
List inlineParsers = this.inlineParsers.get(c);
@@ -183,7 +232,7 @@ private List extends Node> parseInline() {
if (includeSourceSpans && node.getSourceSpans().isEmpty()) {
node.setSourceSpans(scanner.getSource(position, scanner.position()).getSourceSpans());
}
- return Collections.singletonList(node);
+ return List.of(node);
} else {
// Reset position
scanner.setPosition(position);
@@ -200,7 +249,7 @@ private List extends Node> parseInline() {
}
// If we get here, even for a special/delimiter character, we will just treat it as text.
- return Collections.singletonList(parseText());
+ return List.of(parseText());
}
/**
@@ -240,21 +289,23 @@ private Node parseOpenBracket() {
}
/**
- * If next character is [, and ! delimiter to delimiter stack and add a text node to block's children.
- * Otherwise just add a text node.
+ * If next character is {@code [}, add a bracket to the stack.
+ * Otherwise, return null.
*/
- private Node parseBang() {
- Position start = scanner.position();
+ private List extends Node> parseLinkMarker() {
+ var markerPosition = scanner.position();
scanner.next();
+ var bracketPosition = scanner.position();
if (scanner.next('[')) {
- Position contentPosition = scanner.position();
- Text node = text(scanner.getSource(start, contentPosition));
+ var contentPosition = scanner.position();
+ var bangNode = text(scanner.getSource(markerPosition, bracketPosition));
+ var bracketNode = text(scanner.getSource(bracketPosition, contentPosition));
// Add entry to stack for this opener
- addBracket(Bracket.image(node, start, contentPosition, lastBracket, lastDelimiter));
- return node;
+ addBracket(Bracket.withMarker(bangNode, markerPosition, bracketNode, bracketPosition, contentPosition, lastBracket, lastDelimiter));
+ return List.of(bangNode, bracketNode);
} else {
- return text(scanner.getSource(start, scanner.position()));
+ return null;
}
}
@@ -275,107 +326,158 @@ private Node parseCloseBracket() {
}
if (!opener.allowed) {
- // Matching opener but it's not allowed, just return a literal.
+ // Matching opener, but it's not allowed, just return a literal.
removeLastBracket();
return text(scanner.getSource(beforeClose, afterClose));
}
- // Check to see if we have a link/image
- String dest = null;
- String title = null;
+ var linkOrImage = parseLinkOrImage(opener, beforeClose);
+ if (linkOrImage != null) {
+ return linkOrImage;
+ }
+ scanner.setPosition(afterClose);
- // Maybe a inline link like `[foo](/uri "title")`
- if (scanner.next('(')) {
- scanner.whitespace();
- dest = parseLinkDestination(scanner);
- if (dest == null) {
- scanner.setPosition(afterClose);
- } else {
- int whitespace = scanner.whitespace();
- // title needs a whitespace before
- if (whitespace >= 1) {
- title = parseLinkTitle(scanner);
- scanner.whitespace();
- }
- if (!scanner.next(')')) {
- // Don't have a closing `)`, so it's not a destination and title -> reset.
- // Note that something like `[foo](` could be valid, `(` will just be text.
- scanner.setPosition(afterClose);
- dest = null;
- title = null;
- }
- }
+ // Nothing parsed, just parse the bracket as text and continue
+ removeLastBracket();
+ return text(scanner.getSource(beforeClose, afterClose));
+ }
+
+ private Node parseLinkOrImage(Bracket opener, Position beforeClose) {
+ var linkInfo = parseLinkInfo(opener, beforeClose);
+ if (linkInfo == null) {
+ return null;
}
+ var processorStartPosition = scanner.position();
- // Maybe a reference link like `[foo][bar]`, `[foo][]` or `[foo]`.
- // Note that even `[foo](` could be a valid link if there's a reference, which is why this is not just an `else`
- // here.
- if (dest == null) {
- // See if there's a link label like `[bar]` or `[]`
- String ref = parseLinkLabel(scanner);
- if (ref == null) {
- scanner.setPosition(afterClose);
- }
- if ((ref == null || ref.isEmpty()) && !opener.bracketAfter) {
- // If the second label is empty `[foo][]` or missing `[foo]`, then the first label is the reference.
- // But it can only be a reference when there's no (unescaped) bracket in it.
- // If there is, we don't even need to try to look up the reference. This is an optimization.
- ref = scanner.getSource(opener.contentPosition, beforeClose).getContent();
+ for (var linkProcessor : linkProcessors) {
+ var linkResult = linkProcessor.process(linkInfo, scanner, context);
+ if (!(linkResult instanceof LinkResultImpl)) {
+ // Reset position in case the processor used the scanner, and it didn't work out.
+ scanner.setPosition(processorStartPosition);
+ continue;
}
- if (ref != null) {
- LinkReferenceDefinition definition = context.getLinkReferenceDefinition(ref);
- if (definition != null) {
- dest = definition.getDestination();
- title = definition.getTitle();
- }
+ var result = (LinkResultImpl) linkResult;
+ var node = result.getNode();
+ var position = result.getPosition();
+ var includeMarker = result.isIncludeMarker();
+
+ switch (result.getType()) {
+ case WRAP:
+ scanner.setPosition(position);
+ return wrapBracket(opener, node, includeMarker);
+ case REPLACE:
+ scanner.setPosition(position);
+ return replaceBracket(opener, node, includeMarker);
}
}
- if (dest != null) {
- // If we got here, we have a link or image
- Node linkOrImage = opener.image ? new Image(dest, title) : new Link(dest, title);
+ return null;
+ }
- // Add all nodes between the opening bracket and now (closing bracket) as child nodes of the link
- Node node = opener.node.getNext();
- while (node != null) {
- Node next = node.getNext();
- linkOrImage.appendChild(node);
- node = next;
- }
+ private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) {
+ // Check to see if we have a link (or image, with a ! in front). The different types:
+ // - Inline: `[foo](/uri)` or with optional title `[foo](/uri "title")`
+ // - Reference links
+ // - Full: `[foo][bar]` (foo is the text and bar is the label that needs to match a reference)
+ // - Collapsed: `[foo][]` (foo is both the text and label)
+ // - Shortcut: `[foo]` (foo is both the text and label)
+
+ // Starting position is after the closing `]`
+ var afterClose = scanner.position();
+
+ // Maybe an inline link/image
+ var destinationTitle = parseInlineDestinationTitle(scanner);
+ if (destinationTitle != null) {
+ var text = scanner.getSource(opener.contentPosition, beforeClose).getContent();
+ return new LinkInfoImpl(opener.markerNode, opener.bracketNode, text, null, destinationTitle.destination, destinationTitle.title, afterClose);
+ }
+ // Not an inline link/image, rewind back to after `]`.
+ scanner.setPosition(afterClose);
+
+ // Maybe a reference link/image like `[foo][bar]`, `[foo][]` or `[foo]`.
+ // Note that even `[foo](` could be a valid link if foo is a reference, which is why we try this even if the `(`
+ // failed to be parsed as an inline link/image before.
+
+ // See if there's a link label like `[bar]` or `[]`
+ var label = parseLinkLabel(scanner);
+ if (label == null) {
+ // No label, rewind back
+ scanner.setPosition(afterClose);
+ }
+ var textIsReference = label == null || label.isEmpty();
+ if (opener.bracketAfter && textIsReference && opener.markerNode == null) {
+ // In case of shortcut or collapsed links, the text is used as the reference. But the reference is not allowed to
+ // contain an unescaped bracket, so if that's the case we don't need to continue. This is an optimization.
+ return null;
+ }
- if (includeSourceSpans) {
- linkOrImage.setSourceSpans(scanner.getSource(opener.markerPosition, scanner.position()).getSourceSpans());
- }
+ var text = scanner.getSource(opener.contentPosition, beforeClose).getContent();
+ return new LinkInfoImpl(opener.markerNode, opener.bracketNode, text, label, null, null, afterClose);
+ }
- // Process delimiters such as emphasis inside link/image
- processDelimiters(opener.previousDelimiter);
- mergeChildTextNodes(linkOrImage);
- // We don't need the corresponding text node anymore, we turned it into a link/image node
- opener.node.unlink();
- removeLastBracket();
+ private Node wrapBracket(Bracket opener, Node wrapperNode, boolean includeMarker) {
+ // Add all nodes between the opening bracket and now (closing bracket) as child nodes of the link
+ Node n = opener.bracketNode.getNext();
+ while (n != null) {
+ Node next = n.getNext();
+ wrapperNode.appendChild(n);
+ n = next;
+ }
- // Links within links are not allowed. We found this link, so there can be no other link around it.
- if (!opener.image) {
- Bracket bracket = lastBracket;
- while (bracket != null) {
- if (!bracket.image) {
- // Disallow link opener. It will still get matched, but will not result in a link.
- bracket.allowed = false;
- }
- bracket = bracket.previous;
- }
- }
+ if (includeSourceSpans) {
+ var startPosition = includeMarker && opener.markerPosition != null ? opener.markerPosition : opener.bracketPosition;
+ wrapperNode.setSourceSpans(scanner.getSource(startPosition, scanner.position()).getSourceSpans());
+ }
- return linkOrImage;
+ // Process delimiters such as emphasis inside link/image
+ processDelimiters(opener.previousDelimiter);
+ mergeChildTextNodes(wrapperNode);
+ // We don't need the corresponding text node anymore, we turned it into a link/image node
+ if (includeMarker && opener.markerNode != null) {
+ opener.markerNode.unlink();
+ }
+ opener.bracketNode.unlink();
+ removeLastBracket();
- } else {
- // No link or image, parse just the bracket as text and continue
- removeLastBracket();
+ // Links within links are not allowed. We found this link, so there can be no other links around it.
+ if (opener.markerNode == null) {
+ disallowPreviousLinks();
+ }
- scanner.setPosition(afterClose);
- return text(scanner.getSource(beforeClose, afterClose));
+ return wrapperNode;
+ }
+
+ private Node replaceBracket(Bracket opener, Node node, boolean includeMarker) {
+ // Remove delimiters (but keep text nodes)
+ while (lastDelimiter != null && lastDelimiter != opener.previousDelimiter) {
+ removeDelimiterKeepNode(lastDelimiter);
+ }
+
+ if (includeSourceSpans) {
+ var startPosition = includeMarker && opener.markerPosition != null ? opener.markerPosition : opener.bracketPosition;
+ node.setSourceSpans(scanner.getSource(startPosition, scanner.position()).getSourceSpans());
}
+
+ removeLastBracket();
+
+ // Remove nodes that we added since the opener, because we're replacing them
+ Node n = includeMarker && opener.markerNode != null ? opener.markerNode : opener.bracketNode;
+ while (n != null) {
+ var next = n.getNext();
+ n.unlink();
+ n = next;
+ }
+
+ // Links within links are not allowed. We found this link, so there can be no other links around it.
+ // Note that this makes any syntax like `[foo]` behave the same as built-in links, which is probably a good
+ // default (it works for footnotes). It might be useful for a `LinkProcessor` to be able to specify the
+ // behavior; something we could add to `LinkResult` in the future if requested.
+ if (opener.markerNode == null || !includeMarker) {
+ disallowPreviousLinks();
+ }
+
+ return node;
}
private void addBracket(Bracket bracket) {
@@ -389,10 +491,50 @@ private void removeLastBracket() {
lastBracket = lastBracket.previous;
}
+ private void disallowPreviousLinks() {
+ Bracket bracket = lastBracket;
+ while (bracket != null) {
+ if (bracket.markerNode == null) {
+ // Disallow link opener. It will still get matched, but will not result in a link.
+ bracket.allowed = false;
+ }
+ bracket = bracket.previous;
+ }
+ }
+
+ /**
+ * Try to parse the destination and an optional title for an inline link/image.
+ */
+ private static DestinationTitle parseInlineDestinationTitle(Scanner scanner) {
+ if (!scanner.next('(')) {
+ return null;
+ }
+
+ scanner.whitespace();
+ String dest = parseLinkDestination(scanner);
+ if (dest == null) {
+ return null;
+ }
+
+ String title = null;
+ int whitespace = scanner.whitespace();
+ // title needs a whitespace before
+ if (whitespace >= 1) {
+ title = parseLinkTitle(scanner);
+ scanner.whitespace();
+ }
+ if (!scanner.next(')')) {
+ // Don't have a closing `)`, so it's not a destination and title.
+ // Note that something like `[foo](` could still be valid later, `(` will just be text.
+ return null;
+ }
+ return new DestinationTitle(dest, title);
+ }
+
/**
* Attempt to parse link destination, returning the string or null if no match.
*/
- private String parseLinkDestination(Scanner scanner) {
+ private static String parseLinkDestination(Scanner scanner) {
char delimiter = scanner.peek();
Position start = scanner.position();
if (!LinkScanner.scanLinkDestination(scanner)) {
@@ -414,7 +556,7 @@ private String parseLinkDestination(Scanner scanner) {
/**
* Attempt to parse link title (sans quotes), returning the string or null if no match.
*/
- private String parseLinkTitle(Scanner scanner) {
+ private static String parseLinkTitle(Scanner scanner) {
Position start = scanner.position();
if (!LinkScanner.scanLinkTitle(scanner)) {
return null;
@@ -429,7 +571,7 @@ private String parseLinkTitle(Scanner scanner) {
/**
* Attempt to parse a link label, returning the label between the brackets or null.
*/
- String parseLinkLabel(Scanner scanner) {
+ static String parseLinkLabel(Scanner scanner) {
if (!scanner.next('[')) {
return null;
}
@@ -456,7 +598,9 @@ String parseLinkLabel(Scanner scanner) {
private Node parseLineBreak() {
scanner.next();
- if (trailingSpaces >= 2) {
+ var hard = trailingSpaces >= 2;
+ trailingSpaces = 0;
+ if (hard) {
return new HardLineBreak();
} else {
return new SoftLineBreak();
@@ -752,4 +896,74 @@ private static class DelimiterData {
this.canClose = canClose;
}
}
+
+ /**
+ * A destination and optional title for a link or image.
+ */
+ private static class DestinationTitle {
+ final String destination;
+ final String title;
+
+ public DestinationTitle(String destination, String title) {
+ this.destination = destination;
+ this.title = title;
+ }
+ }
+
+ private static class LinkInfoImpl implements LinkInfo {
+
+ private final Text marker;
+ private final Text openingBracket;
+ private final String text;
+ private final String label;
+ private final String destination;
+ private final String title;
+ private final Position afterTextBracket;
+
+ private LinkInfoImpl(Text marker, Text openingBracket, String text, String label,
+ String destination, String title, Position afterTextBracket) {
+ this.marker = marker;
+ this.openingBracket = openingBracket;
+ this.text = text;
+ this.label = label;
+ this.destination = destination;
+ this.title = title;
+ this.afterTextBracket = afterTextBracket;
+ }
+
+ @Override
+ public Text marker() {
+ return marker;
+ }
+
+ @Override
+ public Text openingBracket() {
+ return openingBracket;
+ }
+
+ @Override
+ public String text() {
+ return text;
+ }
+
+ @Override
+ public String label() {
+ return label;
+ }
+
+ @Override
+ public String destination() {
+ return destination;
+ }
+
+ @Override
+ public String title() {
+ return title;
+ }
+
+ @Override
+ public Position afterTextBracket() {
+ return afterTextBracket;
+ }
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java b/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java
index b58e669ef..637d3b111 100644
--- a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java
@@ -10,6 +10,7 @@
import org.commonmark.parser.beta.Scanner;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -102,6 +103,14 @@ State getState() {
return state;
}
+ List removeLines(int lines) {
+ var removedSpans = Collections.unmodifiableList(new ArrayList<>(
+ sourceSpans.subList(Math.max(sourceSpans.size() - lines, 0), sourceSpans.size())));
+ removeLast(lines, paragraphLines);
+ removeLast(lines, sourceSpans);
+ return removedSpans;
+ }
+
private boolean startDefinition(Scanner scanner) {
// Finish any outstanding references now. We don't do this earlier because we need addSourceSpan to have been
// called before we do it.
@@ -269,6 +278,16 @@ private void finishReference() {
title = null;
}
+ private static void removeLast(int n, List list) {
+ if (n >= list.size()) {
+ list.clear();
+ } else {
+ for (int i = 0; i < n; i++) {
+ list.remove(list.size() - 1);
+ }
+ }
+ }
+
enum State {
// Looking for the start of a definition, i.e. `[`
START_DEFINITION,
diff --git a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitions.java b/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitions.java
deleted file mode 100644
index 8fbdb982a..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitions.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.commonmark.internal;
-
-import org.commonmark.internal.util.Escaping;
-import org.commonmark.node.LinkReferenceDefinition;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public class LinkReferenceDefinitions {
-
- // LinkedHashMap for determinism and to preserve document order
- private final Map definitions = new LinkedHashMap<>();
-
- public void add(LinkReferenceDefinition definition) {
- String normalizedLabel = Escaping.normalizeLabelContent(definition.getLabel());
-
- // spec: When there are multiple matching link reference definitions, the first is used
- if (!definitions.containsKey(normalizedLabel)) {
- definitions.put(normalizedLabel, definition);
- }
- }
-
- public LinkReferenceDefinition get(String label) {
- String normalizedLabel = Escaping.normalizeLabelContent(label);
- return definitions.get(normalizedLabel);
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/ListBlockParser.java b/commonmark/src/main/java/org/commonmark/internal/ListBlockParser.java
index d77744da7..fbf034757 100644
--- a/commonmark/src/main/java/org/commonmark/internal/ListBlockParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/ListBlockParser.java
@@ -128,7 +128,7 @@ private static ListMarkerData parseListMarker(CharSequence line, int index) {
}
}
- // spec: An ordered list marker is a sequence of 1–9 arabic digits (0-9), followed by either a `.` character or a
+ // spec: An ordered list marker is a sequence of 1-9 arabic digits (0-9), followed by either a `.` character or a
// `)` character.
private static ListMarkerData parseOrderedList(CharSequence line, int index) {
int digits = 0;
diff --git a/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java
index 89328ef2a..27eb1e647 100644
--- a/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java
@@ -1,9 +1,6 @@
package org.commonmark.internal;
-import org.commonmark.node.Block;
-import org.commonmark.node.LinkReferenceDefinition;
-import org.commonmark.node.Paragraph;
-import org.commonmark.node.SourceSpan;
+import org.commonmark.node.*;
import org.commonmark.parser.InlineParser;
import org.commonmark.parser.SourceLine;
import org.commonmark.parser.SourceLines;
@@ -49,8 +46,21 @@ public void addSourceSpan(SourceSpan sourceSpan) {
linkReferenceDefinitionParser.addSourceSpan(sourceSpan);
}
+ @Override
+ public List> getDefinitions() {
+ var map = new DefinitionMap<>(LinkReferenceDefinition.class);
+ for (var def : linkReferenceDefinitionParser.getDefinitions()) {
+ map.putIfAbsent(def.getLabel(), def);
+ }
+ return List.of(map);
+ }
+
@Override
public void closeBlock() {
+ for (var def : linkReferenceDefinitionParser.getDefinitions()) {
+ block.insertBefore(def);
+ }
+
if (linkReferenceDefinitionParser.getParagraphLines().isEmpty()) {
block.unlink();
} else {
@@ -70,7 +80,7 @@ public SourceLines getParagraphLines() {
return linkReferenceDefinitionParser.getParagraphLines();
}
- public List getDefinitions() {
- return linkReferenceDefinitionParser.getDefinitions();
+ public List removeLines(int lines) {
+ return linkReferenceDefinitionParser.removeLines(lines);
}
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/AutolinkInlineParser.java b/commonmark/src/main/java/org/commonmark/internal/inline/AutolinkInlineParser.java
index 36c43e196..a18966e54 100644
--- a/commonmark/src/main/java/org/commonmark/internal/inline/AutolinkInlineParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/AutolinkInlineParser.java
@@ -3,9 +3,9 @@
import org.commonmark.node.Link;
import org.commonmark.node.Text;
import org.commonmark.parser.SourceLines;
-import org.commonmark.parser.beta.Position;
-import org.commonmark.parser.beta.Scanner;
+import org.commonmark.parser.beta.*;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -46,4 +46,16 @@ public ParsedInline tryParse(InlineParserState inlineParserState) {
}
return ParsedInline.none();
}
+
+ public static class Factory implements InlineContentParserFactory {
+ @Override
+ public Set getTriggerCharacters() {
+ return Set.of('<');
+ }
+
+ @Override
+ public InlineContentParser create() {
+ return new AutolinkInlineParser();
+ }
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/BackslashInlineParser.java b/commonmark/src/main/java/org/commonmark/internal/inline/BackslashInlineParser.java
index 02c136951..7baeed4de 100644
--- a/commonmark/src/main/java/org/commonmark/internal/inline/BackslashInlineParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/BackslashInlineParser.java
@@ -3,8 +3,9 @@
import org.commonmark.internal.util.Escaping;
import org.commonmark.node.HardLineBreak;
import org.commonmark.node.Text;
-import org.commonmark.parser.beta.Scanner;
+import org.commonmark.parser.beta.*;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -32,4 +33,16 @@ public ParsedInline tryParse(InlineParserState inlineParserState) {
return ParsedInline.of(new Text("\\"), scanner.position());
}
}
+
+ public static class Factory implements InlineContentParserFactory {
+ @Override
+ public Set getTriggerCharacters() {
+ return Set.of('\\');
+ }
+
+ @Override
+ public InlineContentParser create() {
+ return new BackslashInlineParser();
+ }
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/BackticksInlineParser.java b/commonmark/src/main/java/org/commonmark/internal/inline/BackticksInlineParser.java
index bef8e1f99..b8e8984e8 100644
--- a/commonmark/src/main/java/org/commonmark/internal/inline/BackticksInlineParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/BackticksInlineParser.java
@@ -3,10 +3,11 @@
import org.commonmark.node.Code;
import org.commonmark.node.Text;
import org.commonmark.parser.SourceLines;
-import org.commonmark.parser.beta.Position;
-import org.commonmark.parser.beta.Scanner;
+import org.commonmark.parser.beta.*;
import org.commonmark.text.Characters;
+import java.util.Set;
+
/**
* Attempt to parse backticks, returning either a backtick code span or a literal sequence of backticks.
*/
@@ -47,4 +48,16 @@ public ParsedInline tryParse(InlineParserState inlineParserState) {
Text text = new Text(source.getContent());
return ParsedInline.of(text, afterOpening);
}
+
+ public static class Factory implements InlineContentParserFactory {
+ @Override
+ public Set getTriggerCharacters() {
+ return Set.of('`');
+ }
+
+ @Override
+ public InlineContentParser create() {
+ return new BackticksInlineParser();
+ }
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/CoreLinkProcessor.java b/commonmark/src/main/java/org/commonmark/internal/inline/CoreLinkProcessor.java
new file mode 100644
index 000000000..528750aba
--- /dev/null
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/CoreLinkProcessor.java
@@ -0,0 +1,37 @@
+package org.commonmark.internal.inline;
+
+import org.commonmark.node.Image;
+import org.commonmark.node.Link;
+import org.commonmark.node.LinkReferenceDefinition;
+import org.commonmark.parser.InlineParserContext;
+import org.commonmark.parser.beta.LinkInfo;
+import org.commonmark.parser.beta.LinkProcessor;
+import org.commonmark.parser.beta.LinkResult;
+import org.commonmark.parser.beta.Scanner;
+
+public class CoreLinkProcessor implements LinkProcessor {
+
+ @Override
+ public LinkResult process(LinkInfo linkInfo, Scanner scanner, InlineParserContext context) {
+ if (linkInfo.destination() != null) {
+ // Inline link
+ return process(linkInfo, scanner, linkInfo.destination(), linkInfo.title());
+ }
+
+ var label = linkInfo.label();
+ var ref = label != null && !label.isEmpty() ? label : linkInfo.text();
+ var def = context.getDefinition(LinkReferenceDefinition.class, ref);
+ if (def != null) {
+ // Reference link
+ return process(linkInfo, scanner, def.getDestination(), def.getTitle());
+ }
+ return LinkResult.none();
+ }
+
+ private static LinkResult process(LinkInfo linkInfo, Scanner scanner, String destination, String title) {
+ if (linkInfo.marker() != null && linkInfo.marker().getLiteral().equals("!")) {
+ return LinkResult.wrapTextIn(new Image(destination, title), scanner.position()).includeMarker();
+ }
+ return LinkResult.wrapTextIn(new Link(destination, title), scanner.position());
+ }
+}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/EntityInlineParser.java b/commonmark/src/main/java/org/commonmark/internal/inline/EntityInlineParser.java
index 2b7d296fb..c24e60747 100644
--- a/commonmark/src/main/java/org/commonmark/internal/inline/EntityInlineParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/EntityInlineParser.java
@@ -1,13 +1,14 @@
package org.commonmark.internal.inline;
-import org.commonmark.text.AsciiMatcher;
import org.commonmark.internal.util.Html5Entities;
import org.commonmark.node.Text;
-import org.commonmark.parser.beta.Position;
-import org.commonmark.parser.beta.Scanner;
+import org.commonmark.parser.beta.*;
+import org.commonmark.text.AsciiMatcher;
+
+import java.util.Set;
/**
- * Attempts to parse a HTML entity or numeric character reference.
+ * Attempts to parse an HTML entity or numeric character reference.
*/
public class EntityInlineParser implements InlineContentParser {
@@ -52,4 +53,17 @@ private ParsedInline entity(Scanner scanner, Position start) {
String text = scanner.getSource(start, scanner.position()).getContent();
return ParsedInline.of(new Text(Html5Entities.entityToString(text)), scanner.position());
}
+
+ public static class Factory implements InlineContentParserFactory {
+
+ @Override
+ public Set getTriggerCharacters() {
+ return Set.of('&');
+ }
+
+ @Override
+ public InlineContentParser create() {
+ return new EntityInlineParser();
+ }
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/HtmlInlineParser.java b/commonmark/src/main/java/org/commonmark/internal/inline/HtmlInlineParser.java
index 6dc525cb9..a48ea5022 100644
--- a/commonmark/src/main/java/org/commonmark/internal/inline/HtmlInlineParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/HtmlInlineParser.java
@@ -1,9 +1,10 @@
package org.commonmark.internal.inline;
-import org.commonmark.text.AsciiMatcher;
import org.commonmark.node.HtmlInline;
-import org.commonmark.parser.beta.Position;
-import org.commonmark.parser.beta.Scanner;
+import org.commonmark.parser.beta.*;
+import org.commonmark.text.AsciiMatcher;
+
+import java.util.Set;
/**
* Attempt to parse inline HTML.
@@ -200,4 +201,17 @@ private static boolean tryDeclaration(Scanner scanner) {
}
return false;
}
+
+ public static class Factory implements InlineContentParserFactory {
+
+ @Override
+ public Set getTriggerCharacters() {
+ return Set.of('<');
+ }
+
+ @Override
+ public InlineContentParser create() {
+ return new HtmlInlineParser();
+ }
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/InlineContentParser.java b/commonmark/src/main/java/org/commonmark/internal/inline/InlineContentParser.java
deleted file mode 100644
index 755ee3135..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/inline/InlineContentParser.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.commonmark.internal.inline;
-
-public interface InlineContentParser {
-
- ParsedInline tryParse(InlineParserState inlineParserState);
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/LinkResultImpl.java b/commonmark/src/main/java/org/commonmark/internal/inline/LinkResultImpl.java
new file mode 100644
index 000000000..c05b24451
--- /dev/null
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/LinkResultImpl.java
@@ -0,0 +1,46 @@
+package org.commonmark.internal.inline;
+
+import org.commonmark.node.Node;
+import org.commonmark.parser.beta.LinkResult;
+import org.commonmark.parser.beta.Position;
+
+public class LinkResultImpl implements LinkResult {
+ @Override
+ public LinkResult includeMarker() {
+ includeMarker = true;
+ return this;
+ }
+
+ public enum Type {
+ WRAP,
+ REPLACE
+ }
+
+ private final Type type;
+ private final Node node;
+ private final Position position;
+
+ private boolean includeMarker = false;
+
+ public LinkResultImpl(Type type, Node node, Position position) {
+ this.type = type;
+ this.node = node;
+ this.position = position;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public boolean isIncludeMarker() {
+ return includeMarker;
+ }
+}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/ParsedInline.java b/commonmark/src/main/java/org/commonmark/internal/inline/ParsedInline.java
deleted file mode 100644
index 7223c1687..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/inline/ParsedInline.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.commonmark.internal.inline;
-
-import org.commonmark.node.Node;
-import org.commonmark.parser.beta.Position;
-
-public abstract class ParsedInline {
-
- protected ParsedInline() {
- }
-
- public static ParsedInline none() {
- return null;
- }
-
- public static ParsedInline of(Node node, Position position) {
- if (node == null) {
- throw new NullPointerException("node must not be null");
- }
- if (position == null) {
- throw new NullPointerException("position must not be null");
- }
- return new ParsedInlineImpl(node, position);
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/inline/ParsedInlineImpl.java b/commonmark/src/main/java/org/commonmark/internal/inline/ParsedInlineImpl.java
index 55f9cc4da..a77630610 100644
--- a/commonmark/src/main/java/org/commonmark/internal/inline/ParsedInlineImpl.java
+++ b/commonmark/src/main/java/org/commonmark/internal/inline/ParsedInlineImpl.java
@@ -1,13 +1,14 @@
package org.commonmark.internal.inline;
import org.commonmark.node.Node;
+import org.commonmark.parser.beta.ParsedInline;
import org.commonmark.parser.beta.Position;
-public class ParsedInlineImpl extends ParsedInline {
+public class ParsedInlineImpl implements ParsedInline {
private final Node node;
private final Position position;
- ParsedInlineImpl(Node node, Position position) {
+ public ParsedInlineImpl(Node node, Position position) {
this.node = node;
this.position = position;
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/renderer/NodeRendererMap.java b/commonmark/src/main/java/org/commonmark/internal/renderer/NodeRendererMap.java
index e3adaa11f..c74f90758 100644
--- a/commonmark/src/main/java/org/commonmark/internal/renderer/NodeRendererMap.java
+++ b/commonmark/src/main/java/org/commonmark/internal/renderer/NodeRendererMap.java
@@ -3,24 +3,39 @@
import org.commonmark.node.Node;
import org.commonmark.renderer.NodeRenderer;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class NodeRendererMap {
+ private final List nodeRenderers = new ArrayList<>();
private final Map, NodeRenderer> renderers = new HashMap<>(32);
+ /**
+ * Set the renderer for each {@link NodeRenderer#getNodeTypes()}, unless there was already a renderer set (first wins).
+ */
public void add(NodeRenderer nodeRenderer) {
- for (Class extends Node> nodeType : nodeRenderer.getNodeTypes()) {
- // Overwrite existing renderer
- renderers.put(nodeType, nodeRenderer);
+ nodeRenderers.add(nodeRenderer);
+ for (var nodeType : nodeRenderer.getNodeTypes()) {
+ // The first node renderer for a node type "wins".
+ renderers.putIfAbsent(nodeType, nodeRenderer);
}
}
public void render(Node node) {
- NodeRenderer nodeRenderer = renderers.get(node.getClass());
+ var nodeRenderer = renderers.get(node.getClass());
if (nodeRenderer != null) {
nodeRenderer.render(node);
}
}
+
+ public void beforeRoot(Node node) {
+ nodeRenderers.forEach(r -> r.beforeRoot(node));
+ }
+
+ public void afterRoot(Node node) {
+ nodeRenderers.forEach(r -> r.afterRoot(node));
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/renderer/text/BulletListHolder.java b/commonmark/src/main/java/org/commonmark/internal/renderer/text/BulletListHolder.java
deleted file mode 100644
index a9271dcdb..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/BulletListHolder.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.commonmark.internal.renderer.text;
-
-import org.commonmark.node.BulletList;
-
-public class BulletListHolder extends ListHolder {
- private final String marker;
-
- public BulletListHolder(ListHolder parent, BulletList list) {
- super(parent);
- marker = list.getMarker();
- }
-
- public String getMarker() {
- return marker;
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java b/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java
deleted file mode 100644
index cb06d4a9d..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.commonmark.internal.renderer.text;
-
-public abstract class ListHolder {
- private static final String INDENT_DEFAULT = " ";
- private static final String INDENT_EMPTY = "";
-
- private final ListHolder parent;
- private final String indent;
-
- ListHolder(ListHolder parent) {
- this.parent = parent;
-
- if (parent != null) {
- indent = parent.indent + INDENT_DEFAULT;
- } else {
- indent = INDENT_EMPTY;
- }
- }
-
- public ListHolder getParent() {
- return parent;
- }
-
- public String getIndent() {
- return indent;
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/renderer/text/OrderedListHolder.java b/commonmark/src/main/java/org/commonmark/internal/renderer/text/OrderedListHolder.java
deleted file mode 100644
index e5e470951..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/OrderedListHolder.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.commonmark.internal.renderer.text;
-
-import org.commonmark.node.OrderedList;
-
-public class OrderedListHolder extends ListHolder {
- private final String delimiter;
- private int counter;
-
- public OrderedListHolder(ListHolder parent, OrderedList list) {
- super(parent);
- delimiter = list.getMarkerDelimiter() != null ? list.getMarkerDelimiter() : ".";
- counter = list.getMarkerStartNumber() != null ? list.getMarkerStartNumber() : 1;
- }
-
- public String getDelimiter() {
- return delimiter;
- }
-
- public int getCounter() {
- return counter;
- }
-
- public void increaseCounter() {
- counter++;
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/util/Escaping.java b/commonmark/src/main/java/org/commonmark/internal/util/Escaping.java
index ade64d933..3350003c0 100644
--- a/commonmark/src/main/java/org/commonmark/internal/util/Escaping.java
+++ b/commonmark/src/main/java/org/commonmark/internal/util/Escaping.java
@@ -1,6 +1,6 @@
package org.commonmark.internal.util;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -49,7 +49,7 @@ public void replace(String input, StringBuilder sb) {
sb.append(input, 1, input.length());
}
} else {
- byte[] bytes = input.getBytes(Charset.forName("UTF-8"));
+ byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
for (byte b : bytes) {
sb.append('%');
sb.append(HEX_DIGITS[(b >> 4) & 0xF]);
@@ -114,11 +114,11 @@ public static String percentEncodeUrl(String s) {
public static String normalizeLabelContent(String input) {
String trimmed = input.trim();
- // This is necessary to correctly case fold "ẞ" to "SS":
- // "ẞ".toLowerCase(Locale.ROOT) -> "ß"
- // "ß".toUpperCase(Locale.ROOT) -> "SS"
+ // This is necessary to correctly case fold "\u1E9E" (LATIN CAPITAL LETTER SHARP S) to "SS":
+ // "\u1E9E".toLowerCase(Locale.ROOT) -> "\u00DF" (LATIN SMALL LETTER SHARP S)
+ // "\u00DF".toUpperCase(Locale.ROOT) -> "SS"
// Note that doing upper first (or only upper without lower) wouldn't work because:
- // "ẞ".toUpperCase(Locale.ROOT) -> "ẞ"
+ // "\u1E9E".toUpperCase(Locale.ROOT) -> "\u1E9E"
String caseFolded = trimmed.toLowerCase(Locale.ROOT).toUpperCase(Locale.ROOT);
return WHITESPACE.matcher(caseFolded).replaceAll(" ");
diff --git a/commonmark/src/main/java/org/commonmark/internal/util/Html5Entities.java b/commonmark/src/main/java/org/commonmark/internal/util/Html5Entities.java
index 523c596ed..8da53c053 100644
--- a/commonmark/src/main/java/org/commonmark/internal/util/Html5Entities.java
+++ b/commonmark/src/main/java/org/commonmark/internal/util/Html5Entities.java
@@ -12,7 +12,7 @@
public class Html5Entities {
private static final Map NAMED_CHARACTER_REFERENCES = readEntities();
- private static final String ENTITY_PATH = "/org/commonmark/internal/util/entities.properties";
+ private static final String ENTITY_PATH = "/org/commonmark/internal/util/entities.txt";
public static String entityToString(String input) {
if (!input.startsWith("&") || !input.endsWith(";")) {
diff --git a/commonmark/src/main/java/org/commonmark/internal/util/LineReader.java b/commonmark/src/main/java/org/commonmark/internal/util/LineReader.java
new file mode 100644
index 000000000..b44098257
--- /dev/null
+++ b/commonmark/src/main/java/org/commonmark/internal/util/LineReader.java
@@ -0,0 +1,149 @@
+package org.commonmark.internal.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Reads lines from a reader like {@link java.io.BufferedReader} but also returns the line terminators.
+ *
+ * Line terminators can be either a line feed {@code "\n"}, carriage return {@code "\r"}, or a carriage return followed
+ * by a line feed {@code "\r\n"}. Call {@link #getLineTerminator()} after {@link #readLine()} to obtain the
+ * corresponding line terminator. If a stream has a line at the end without a terminator, {@link #getLineTerminator()}
+ * returns {@code null}.
+ */
+public class LineReader implements Closeable {
+
+ // Same as java.io.BufferedReader
+ static final int CHAR_BUFFER_SIZE = 8192;
+ static final int EXPECTED_LINE_LENGTH = 80;
+
+ private Reader reader;
+ private char[] cbuf;
+
+ private int position = 0;
+ private int limit = 0;
+
+ private String lineTerminator = null;
+
+ public LineReader(Reader reader) {
+ this.reader = reader;
+ this.cbuf = new char[CHAR_BUFFER_SIZE];
+ }
+
+ /**
+ * Read a line of text.
+ *
+ * @return the line, or {@code null} when the end of the stream has been reached and no more lines can be read
+ */
+ public String readLine() throws IOException {
+ StringBuilder sb = null;
+ boolean cr = false;
+
+ while (true) {
+ if (position >= limit) {
+ fill();
+ }
+
+ if (cr) {
+ // We saw a CR before, check if we have CR LF or just CR.
+ if (position < limit && cbuf[position] == '\n') {
+ position++;
+ return line(sb.toString(), "\r\n");
+ } else {
+ return line(sb.toString(), "\r");
+ }
+ }
+
+ if (position >= limit) {
+ // End of stream, return either the last line without terminator or null for end.
+ return line(sb != null ? sb.toString() : null, null);
+ }
+
+ int start = position;
+ int i = position;
+ for (; i < limit; i++) {
+ char c = cbuf[i];
+ if (c == '\n') {
+ position = i + 1;
+ return line(finish(sb, start, i), "\n");
+ } else if (c == '\r') {
+ if (i + 1 < limit) {
+ // We know what the next character is, so we can check now whether we have
+ // a CR LF or just a CR and return.
+ if (cbuf[i + 1] == '\n') {
+ position = i + 2;
+ return line(finish(sb, start, i), "\r\n");
+ } else {
+ position = i + 1;
+ return line(finish(sb, start, i), "\r");
+ }
+ } else {
+ // We don't know what the next character is yet, check on next iteration.
+ cr = true;
+ position = i + 1;
+ break;
+ }
+ }
+ }
+
+ if (position < i) {
+ position = i;
+ }
+
+ // Haven't found a finished line yet, copy the data from the buffer so that we can fill
+ // the buffer again.
+ if (sb == null) {
+ sb = new StringBuilder(EXPECTED_LINE_LENGTH);
+ }
+ sb.append(cbuf, start, i - start);
+ }
+ }
+
+ /**
+ * Return the line terminator of the last read line from {@link #readLine()}.
+ *
+ * @return {@code "\n"}, {@code "\r"}, {@code "\r\n"}, or {@code null}
+ */
+ public String getLineTerminator() {
+ return lineTerminator;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (reader == null) {
+ return;
+ }
+ try {
+ reader.close();
+ } finally {
+ reader = null;
+ cbuf = null;
+ }
+ }
+
+ private void fill() throws IOException {
+ int read;
+ do {
+ read = reader.read(cbuf, 0, cbuf.length);
+ } while (read == 0);
+ if (read > 0) {
+ limit = read;
+ position = 0;
+ }
+ }
+
+ private String line(String line, String lineTerminator) {
+ this.lineTerminator = lineTerminator;
+ return line;
+ }
+
+ private String finish(StringBuilder sb, int start, int end) {
+ int len = end - start;
+ if (sb == null) {
+ return new String(cbuf, start, len);
+ } else {
+ return sb.append(cbuf, start, len).toString();
+ }
+ }
+}
diff --git a/commonmark/src/main/java/org/commonmark/node/Block.java b/commonmark/src/main/java/org/commonmark/node/Block.java
index 753447c5c..332346b0e 100644
--- a/commonmark/src/main/java/org/commonmark/node/Block.java
+++ b/commonmark/src/main/java/org/commonmark/node/Block.java
@@ -5,6 +5,7 @@
*/
public abstract class Block extends Node {
+ @Override
public Block getParent() {
return (Block) super.getParent();
}
diff --git a/commonmark/src/main/java/org/commonmark/node/BlockQuote.java b/commonmark/src/main/java/org/commonmark/node/BlockQuote.java
index 160f25ae2..f68252398 100644
--- a/commonmark/src/main/java/org/commonmark/node/BlockQuote.java
+++ b/commonmark/src/main/java/org/commonmark/node/BlockQuote.java
@@ -1,5 +1,15 @@
package org.commonmark.node;
+/**
+ * A block quote, e.g.:
+ *
+ * > Some quoted text
+ *
+ *
+ * Note that child nodes are themselves blocks, e.g. {@link Paragraph}, {@link ListBlock} etc.
+ *
+ * @see CommonMark Spec
+ */
public class BlockQuote extends Block {
@Override
diff --git a/commonmark/src/main/java/org/commonmark/node/BulletList.java b/commonmark/src/main/java/org/commonmark/node/BulletList.java
index 4d5c2a894..014f4d3b2 100644
--- a/commonmark/src/main/java/org/commonmark/node/BulletList.java
+++ b/commonmark/src/main/java/org/commonmark/node/BulletList.java
@@ -1,5 +1,17 @@
package org.commonmark.node;
+/**
+ * A bullet list, e.g.:
+ *
+ * - One
+ * - Two
+ * - Three
+ *
+ *
+ * The children are {@link ListItem} blocks, which contain other blocks (or nested lists).
+ *
+ * @see CommonMark Spec: List items
+ */
public class BulletList extends ListBlock {
private String marker;
diff --git a/commonmark/src/main/java/org/commonmark/node/Code.java b/commonmark/src/main/java/org/commonmark/node/Code.java
index 0b47ecb71..3b79e0c9c 100644
--- a/commonmark/src/main/java/org/commonmark/node/Code.java
+++ b/commonmark/src/main/java/org/commonmark/node/Code.java
@@ -1,5 +1,13 @@
package org.commonmark.node;
+/**
+ * Inline code span, e.g.:
+ *
+ * Some `inline code`
+ *
+ *
+ * @see CommonMark Spec
+ */
public class Code extends Node {
private String literal;
@@ -16,6 +24,10 @@ public void accept(Visitor visitor) {
visitor.visit(this);
}
+ /**
+ * @return the literal text in the code span (note that it's not necessarily the raw text between tildes,
+ * e.g. when spaces are stripped)
+ */
public String getLiteral() {
return literal;
}
diff --git a/commonmark/src/main/java/org/commonmark/node/CustomBlock.java b/commonmark/src/main/java/org/commonmark/node/CustomBlock.java
index 6596ec1a0..cad88933a 100644
--- a/commonmark/src/main/java/org/commonmark/node/CustomBlock.java
+++ b/commonmark/src/main/java/org/commonmark/node/CustomBlock.java
@@ -1,5 +1,8 @@
package org.commonmark.node;
+/**
+ * A block that extensions can subclass to define custom blocks (not part of the core specification).
+ */
public abstract class CustomBlock extends Block {
@Override
diff --git a/commonmark/src/main/java/org/commonmark/node/CustomNode.java b/commonmark/src/main/java/org/commonmark/node/CustomNode.java
index a68e5cc11..88f0254da 100644
--- a/commonmark/src/main/java/org/commonmark/node/CustomNode.java
+++ b/commonmark/src/main/java/org/commonmark/node/CustomNode.java
@@ -1,5 +1,8 @@
package org.commonmark.node;
+/**
+ * A node that extensions can subclass to define custom nodes (not part of the core specification).
+ */
public abstract class CustomNode extends Node {
@Override
public void accept(Visitor visitor) {
diff --git a/commonmark/src/main/java/org/commonmark/node/DefinitionMap.java b/commonmark/src/main/java/org/commonmark/node/DefinitionMap.java
new file mode 100644
index 000000000..59cb88274
--- /dev/null
+++ b/commonmark/src/main/java/org/commonmark/node/DefinitionMap.java
@@ -0,0 +1,67 @@
+package org.commonmark.node;
+
+import org.commonmark.internal.util.Escaping;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A map that can be used to store and look up reference definitions by a label. The labels are case-insensitive and
+ * normalized, the same way as for {@link LinkReferenceDefinition} nodes.
+ *
+ * @param the type of value
+ */
+public class DefinitionMap {
+
+ private final Class type;
+ // LinkedHashMap for determinism and to preserve document order
+ private final Map definitions = new LinkedHashMap<>();
+
+ public DefinitionMap(Class type) {
+ this.type = type;
+ }
+
+ public Class getType() {
+ return type;
+ }
+
+ public void addAll(DefinitionMap that) {
+ for (var entry : that.definitions.entrySet()) {
+ // Note that keys are already normalized, so we can add them directly
+ definitions.putIfAbsent(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Store a new definition unless one is already in the map. If there is no definition for that label yet, return null.
+ * Otherwise, return the existing definition.
+ *
+ * The label is normalized by the definition map before storing.
+ */
+ public D putIfAbsent(String label, D definition) {
+ String normalizedLabel = Escaping.normalizeLabelContent(label);
+
+ // spec: When there are multiple matching link reference definitions, the first is used
+ return definitions.putIfAbsent(normalizedLabel, definition);
+ }
+
+ /**
+ * Look up a definition by label. The label is normalized by the definition map before lookup.
+ *
+ * @return the value or null
+ */
+ public D get(String label) {
+ String normalizedLabel = Escaping.normalizeLabelContent(label);
+ return definitions.get(normalizedLabel);
+ }
+
+ public Set keySet() {
+ return definitions.keySet();
+ }
+
+ public Collection values() {
+ return definitions.values();
+ }
+}
diff --git a/commonmark/src/main/java/org/commonmark/node/Document.java b/commonmark/src/main/java/org/commonmark/node/Document.java
index 5b7e74189..b4968c206 100644
--- a/commonmark/src/main/java/org/commonmark/node/Document.java
+++ b/commonmark/src/main/java/org/commonmark/node/Document.java
@@ -1,5 +1,8 @@
package org.commonmark.node;
+/**
+ * The root block of a document, containing the top-level blocks.
+ */
public class Document extends Block {
@Override
diff --git a/commonmark/src/main/java/org/commonmark/node/Emphasis.java b/commonmark/src/main/java/org/commonmark/node/Emphasis.java
index 9877e7b63..5efc8c327 100644
--- a/commonmark/src/main/java/org/commonmark/node/Emphasis.java
+++ b/commonmark/src/main/java/org/commonmark/node/Emphasis.java
@@ -1,5 +1,13 @@
package org.commonmark.node;
+/**
+ * Emphasis, e.g.:
+ *
+ * Some *emphasis* or _emphasis_
+ *
+ *
+ * @see CommonMark Spec: Emphasis and strong emphasis
+ */
public class Emphasis extends Node implements Delimited {
private String delimiter;
diff --git a/commonmark/src/main/java/org/commonmark/node/FencedCodeBlock.java b/commonmark/src/main/java/org/commonmark/node/FencedCodeBlock.java
index 205ef9126..0e279a470 100644
--- a/commonmark/src/main/java/org/commonmark/node/FencedCodeBlock.java
+++ b/commonmark/src/main/java/org/commonmark/node/FencedCodeBlock.java
@@ -1,5 +1,17 @@
package org.commonmark.node;
+/**
+ * A fenced code block, e.g.:
+ *
+ * ```
+ * foo
+ * bar
+ * ```
+ *
+ *
+ *
+ * @see CommonMark Spec
+ */
public class FencedCodeBlock extends Block {
private String fenceCharacter;
@@ -67,7 +79,7 @@ public void setFenceIndent(int fenceIndent) {
}
/**
- * @see CommonMark spec
+ * @see CommonMark spec
*/
public String getInfo() {
return info;
diff --git a/commonmark/src/main/java/org/commonmark/node/HardLineBreak.java b/commonmark/src/main/java/org/commonmark/node/HardLineBreak.java
index 0640fc3c4..28874ec01 100644
--- a/commonmark/src/main/java/org/commonmark/node/HardLineBreak.java
+++ b/commonmark/src/main/java/org/commonmark/node/HardLineBreak.java
@@ -1,5 +1,15 @@
package org.commonmark.node;
+/**
+ * A hard line break, e.g.:
+ *
+ * line\
+ * break
+ *
+ *
+ *
+ * @see CommonMark Spec
+ */
public class HardLineBreak extends Node {
@Override
diff --git a/commonmark/src/main/java/org/commonmark/node/Heading.java b/commonmark/src/main/java/org/commonmark/node/Heading.java
index 41f3b2504..5369d8739 100644
--- a/commonmark/src/main/java/org/commonmark/node/Heading.java
+++ b/commonmark/src/main/java/org/commonmark/node/Heading.java
@@ -1,5 +1,17 @@
package org.commonmark.node;
+/**
+ * A heading, e.g.:
+ *
+ * First heading
+ * =============
+ *
+ * ## Another heading
+ *
+ *
+ * @see CommonMark Spec: ATX headings
+ * @see CommonMark Spec: Setext headings
+ */
public class Heading extends Block {
private int level;
diff --git a/commonmark/src/main/java/org/commonmark/node/HtmlBlock.java b/commonmark/src/main/java/org/commonmark/node/HtmlBlock.java
index ad46c56ce..fbe00927d 100644
--- a/commonmark/src/main/java/org/commonmark/node/HtmlBlock.java
+++ b/commonmark/src/main/java/org/commonmark/node/HtmlBlock.java
@@ -3,7 +3,7 @@
/**
* HTML block
*
- * @see CommonMark Spec
+ * @see CommonMark Spec
*/
public class HtmlBlock extends Block {
diff --git a/commonmark/src/main/java/org/commonmark/node/HtmlInline.java b/commonmark/src/main/java/org/commonmark/node/HtmlInline.java
index 291fcde3c..35360c639 100644
--- a/commonmark/src/main/java/org/commonmark/node/HtmlInline.java
+++ b/commonmark/src/main/java/org/commonmark/node/HtmlInline.java
@@ -3,7 +3,7 @@
/**
* Inline HTML element.
*
- * @see CommonMark Spec
+ * @see CommonMark Spec
*/
public class HtmlInline extends Node {
diff --git a/commonmark/src/main/java/org/commonmark/node/Image.java b/commonmark/src/main/java/org/commonmark/node/Image.java
index 63481773a..1b31f6020 100644
--- a/commonmark/src/main/java/org/commonmark/node/Image.java
+++ b/commonmark/src/main/java/org/commonmark/node/Image.java
@@ -1,5 +1,13 @@
package org.commonmark.node;
+/**
+ * An image, e.g.:
+ *
+ * 
+ *
+ *
+ * @see CommonMark Spec
+ */
public class Image extends Node {
private String destination;
diff --git a/commonmark/src/main/java/org/commonmark/node/IndentedCodeBlock.java b/commonmark/src/main/java/org/commonmark/node/IndentedCodeBlock.java
index ccafca943..97642b7f3 100644
--- a/commonmark/src/main/java/org/commonmark/node/IndentedCodeBlock.java
+++ b/commonmark/src/main/java/org/commonmark/node/IndentedCodeBlock.java
@@ -1,5 +1,17 @@
package org.commonmark.node;
+/**
+ * An indented code block, e.g.:
+ *
+ * Code follows:
+ *
+ * foo
+ * bar
+ *
+ *
+ *
+ * @see CommonMark Spec
+ */
public class IndentedCodeBlock extends Block {
private String literal;
diff --git a/commonmark/src/main/java/org/commonmark/node/Link.java b/commonmark/src/main/java/org/commonmark/node/Link.java
index b2ed8c2a1..4edc7f676 100644
--- a/commonmark/src/main/java/org/commonmark/node/Link.java
+++ b/commonmark/src/main/java/org/commonmark/node/Link.java
@@ -18,7 +18,7 @@
* Note that the text in the link can contain inline formatting, so it could also contain an {@link Image} or
* {@link Emphasis}, etc.
*
- * @see CommonMark Spec for links
+ * @see CommonMark Spec
*/
public class Link extends Node {
@@ -46,6 +46,9 @@ public void setDestination(String destination) {
this.destination = destination;
}
+ /**
+ * @return the title or null
+ */
public String getTitle() {
return title;
}
diff --git a/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java b/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java
index 3f8bfd0f0..b866781f0 100644
--- a/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java
+++ b/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java
@@ -9,9 +9,9 @@
* They can be referenced anywhere else in the document to produce a link using [foo]. The definitions
* themselves are usually not rendered in the final output.
*
- * @see Link reference definitions
+ * @see CommonMark Spec
*/
-public class LinkReferenceDefinition extends Node {
+public class LinkReferenceDefinition extends Block {
private String label;
private String destination;
diff --git a/commonmark/src/main/java/org/commonmark/node/ListBlock.java b/commonmark/src/main/java/org/commonmark/node/ListBlock.java
index 69482f66e..1290bc622 100644
--- a/commonmark/src/main/java/org/commonmark/node/ListBlock.java
+++ b/commonmark/src/main/java/org/commonmark/node/ListBlock.java
@@ -1,12 +1,15 @@
package org.commonmark.node;
+/**
+ * A list block like {@link BulletList} or {@link OrderedList}.
+ */
public abstract class ListBlock extends Block {
private boolean tight;
/**
* @return whether this list is tight or loose
- * @see CommonMark Spec for tight lists
+ * @see CommonMark Spec for tight lists
*/
public boolean isTight() {
return tight;
diff --git a/commonmark/src/main/java/org/commonmark/node/ListItem.java b/commonmark/src/main/java/org/commonmark/node/ListItem.java
index 4e63b6145..c4d1214e7 100644
--- a/commonmark/src/main/java/org/commonmark/node/ListItem.java
+++ b/commonmark/src/main/java/org/commonmark/node/ListItem.java
@@ -1,5 +1,14 @@
package org.commonmark.node;
+/**
+ * A child of a {@link ListBlock}, containing other blocks (e.g. {@link Paragraph}, other lists, etc).
+ *
+ * Note that a list item can't directly contain {@link Text}, it needs to be:
+ * {@link ListItem} : {@link Paragraph} : {@link Text}.
+ * If you want a list that is rendered tightly, create a list with {@link ListBlock#setTight(boolean)}.
+ *
+ * @see CommonMark Spec: List items
+ */
public class ListItem extends Block {
private Integer markerIndent;
@@ -52,4 +61,18 @@ public Integer getContentIndent() {
public void setContentIndent(Integer contentIndent) {
this.contentIndent = contentIndent;
}
+
+ /**
+ * @deprecated list items should only contain block nodes; if you're trying to create a list that is rendered
+ * without paragraphs, use {@link ListBlock#setTight(boolean)} instead.
+ */
+ @Override
+ @Deprecated
+ public void appendChild(Node child) {
+ super.appendChild(child);
+ }
+
+ public void appendChild(Block child) {
+ super.appendChild(child);
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/node/Node.java b/commonmark/src/main/java/org/commonmark/node/Node.java
index 5a2f036e4..d95a72c60 100644
--- a/commonmark/src/main/java/org/commonmark/node/Node.java
+++ b/commonmark/src/main/java/org/commonmark/node/Node.java
@@ -86,6 +86,9 @@ public void unlink() {
this.prev = null;
}
+ /**
+ * Inserts the {@code sibling} node after {@code this} node.
+ */
public void insertAfter(Node sibling) {
sibling.unlink();
sibling.next = this.next;
@@ -100,6 +103,9 @@ public void insertAfter(Node sibling) {
}
}
+ /**
+ * Inserts the {@code sibling} node before {@code this} node.
+ */
public void insertBefore(Node sibling) {
sibling.unlink();
sibling.prev = this.prev;
@@ -114,13 +120,12 @@ public void insertBefore(Node sibling) {
}
}
-
/**
* @return the source spans of this node if included by the parser, an empty list otherwise
* @since 0.16.0
*/
public List getSourceSpans() {
- return sourceSpans != null ? Collections.unmodifiableList(sourceSpans) : Collections.emptyList();
+ return sourceSpans != null ? Collections.unmodifiableList(sourceSpans) : List.of();
}
/**
diff --git a/commonmark/src/main/java/org/commonmark/node/OrderedList.java b/commonmark/src/main/java/org/commonmark/node/OrderedList.java
index 0bbe09917..61f8902c0 100644
--- a/commonmark/src/main/java/org/commonmark/node/OrderedList.java
+++ b/commonmark/src/main/java/org/commonmark/node/OrderedList.java
@@ -1,5 +1,17 @@
package org.commonmark.node;
+/**
+ * An ordered list, e.g.:
+ *
+ * 1. One
+ * 2. Two
+ * 3. Three
+ *
+ *
+ * The children are {@link ListItem} blocks, which contain other blocks (or nested lists).
+ *
+ * @see CommonMark Spec: List items
+ */
public class OrderedList extends ListBlock {
private String markerDelimiter;
diff --git a/commonmark/src/main/java/org/commonmark/node/Paragraph.java b/commonmark/src/main/java/org/commonmark/node/Paragraph.java
index 176eaaa76..b298f1ce4 100644
--- a/commonmark/src/main/java/org/commonmark/node/Paragraph.java
+++ b/commonmark/src/main/java/org/commonmark/node/Paragraph.java
@@ -2,6 +2,8 @@
/**
* A paragraph block, contains inline nodes such as {@link Text}
+ *
+ * @see CommonMark Spec
*/
public class Paragraph extends Block {
diff --git a/commonmark/src/main/java/org/commonmark/node/SoftLineBreak.java b/commonmark/src/main/java/org/commonmark/node/SoftLineBreak.java
index e66458912..87445db56 100644
--- a/commonmark/src/main/java/org/commonmark/node/SoftLineBreak.java
+++ b/commonmark/src/main/java/org/commonmark/node/SoftLineBreak.java
@@ -1,5 +1,14 @@
package org.commonmark.node;
+/**
+ * A soft line break (as opposed to a {@link HardLineBreak}), e.g. between:
+ *
+ * foo
+ * bar
+ *
+ *
+ * @see CommonMark Spec
+ */
public class SoftLineBreak extends Node {
@Override
diff --git a/commonmark/src/main/java/org/commonmark/node/SourceSpan.java b/commonmark/src/main/java/org/commonmark/node/SourceSpan.java
index f7dbabc27..6558cc84a 100644
--- a/commonmark/src/main/java/org/commonmark/node/SourceSpan.java
+++ b/commonmark/src/main/java/org/commonmark/node/SourceSpan.java
@@ -27,32 +27,64 @@ public class SourceSpan {
private final int lineIndex;
private final int columnIndex;
+ private final int inputIndex;
private final int length;
+ public static SourceSpan of(int line, int col, int input, int length) {
+ return new SourceSpan(line, col, input, length);
+ }
+
+ /**
+ * @deprecated Use {{@link #of(int, int, int, int)}} instead to also specify input index. Using the deprecated one
+ * will set {@link #inputIndex} to 0.
+ */
+ @Deprecated
public static SourceSpan of(int lineIndex, int columnIndex, int length) {
- return new SourceSpan(lineIndex, columnIndex, length);
+ return of(lineIndex, columnIndex, 0, length);
}
- private SourceSpan(int lineIndex, int columnIndex, int length) {
+ private SourceSpan(int lineIndex, int columnIndex, int inputIndex, int length) {
+ if (lineIndex < 0) {
+ throw new IllegalArgumentException("lineIndex " + lineIndex + " must be >= 0");
+ }
+ if (columnIndex < 0) {
+ throw new IllegalArgumentException("columnIndex " + columnIndex + " must be >= 0");
+ }
+ if (inputIndex < 0) {
+ throw new IllegalArgumentException("inputIndex " + inputIndex + " must be >= 0");
+ }
+ if (length < 0) {
+ throw new IllegalArgumentException("length " + length + " must be >= 0");
+ }
this.lineIndex = lineIndex;
this.columnIndex = columnIndex;
+ this.inputIndex = inputIndex;
this.length = length;
}
/**
- * @return 0-based index of line in source
+ * @return 0-based line index, e.g. 0 for first line, 1 for the second line, etc
*/
public int getLineIndex() {
return lineIndex;
}
/**
- * @return 0-based index of column (character on line) in source
+ * @return 0-based index of column (character on line) in source, e.g. 0 for the first character of a line, 1 for
+ * the second character, etc
*/
public int getColumnIndex() {
return columnIndex;
}
+ /**
+ * @return 0-based index in whole input
+ * @since 0.24.0
+ */
+ public int getInputIndex() {
+ return inputIndex;
+ }
+
/**
* @return length of the span in characters
*/
@@ -60,6 +92,32 @@ public int getLength() {
return length;
}
+ public SourceSpan subSpan(int beginIndex) {
+ return subSpan(beginIndex, length);
+ }
+
+ public SourceSpan subSpan(int beginIndex, int endIndex) {
+ if (beginIndex < 0) {
+ throw new IndexOutOfBoundsException("beginIndex " + beginIndex + " + must be >= 0");
+ }
+ if (beginIndex > length) {
+ throw new IndexOutOfBoundsException("beginIndex " + beginIndex + " must be <= length " + length);
+ }
+ if (endIndex < 0) {
+ throw new IndexOutOfBoundsException("endIndex " + endIndex + " + must be >= 0");
+ }
+ if (endIndex > length) {
+ throw new IndexOutOfBoundsException("endIndex " + endIndex + " must be <= length " + length);
+ }
+ if (beginIndex > endIndex) {
+ throw new IndexOutOfBoundsException("beginIndex " + beginIndex + " must be <= endIndex " + endIndex);
+ }
+ if (beginIndex == 0 && endIndex == length) {
+ return this;
+ }
+ return new SourceSpan(lineIndex, columnIndex + beginIndex, inputIndex + beginIndex, endIndex - beginIndex);
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -71,12 +129,13 @@ public boolean equals(Object o) {
SourceSpan that = (SourceSpan) o;
return lineIndex == that.lineIndex &&
columnIndex == that.columnIndex &&
+ inputIndex == that.inputIndex &&
length == that.length;
}
@Override
public int hashCode() {
- return Objects.hash(lineIndex, columnIndex, length);
+ return Objects.hash(lineIndex, columnIndex, inputIndex, length);
}
@Override
@@ -84,6 +143,7 @@ public String toString() {
return "SourceSpan{" +
"line=" + lineIndex +
", column=" + columnIndex +
+ ", input=" + inputIndex +
", length=" + length +
"}";
}
diff --git a/commonmark/src/main/java/org/commonmark/node/SourceSpans.java b/commonmark/src/main/java/org/commonmark/node/SourceSpans.java
index 3ab29f536..975d7fbdb 100644
--- a/commonmark/src/main/java/org/commonmark/node/SourceSpans.java
+++ b/commonmark/src/main/java/org/commonmark/node/SourceSpans.java
@@ -1,7 +1,6 @@
package org.commonmark.node;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
/**
@@ -18,7 +17,7 @@ public static SourceSpans empty() {
}
public List getSourceSpans() {
- return sourceSpans != null ? sourceSpans : Collections.emptyList();
+ return sourceSpans != null ? sourceSpans : List.of();
}
public void addAllFrom(Iterable extends Node> nodes) {
@@ -42,8 +41,8 @@ public void addAll(List other) {
int lastIndex = sourceSpans.size() - 1;
SourceSpan a = sourceSpans.get(lastIndex);
SourceSpan b = other.get(0);
- if (a.getLineIndex() == b.getLineIndex() && a.getColumnIndex() + a.getLength() == b.getColumnIndex()) {
- sourceSpans.set(lastIndex, SourceSpan.of(a.getLineIndex(), a.getColumnIndex(), a.getLength() + b.getLength()));
+ if (a.getInputIndex() + a.getLength() == b.getInputIndex()) {
+ sourceSpans.set(lastIndex, SourceSpan.of(a.getLineIndex(), a.getColumnIndex(), a.getInputIndex(), a.getLength() + b.getLength()));
sourceSpans.addAll(other.subList(1, other.size()));
} else {
sourceSpans.addAll(other);
diff --git a/commonmark/src/main/java/org/commonmark/node/StrongEmphasis.java b/commonmark/src/main/java/org/commonmark/node/StrongEmphasis.java
index dbff571cd..0dbeed3df 100644
--- a/commonmark/src/main/java/org/commonmark/node/StrongEmphasis.java
+++ b/commonmark/src/main/java/org/commonmark/node/StrongEmphasis.java
@@ -1,5 +1,13 @@
package org.commonmark.node;
+/**
+ * Strong emphasis, e.g.:
+ *