From cd649a1657bcd844c66a33c4af007a6f1926f8c2 Mon Sep 17 00:00:00 2001 From: sebthom Date: Sun, 5 Jan 2025 17:26:47 +0100 Subject: [PATCH 01/89] refact: remove unused imports --- .../org/commonmark/ext/footnotes/FootnotesExtension.java | 1 - .../ext/footnotes/internal/FootnoteLinkProcessor.java | 1 - .../ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java | 5 ----- .../src/main/java/org/commonmark/testutil/SpecTestCase.java | 3 --- .../main/java/org/commonmark/internal/DocumentParser.java | 1 - .../commonmark/internal/LinkReferenceDefinitionParser.java | 1 - .../main/java/org/commonmark/internal/ParagraphParser.java | 1 - .../src/main/java/org/commonmark/internal/util/Escaping.java | 1 - .../commonmark/renderer/markdown/MarkdownRendererTest.java | 1 - .../src/test/java/org/commonmark/test/HtmlRendererTest.java | 2 -- .../test/java/org/commonmark/test/ListBlockParserTest.java | 1 - .../test/java/org/commonmark/test/SourceSpanRenderer.java | 1 - .../java/org/commonmark/test/TextContentRendererTest.java | 2 -- .../java/org/commonmark/test/ThematicBreakParserTest.java | 1 - 14 files changed, 22 deletions(-) diff --git a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/FootnotesExtension.java b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/FootnotesExtension.java index e2f90c239..dd532fa34 100644 --- a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/FootnotesExtension.java +++ b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/FootnotesExtension.java @@ -3,7 +3,6 @@ import org.commonmark.Extension; import org.commonmark.ext.footnotes.internal.*; import org.commonmark.parser.Parser; -import org.commonmark.parser.beta.InlineContentParserFactory; import org.commonmark.renderer.NodeRenderer; import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.renderer.markdown.MarkdownNodeRendererContext; diff --git a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteLinkProcessor.java b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteLinkProcessor.java index b2b420bae..07b008576 100644 --- a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteLinkProcessor.java +++ b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteLinkProcessor.java @@ -4,7 +4,6 @@ import org.commonmark.ext.footnotes.FootnoteReference; import org.commonmark.ext.footnotes.InlineFootnote; import org.commonmark.node.LinkReferenceDefinition; -import org.commonmark.node.Text; import org.commonmark.parser.InlineParserContext; import org.commonmark.parser.beta.LinkInfo; import org.commonmark.parser.beta.LinkProcessor; diff --git a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java index 342fb7ebb..eddf6f2ea 100644 --- a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java +++ b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java @@ -5,14 +5,9 @@ import org.commonmark.ext.footnotes.InlineFootnote; import org.commonmark.node.*; import org.commonmark.renderer.NodeRenderer; -import org.commonmark.renderer.html.HtmlNodeRendererContext; -import org.commonmark.renderer.html.HtmlWriter; import org.commonmark.renderer.markdown.MarkdownNodeRendererContext; import org.commonmark.renderer.markdown.MarkdownWriter; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Set; public class FootnoteMarkdownNodeRenderer implements NodeRenderer { 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..2fb175772 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,7 +2,6 @@ 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; @@ -10,8 +9,6 @@ import java.util.ArrayList; import java.util.List; -import static org.commonmark.testutil.Asserts.assertRendering; - @RunWith(Parameterized.class) public abstract class SpecTestCase { diff --git a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java index 7fd2d4300..6059cc51c 100644 --- a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java +++ b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java @@ -13,7 +13,6 @@ 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.*; diff --git a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java b/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java index 070f29ceb..b58e669ef 100644 --- a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java +++ b/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java @@ -2,7 +2,6 @@ import org.commonmark.internal.util.Escaping; import org.commonmark.internal.util.LinkScanner; -import org.commonmark.node.DefinitionMap; import org.commonmark.node.LinkReferenceDefinition; import org.commonmark.node.SourceSpan; import org.commonmark.parser.SourceLine; diff --git a/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java index 18808d499..93a2dd593 100644 --- a/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java +++ b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java @@ -9,7 +9,6 @@ import org.commonmark.parser.block.ParserState; import java.util.List; -import java.util.Map; public class ParagraphParser extends AbstractBlockParser { 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 d928b6f43..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,5 @@ package org.commonmark.internal.util; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.regex.Matcher; diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java index 59d525fed..c47a6dde0 100644 --- a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java @@ -2,7 +2,6 @@ import org.commonmark.node.*; import org.commonmark.parser.Parser; -import org.commonmark.testutil.Asserts; import org.junit.Test; import static org.commonmark.testutil.Asserts.assertRendering; diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java index 018b9e453..90a4aff62 100644 --- a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java @@ -4,8 +4,6 @@ import org.commonmark.parser.Parser; import org.commonmark.renderer.NodeRenderer; import org.commonmark.renderer.html.*; -import org.commonmark.testutil.Asserts; -import org.commonmark.testutil.RenderingTestCase; import org.commonmark.testutil.TestResources; import org.junit.Test; diff --git a/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java b/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java index 667d60efe..4a38bc412 100644 --- a/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java @@ -6,7 +6,6 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; public class ListBlockParserTest { diff --git a/commonmark/src/test/java/org/commonmark/test/SourceSpanRenderer.java b/commonmark/src/test/java/org/commonmark/test/SourceSpanRenderer.java index 5181a36e7..c29aac61e 100644 --- a/commonmark/src/test/java/org/commonmark/test/SourceSpanRenderer.java +++ b/commonmark/src/test/java/org/commonmark/test/SourceSpanRenderer.java @@ -2,7 +2,6 @@ import org.commonmark.node.AbstractVisitor; import org.commonmark.node.Node; -import org.commonmark.node.SourceSpan; import java.util.*; diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java index 45d7aa91d..fd7744eff 100644 --- a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java @@ -13,8 +13,6 @@ import java.util.Set; -import static org.junit.Assert.assertEquals; - public class TextContentRendererTest { private static final Parser PARSER = Parser.builder().build(); diff --git a/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java b/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java index a6b297f8e..2b15f8add 100644 --- a/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java @@ -5,7 +5,6 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; public class ThematicBreakParserTest { From a689cdd289e42cbc9a776d321a1ed1e3fe16e610 Mon Sep 17 00:00:00 2001 From: sebthom Date: Sun, 5 Jan 2025 17:26:57 +0100 Subject: [PATCH 02/89] refact: remove redundant superinterface/type argument --- .../ext/gfm/tables/internal/TableMarkdownNodeRenderer.java | 3 +-- .../org/commonmark/renderer/markdown/MarkdownRenderer.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) 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/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java index 7683b6921..0f8559f32 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java @@ -130,7 +130,7 @@ private class RendererContext implements MarkdownNodeRendererContext { private RendererContext(MarkdownWriter writer) { // Set fields that are used by interface this.writer = writer; - Set escapes = new HashSet(); + Set escapes = new HashSet<>(); for (MarkdownNodeRendererFactory factory : nodeRendererFactories) { escapes.addAll(factory.getSpecialCharacters()); } From f6a6a3eda21bb8bd9919ed9064153200eb7334dc Mon Sep 17 00:00:00 2001 From: sebthom Date: Sun, 5 Jan 2025 17:30:30 +0100 Subject: [PATCH 03/89] refact: remove unused local variable --- .../test/java/org/commonmark/test/TextContentRendererTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java index fd7744eff..bbacf3da4 100644 --- a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java @@ -45,7 +45,6 @@ public void textContentHeading() { @Test public void textContentEmphasis() { String s; - String rendered; s = "***foo***"; assertCompact(s, "foo"); From a45d3d24f19cbf2eb94da3a6cbd47f52deec66b6 Mon Sep 17 00:00:00 2001 From: sebthom Date: Sun, 5 Jan 2025 17:36:59 +0100 Subject: [PATCH 04/89] refact: fix deprecation warnings --- .../test/java/org/commonmark/ext/gfm/tables/TablesTest.java | 2 +- .../test/java/org/commonmark/internal/DocumentParserTest.java | 2 +- .../src/test/java/org/commonmark/test/HtmlRendererTest.java | 2 +- .../org/commonmark/test/LinkReferenceDefinitionNodeTest.java | 2 +- commonmark/src/test/java/org/commonmark/test/ParserTest.java | 2 +- .../test/java/org/commonmark/test/TextContentRendererTest.java | 3 ++- 6 files changed, 7 insertions(+), 6 deletions(-) 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 415a17815..57f4a4ae5 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 @@ -15,8 +15,8 @@ import java.util.*; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; public class TablesTest extends RenderingTestCase { diff --git a/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java b/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java index 621bef25b..45143b852 100644 --- a/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java +++ b/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java @@ -9,7 +9,7 @@ import java.util.Set; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; public class DocumentParserTest { diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java index 90a4aff62..8e5a8f30d 100644 --- a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java @@ -17,8 +17,8 @@ import java.util.concurrent.Future; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; public class HtmlRendererTest { diff --git a/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java b/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java index bf7bde6ec..81a71ee69 100644 --- a/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java +++ b/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java @@ -8,7 +8,7 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class LinkReferenceDefinitionNodeTest { diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index f56cd560c..447a50c3d 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -21,8 +21,8 @@ import java.util.concurrent.Future; import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; public class ParserTest { diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java index bbacf3da4..93ca87d93 100644 --- a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java @@ -19,7 +19,8 @@ public class TextContentRendererTest { private static final TextContentRenderer COMPACT_RENDERER = TextContentRenderer.builder().build(); private static final TextContentRenderer SEPARATE_RENDERER = TextContentRenderer.builder() .lineBreakRendering(LineBreakRendering.SEPARATE_BLOCKS).build(); - private static final TextContentRenderer STRIPPED_RENDERER = TextContentRenderer.builder().stripNewlines(true).build(); + private static final TextContentRenderer STRIPPED_RENDERER = TextContentRenderer.builder() + .lineBreakRendering(LineBreakRendering.STRIP).build(); @Test public void textContentText() { From 5cd425e471c328ff0625a85738fe3f3f84ab19d6 Mon Sep 17 00:00:00 2001 From: sebthom Date: Sun, 5 Jan 2025 17:48:55 +0100 Subject: [PATCH 05/89] refact: add missing Override annotations --- .../ext/gfm/tables/internal/TableHtmlNodeRenderer.java | 5 +++++ .../gfm/tables/internal/TableTextContentNodeRenderer.java | 5 +++++ .../commonmark/integration/ExtensionsIntegrationTest.java | 1 + .../main/java/org/commonmark/internal/BlockQuoteParser.java | 1 + commonmark/src/main/java/org/commonmark/node/Block.java | 1 + .../src/test/java/org/commonmark/test/UsageExampleTest.java | 2 ++ 6 files changed, 15 insertions(+) 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 fd07e84df..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 @@ -17,6 +17,7 @@ public TableHtmlNodeRenderer(HtmlNodeRendererContext context) { this.context = context; } + @Override protected void renderBlock(TableBlock tableBlock) { htmlWriter.line(); htmlWriter.tag("table", getAttributes(tableBlock, "table")); @@ -25,6 +26,7 @@ protected void renderBlock(TableBlock tableBlock) { htmlWriter.line(); } + @Override protected void renderHead(TableHead tableHead) { htmlWriter.line(); htmlWriter.tag("thead", getAttributes(tableHead, "thead")); @@ -33,6 +35,7 @@ protected void renderHead(TableHead tableHead) { htmlWriter.line(); } + @Override protected void renderBody(TableBody tableBody) { htmlWriter.line(); htmlWriter.tag("tbody", getAttributes(tableBody, "tbody")); @@ -41,6 +44,7 @@ protected void renderBody(TableBody tableBody) { htmlWriter.line(); } + @Override protected void renderRow(TableRow tableRow) { htmlWriter.line(); htmlWriter.tag("tr", getAttributes(tableRow, "tr")); @@ -49,6 +53,7 @@ protected void renderRow(TableRow tableRow) { htmlWriter.line(); } + @Override protected void renderCell(TableCell tableCell) { String tagName = tableCell.isHeader() ? "th" : "td"; htmlWriter.line(); 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 7d28f61a8..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,6 +22,7 @@ public TableTextContentNodeRenderer(TextContentNodeRendererContext context) { this.context = context; } + @Override protected void renderBlock(TableBlock tableBlock) { // Render rows tight textContentWriter.pushTight(true); @@ -30,19 +31,23 @@ protected void renderBlock(TableBlock tableBlock) { textContentWriter.block(); } + @Override protected void renderHead(TableHead tableHead) { renderChildren(tableHead); } + @Override protected void renderBody(TableBody tableBody) { renderChildren(tableBody); } + @Override protected void renderRow(TableRow tableRow) { renderChildren(tableRow); textContentWriter.block(); } + @Override protected void renderCell(TableCell tableCell) { renderChildren(tableCell); // For the last cell in row, don't render the delimiter 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..64cf60ed9 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 @@ -31,6 +31,7 @@ public void testTaskListItems() { } + @Override protected String render(String source) { return RENDERER.render(PARSER.parse(source)); } 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/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/test/java/org/commonmark/test/UsageExampleTest.java b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java index 1bff79a26..63901a49b 100644 --- a/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java +++ b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java @@ -79,6 +79,7 @@ public void addAttributes() { Parser parser = Parser.builder().build(); HtmlRenderer renderer = HtmlRenderer.builder() .attributeProviderFactory(new AttributeProviderFactory() { + @Override public AttributeProvider create(AttributeProviderContext context) { return new ImageAttributeProvider(); } @@ -95,6 +96,7 @@ public void customizeRendering() { Parser parser = Parser.builder().build(); HtmlRenderer renderer = HtmlRenderer.builder() .nodeRendererFactory(new HtmlNodeRendererFactory() { + @Override public NodeRenderer create(HtmlNodeRendererContext context) { return new IndentedCodeBlockNodeRenderer(context); } From 2e24c8670f7b4ade7a8bb234fa117a9332b9225e Mon Sep 17 00:00:00 2001 From: sebthom Date: Sun, 5 Jan 2025 18:08:12 +0100 Subject: [PATCH 06/89] fix: Add 'requires transitive' to fix compiler warning This fixes the compiler warning "The type Extension from module org.commonmark may not be accessible to clients due to missing 'requires transitive'" --- commonmark-ext-autolink/src/main/java/module-info.java | 2 +- commonmark-ext-footnotes/src/main/java/module-info.java | 2 +- commonmark-ext-gfm-strikethrough/src/main/java/module-info.java | 2 +- commonmark-ext-gfm-tables/src/main/java/module-info.java | 2 +- commonmark-ext-heading-anchor/src/main/java/module-info.java | 2 +- commonmark-ext-image-attributes/src/main/java/module-info.java | 2 +- commonmark-ext-ins/src/main/java/module-info.java | 2 +- commonmark-ext-task-list-items/src/main/java/module-info.java | 2 +- commonmark-ext-yaml-front-matter/src/main/java/module-info.java | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/commonmark-ext-autolink/src/main/java/module-info.java b/commonmark-ext-autolink/src/main/java/module-info.java index 6fe98c3b1..561934b85 100644 --- a/commonmark-ext-autolink/src/main/java/module-info.java +++ b/commonmark-ext-autolink/src/main/java/module-info.java @@ -1,6 +1,6 @@ module org.commonmark.ext.autolink { exports org.commonmark.ext.autolink; - requires org.commonmark; + requires transitive org.commonmark; requires org.nibor.autolink; } diff --git a/commonmark-ext-footnotes/src/main/java/module-info.java b/commonmark-ext-footnotes/src/main/java/module-info.java index 806d65c3e..0667b2801 100644 --- a/commonmark-ext-footnotes/src/main/java/module-info.java +++ b/commonmark-ext-footnotes/src/main/java/module-info.java @@ -1,5 +1,5 @@ module org.commonmark.ext.footnotes { exports org.commonmark.ext.footnotes; - requires org.commonmark; + requires transitive org.commonmark; } 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-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-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-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-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-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-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; } From 504f43c22da03fa09bab6f4b967dff9a5f9d1b6e Mon Sep 17 00:00:00 2001 From: Yang Dai <107718193+YangDai2003@users.noreply.github.com> Date: Sat, 8 Feb 2025 17:08:25 +0100 Subject: [PATCH 07/89] Update README.md Add Open Note to the "used by" list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dca939714..3bcbaffb9 100644 --- a/README.md +++ b/README.md @@ -450,6 +450,7 @@ Some users of this library (feel free to raise a PR if you want to be added): * [Gerrit](https://www.gerritcodereview.com/) code review/Gitiles ([link](https://gerrit-review.googlesource.com/c/gitiles/+/353794)) * [Clerk](https://clerk.vision/) moldable live programming for Clojure * [Znai](https://github.com/testingisdocumenting/znai) +* [Open Note](https://github.com/YangDai2003/OpenNote-Compose) a markdown editor and note-taking app for Android See also -------- From 762c2adba503e04947fd5c378de19484e13ac24e Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Thu, 13 Feb 2025 14:04:18 +0100 Subject: [PATCH 08/89] Add Quarkus Roq in "Used by" --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dca939714..b86c782cf 100644 --- a/README.md +++ b/README.md @@ -450,6 +450,7 @@ Some users of this library (feel free to raise a PR if you want to be added): * [Gerrit](https://www.gerritcodereview.com/) code review/Gitiles ([link](https://gerrit-review.googlesource.com/c/gitiles/+/353794)) * [Clerk](https://clerk.vision/) moldable live programming for Clojure * [Znai](https://github.com/testingisdocumenting/znai) +* [Quarkus Roq](https://github.com/quarkiverse/quarkus-roq/) The Roq Static Site Generator allows to easily create a static website or blog using Quarkus super-powers. See also -------- From 7db535466a86298366d5657883f14413e7d2b15b Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 23 Mar 2025 22:23:43 +1100 Subject: [PATCH 09/89] MarkdownRenderer: Fix overriding of core rendering --- CHANGELOG.md | 8 +++++ .../internal/renderer/NodeRendererMap.java | 3 ++ .../renderer/markdown/MarkdownRenderer.java | 8 ++--- .../markdown/MarkdownRendererTest.java | 35 +++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb412397f..62831d7b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. +## [Unreleased] +### Added +### Changed +### Fixed +- `MarkdownRenderer`: Fix precedence for `nodeRendererFactory`: Factories passed + to the builder can now override rendering for core node types. + ## [0.24.0] - 2024-10-21 ### Added - `SourceSpan` on nodes now have a `getInputIndex` to get the index within the @@ -459,6 +466,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. +[Unreleased]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...main [0.24.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.23.0...commonmark-parent-0.24.0 [0.23.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.22.0...commonmark-parent-0.23.0 [0.22.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.21.0...commonmark-parent-0.22.0 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 95ac6ce46..c74f90758 100644 --- a/commonmark/src/main/java/org/commonmark/internal/renderer/NodeRendererMap.java +++ b/commonmark/src/main/java/org/commonmark/internal/renderer/NodeRendererMap.java @@ -13,6 +13,9 @@ 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) { nodeRenderers.add(nodeRenderer); for (var nodeType : nodeRenderer.getNodeTypes()) { diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java index 0f8559f32..e4996fb08 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java @@ -136,12 +136,10 @@ private RendererContext(MarkdownWriter writer) { } additionalTextEscapes = Collections.unmodifiableSet(escapes); - // The first node renderer for a node type "wins". - for (int i = nodeRendererFactories.size() - 1; i >= 0; i--) { - MarkdownNodeRendererFactory nodeRendererFactory = nodeRendererFactories.get(i); + for (var factory : nodeRendererFactories) { // Pass in this as context here, which uses the fields set above - NodeRenderer nodeRenderer = nodeRendererFactory.create(this); - nodeRendererMap.add(nodeRenderer); + var renderer = factory.create(this); + nodeRendererMap.add(renderer); } } diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java index c47a6dde0..36a5b528c 100644 --- a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java @@ -2,8 +2,14 @@ import org.commonmark.node.*; 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.junit.Test; +import java.util.Set; + import static org.commonmark.testutil.Asserts.assertRendering; import static org.junit.Assert.assertEquals; @@ -300,6 +306,35 @@ public void testSoftLineBreaks() { assertRoundTrip("foo\nbar\n"); } + @Test + public void overrideNodeRender() { + var nodeRendererFactory = new MarkdownNodeRendererFactory() { + @Override + public NodeRenderer create(MarkdownNodeRendererContext context) { + return new NodeRenderer() { + @Override + public Set> getNodeTypes() { + return Set.of(Heading.class); + } + + @Override + public void render(Node node) { + context.getWriter().raw("# Custom heading"); + } + }; + } + + @Override + public Set getSpecialCharacters() { + return Set.of(); + } + }; + + MarkdownRenderer renderer = MarkdownRenderer.builder().nodeRendererFactory(nodeRendererFactory).build(); + String rendered = renderer.render(parse("# Hello")); + assertEquals("# Custom heading\n", rendered); + } + private void assertRoundTrip(String input) { String rendered = parseAndRender(input); assertEquals(input, rendered); From 5bb93e7b6fa5b79256f2b23bbb70eb28d12f3926 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 23 Mar 2025 22:49:19 +1100 Subject: [PATCH 10/89] Remove usage of requireNonNullElseGet (Android compat) --- commonmark/src/main/java/org/commonmark/parser/Parser.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commonmark/src/main/java/org/commonmark/parser/Parser.java b/commonmark/src/main/java/org/commonmark/parser/Parser.java index 4d534cf72..b98d0581f 100644 --- a/commonmark/src/main/java/org/commonmark/parser/Parser.java +++ b/commonmark/src/main/java/org/commonmark/parser/Parser.java @@ -311,7 +311,11 @@ public Builder inlineParserFactory(InlineParserFactory inlineParserFactory) { } private InlineParserFactory getInlineParserFactory() { - return Objects.requireNonNullElseGet(inlineParserFactory, () -> InlineParserImpl::new); + if (inlineParserFactory != null) { + return inlineParserFactory; + } else { + return InlineParserImpl::new; + } } } From 40f1fd5a0bb9fa7422272848da5d599582add766 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 23 Mar 2025 22:55:23 +1100 Subject: [PATCH 11/89] CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62831d7b7..2a1535e11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ with the exception that 0.x versions can break between minor versions. ### Fixed - `MarkdownRenderer`: Fix precedence for `nodeRendererFactory`: Factories passed to the builder can now override rendering for core node types. +- Fix warning in Eclipse about "missing 'requires transitive'" +- Fix Android incompatibility with `requireNonNullElseGet` ## [0.24.0] - 2024-10-21 ### Added From 245c3459374d57610d9001bd50a508783d23e32e Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 23 Mar 2025 23:46:16 +1100 Subject: [PATCH 12/89] Add docs with examples to Node classes --- CHANGELOG.md | 1 + .../main/java/org/commonmark/node/BlockQuote.java | 10 ++++++++++ .../main/java/org/commonmark/node/BulletList.java | 12 ++++++++++++ .../src/main/java/org/commonmark/node/Code.java | 12 ++++++++++++ .../main/java/org/commonmark/node/CustomBlock.java | 3 +++ .../main/java/org/commonmark/node/CustomNode.java | 3 +++ .../src/main/java/org/commonmark/node/Document.java | 3 +++ .../src/main/java/org/commonmark/node/Emphasis.java | 8 ++++++++ .../java/org/commonmark/node/FencedCodeBlock.java | 12 ++++++++++++ .../main/java/org/commonmark/node/HardLineBreak.java | 10 ++++++++++ .../src/main/java/org/commonmark/node/Heading.java | 12 ++++++++++++ .../src/main/java/org/commonmark/node/Image.java | 8 ++++++++ .../java/org/commonmark/node/IndentedCodeBlock.java | 12 ++++++++++++ .../src/main/java/org/commonmark/node/Link.java | 2 +- .../org/commonmark/node/LinkReferenceDefinition.java | 2 +- .../src/main/java/org/commonmark/node/ListBlock.java | 3 +++ .../src/main/java/org/commonmark/node/ListItem.java | 5 +++++ .../main/java/org/commonmark/node/OrderedList.java | 12 ++++++++++++ .../src/main/java/org/commonmark/node/Paragraph.java | 2 ++ .../main/java/org/commonmark/node/SoftLineBreak.java | 9 +++++++++ .../java/org/commonmark/node/StrongEmphasis.java | 8 ++++++++ .../src/main/java/org/commonmark/node/Text.java | 10 ++++++++++ .../main/java/org/commonmark/node/ThematicBreak.java | 12 ++++++++++++ 23 files changed, 169 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a1535e11..0808edb72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ with the exception that 0.x versions can break between minor versions. ## [Unreleased] ### Added +- More documentation with examples for `Node` classes ### Changed ### Fixed - `MarkdownRenderer`: Fix precedence for `nodeRendererFactory`: Factories passed 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/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 314c7457d..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; 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/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.: + *
+ * ![foo](/url "title")
+ * 
+ * + * @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 6341fed13..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 { diff --git a/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java b/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java index 412a3c38d..b866781f0 100644 --- a/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java +++ b/commonmark/src/main/java/org/commonmark/node/LinkReferenceDefinition.java @@ -9,7 +9,7 @@ * 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 Block { diff --git a/commonmark/src/main/java/org/commonmark/node/ListBlock.java b/commonmark/src/main/java/org/commonmark/node/ListBlock.java index 1a7c8b2fd..1290bc622 100644 --- a/commonmark/src/main/java/org/commonmark/node/ListBlock.java +++ b/commonmark/src/main/java/org/commonmark/node/ListBlock.java @@ -1,5 +1,8 @@ package org.commonmark.node; +/** + * A list block like {@link BulletList} or {@link OrderedList}. + */ public abstract class ListBlock extends Block { private boolean 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..e488e8fbe 100644 --- a/commonmark/src/main/java/org/commonmark/node/ListItem.java +++ b/commonmark/src/main/java/org/commonmark/node/ListItem.java @@ -1,5 +1,10 @@ package org.commonmark.node; +/** + * A child of a {@link ListBlock}, containing other blocks (e.g. {@link Paragraph}, other lists, etc). + * + * @see CommonMark Spec: List items + */ public class ListItem extends Block { private Integer markerIndent; 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/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.: + *

+ * Some **strong emphasis** or __strong emphasis__
+ * 
+ * + * @see CommonMark Spec: Emphasis and strong emphasis + */ public class StrongEmphasis extends Node implements Delimited { private String delimiter; diff --git a/commonmark/src/main/java/org/commonmark/node/Text.java b/commonmark/src/main/java/org/commonmark/node/Text.java index f16fc907b..9a04c41c1 100644 --- a/commonmark/src/main/java/org/commonmark/node/Text.java +++ b/commonmark/src/main/java/org/commonmark/node/Text.java @@ -1,5 +1,15 @@ package org.commonmark.node; +/** + * A text node, e.g. in: + *
+ * foo *bar*
+ * 
+ *

+ * The foo is a text node, and the bar inside the emphasis is also a text node. + * + * @see CommonMark Spec + */ public class Text extends Node { private String literal; diff --git a/commonmark/src/main/java/org/commonmark/node/ThematicBreak.java b/commonmark/src/main/java/org/commonmark/node/ThematicBreak.java index 836f8dfa1..a31131e07 100644 --- a/commonmark/src/main/java/org/commonmark/node/ThematicBreak.java +++ b/commonmark/src/main/java/org/commonmark/node/ThematicBreak.java @@ -1,5 +1,17 @@ package org.commonmark.node; +/** + * A thematic break, e.g. between text: + *

+ * Some text
+ *
+ * ___
+ *
+ * Some other text.
+ * 
+ * + * @see CommonMark Spec + */ public class ThematicBreak extends Block { private String literal; From df70419317a74fd0275a4055bde7b3d7d3f1af9b Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sat, 7 Jun 2025 17:40:10 +1000 Subject: [PATCH 13/89] Bump autolink to 0.12.0 https://github.com/robinst/autolink-java/blob/main/CHANGELOG.md#0120---2025-06-04 --- commonmark-ext-autolink/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index c2f9bf438..7f6a5cbc3 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -12,7 +12,7 @@ commonmark-java extension for turning plain URLs and email addresses into links - 0.11.0 + 0.12.0 From b79b949c105e783fa3e100de900c17e863704c4d Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sat, 7 Jun 2025 17:43:11 +1000 Subject: [PATCH 14/89] CI: Test on Java 24 instead of 23 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24160d342..e94dabbba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [11, 17, 21, 23] + java: [11, 17, 21, 24] steps: - name: Checkout sources uses: actions/checkout@v4 From 71b5148d33dff4b9b671c46cbac7f5cb1bd16dcb Mon Sep 17 00:00:00 2001 From: Zac Spitzer Date: Sun, 19 Jan 2025 00:04:13 +0100 Subject: [PATCH 15/89] add osgi headers via maven-bundle-plugin --- commonmark-ext-autolink/pom.xml | 9 ++++++ commonmark-ext-footnotes/pom.xml | 9 ++++++ commonmark-ext-gfm-strikethrough/pom.xml | 9 ++++++ commonmark-ext-gfm-tables/pom.xml | 9 ++++++ commonmark-ext-heading-anchor/pom.xml | 9 ++++++ commonmark-ext-image-attributes/pom.xml | 9 ++++++ commonmark-ext-ins/pom.xml | 9 ++++++ commonmark-ext-task-list-items/pom.xml | 9 ++++++ commonmark-ext-yaml-front-matter/pom.xml | 9 ++++++ commonmark/pom.xml | 9 ++++++ pom.xml | 36 ++++++++++++++++++++++++ 11 files changed, 126 insertions(+) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 7f6a5cbc3..b471e2e81 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -33,4 +33,13 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index d34e4e2f9..09b09935d 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -24,4 +24,13 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 1fd1269bf..23d93fa97 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -24,4 +24,13 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index d261a1d24..fc3f7957c 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -23,5 +23,14 @@ test + + + + + org.apache.felix + maven-bundle-plugin + + + diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index e4a62b2b3..b63b36472 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -23,5 +23,14 @@ test + + + + + org.apache.felix + maven-bundle-plugin + + + diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 55b0dce20..cbf102b8d 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -24,4 +24,13 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 9dbe2cf06..7af326271 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -24,4 +24,13 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 3926c8b5e..62fd974f5 100644 --- a/commonmark-ext-task-list-items/pom.xml +++ b/commonmark-ext-task-list-items/pom.xml @@ -24,4 +24,13 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 9e329e0d3..adff7d718 100644 --- a/commonmark-ext-yaml-front-matter/pom.xml +++ b/commonmark-ext-yaml-front-matter/pom.xml @@ -24,4 +24,13 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 17f3bc747..4ffd0ba95 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -54,6 +54,15 @@ + + + + org.apache.felix + maven-bundle-plugin + + + + BSD-2-Clause diff --git a/pom.xml b/pom.xml index 031869f6e..51583df21 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,42 @@ maven-surefire-plugin 3.2.5 + + org.apache.felix + maven-bundle-plugin + 6.0.0 + + + jar + bundle + + + + org.commonmark.*;-split-package:=merge-first + + + {local-packages} + + + + + + manifest + + manifest + + + true + + + + bundle + + bundle + + + + From e811c5cef0fbac91d25e581d75252f4bf99623aa Mon Sep 17 00:00:00 2001 From: Zac Spitzer Date: Sun, 19 Jan 2025 10:26:46 +0100 Subject: [PATCH 16/89] downgrade maven-bundle-plugin to 5.1.9 as 6 requires java 17 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 51583df21..eea051811 100644 --- a/pom.xml +++ b/pom.xml @@ -79,7 +79,7 @@ org.apache.felix maven-bundle-plugin - 6.0.0 + 5.1.9 jar From 4e625178708118036b28ebee80d6520ebfe4b5d0 Mon Sep 17 00:00:00 2001 From: Zac Spitzer Date: Sun, 19 Jan 2025 10:34:34 +0100 Subject: [PATCH 17/89] Add Lucee to README used by list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7660cc50c..73e69d9df 100644 --- a/README.md +++ b/README.md @@ -452,6 +452,7 @@ Some users of this library (feel free to raise a PR if you want to be added): * [Znai](https://github.com/testingisdocumenting/znai) * [Open Note](https://github.com/YangDai2003/OpenNote-Compose) a markdown editor and note-taking app for Android * [Quarkus Roq](https://github.com/quarkiverse/quarkus-roq/) The Roq Static Site Generator allows to easily create a static website or blog using Quarkus super-powers. +* [Lucee](https://github.com/lucee/lucee) See also -------- From 5fb452d97a7eb818ffad8f54a9ee3b2ef0577669 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sat, 7 Jun 2025 17:48:46 +1000 Subject: [PATCH 18/89] Simplify maven-bundle-plugin setup The only downside to defining it in `` in the parent pom is that there's a warning when running the plugin on the parent pom: [INFO] --- bundle:5.1.9:manifest (bundle-manifest) @ commonmark-parent --- [WARNING] Ignoring project type pom - supportedProjectTypes = [jar, bundle] The upside is we don't have to remember to put the plugin into all the poms. The configuration is as recommended here: https://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html#_adding_osgi_metadata_to_existing_projects_without_changing_the_packaging_type Compared to the previous one, the only differences are: - Import-Package adds imports for java packages, which seems to be recommended, see https://github.com/bndtools/bnd/issues/2507 - Import-Package of own packages that were exported (e.g. org.commonmark in core) don't have the version number, not sure what's recommended there --- commonmark-ext-autolink/pom.xml | 9 ---- commonmark-ext-footnotes/pom.xml | 9 ---- commonmark-ext-gfm-strikethrough/pom.xml | 9 ---- commonmark-ext-gfm-tables/pom.xml | 9 ---- commonmark-ext-heading-anchor/pom.xml | 9 ---- commonmark-ext-image-attributes/pom.xml | 9 ---- commonmark-ext-ins/pom.xml | 9 ---- commonmark-ext-task-list-items/pom.xml | 9 ---- commonmark-ext-yaml-front-matter/pom.xml | 9 ---- commonmark/pom.xml | 9 ---- pom.xml | 56 +++++++++--------------- 11 files changed, 20 insertions(+), 126 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index b471e2e81..7f6a5cbc3 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -33,13 +33,4 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 09b09935d..d34e4e2f9 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -24,13 +24,4 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 23d93fa97..1fd1269bf 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -24,13 +24,4 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index fc3f7957c..d261a1d24 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -23,14 +23,5 @@ test - - - - - org.apache.felix - maven-bundle-plugin - - - diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index b63b36472..e4a62b2b3 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -23,14 +23,5 @@ test - - - - - org.apache.felix - maven-bundle-plugin - - - diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index cbf102b8d..55b0dce20 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -24,13 +24,4 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 7af326271..9dbe2cf06 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -24,13 +24,4 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 62fd974f5..3926c8b5e 100644 --- a/commonmark-ext-task-list-items/pom.xml +++ b/commonmark-ext-task-list-items/pom.xml @@ -24,13 +24,4 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index adff7d718..9e329e0d3 100644 --- a/commonmark-ext-yaml-front-matter/pom.xml +++ b/commonmark-ext-yaml-front-matter/pom.xml @@ -24,13 +24,4 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 4ffd0ba95..17f3bc747 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -54,15 +54,6 @@ - - - - org.apache.felix - maven-bundle-plugin - - - - BSD-2-Clause diff --git a/pom.xml b/pom.xml index eea051811..29ca4ead2 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,11 @@ org.apache.maven.plugins maven-jar-plugin 3.4.1 + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + org.apache.maven.plugins @@ -76,42 +81,6 @@ maven-surefire-plugin 3.2.5 - - org.apache.felix - maven-bundle-plugin - 5.1.9 - - - jar - bundle - - - - org.commonmark.*;-split-package:=merge-first - - - {local-packages} - - - - - - manifest - - manifest - - - true - - - - bundle - - bundle - - - - @@ -139,6 +108,21 @@ deploy + + org.apache.felix + maven-bundle-plugin + + 5.1.9 + + + bundle-manifest + process-classes + + manifest + + + + From 664627727f1abe8f73f85391a659a0a17ea055ee Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 15 Jun 2025 21:19:02 +1000 Subject: [PATCH 19/89] Upgrade to JUnit 5 and AssertJ --- .../commonmark/ext/autolink/AutolinkTest.java | 44 +++--- .../footnotes/FootnoteHtmlRendererTest.java | 2 +- .../FootnoteMarkdownRendererTest.java | 6 +- .../ext/footnotes/FootnotesTest.java | 87 ++++++------ .../StrikethroughMarkdownRendererTest.java | 6 +- .../strikethrough/StrikethroughSpecTest.java | 23 ++-- .../gfm/strikethrough/StrikethroughTest.java | 15 +-- .../gfm/tables/TableMarkdownRendererTest.java | 6 +- .../ext/gfm/tables/TablesSpecTest.java | 23 ++-- .../commonmark/ext/gfm/tables/TablesTest.java | 63 +++++---- .../ext/gfm/tables/TablesTextContentTest.java | 2 +- .../HeadingAnchorConfigurationTest.java | 13 +- .../ext/heading/anchor/HeadingAnchorTest.java | 2 +- .../image/attributes/ImageAttributesTest.java | 7 +- .../ext/ins/InsMarkdownRendererTest.java | 6 +- .../java/org/commonmark/ext/ins/InsTest.java | 13 +- .../task/list/items/TaskListItemsTest.java | 2 +- .../ext/front/matter/YamlFrontMatterTest.java | 109 ++++++++------- .../integration/BoundsIntegrationTest.java | 33 ++--- .../ExtensionsIntegrationTest.java | 2 +- .../MarkdownRendererIntegrationTest.java | 6 +- .../SourceSpanIntegrationTest.java | 4 - .../integration/SpecIntegrationTest.java | 6 +- commonmark-test-util/pom.xml | 8 +- .../src/main/java/module-info.java | 3 +- .../java/org/commonmark/testutil/Asserts.java | 4 +- .../testutil/RenderingTestCase.java | 2 + .../org/commonmark/testutil/SpecTestCase.java | 28 ++-- .../testutil/example/ExampleReader.java | 15 +-- .../internal/DocumentParserTest.java | 20 ++- .../LinkReferenceDefinitionParserTest.java | 104 +++++++-------- .../internal/util/EscapingTest.java | 26 ++-- .../internal/util/LineReaderTest.java | 24 ++-- .../parser/InlineContentParserTest.java | 36 +++-- .../parser/beta/LinkProcessorTest.java | 13 +- .../commonmark/parser/beta/ScannerTest.java | 94 ++++++------- .../markdown/MarkdownRendererTest.java | 15 +-- .../markdown/SpecMarkdownRendererTest.java | 10 +- .../commonmark/test/AbstractVisitorTest.java | 11 +- .../org/commonmark/test/DelimitedTest.java | 22 +-- .../test/DelimiterProcessorTest.java | 24 ++-- .../test/FencedCodeBlockParserTest.java | 8 +- .../commonmark/test/HeadingParserTest.java | 2 +- .../commonmark/test/HtmlInlineParserTest.java | 2 +- .../org/commonmark/test/HtmlRendererTest.java | 125 ++++++++---------- .../test/InlineParserContextTest.java | 8 +- .../test/LinkReferenceDefinitionNodeTest.java | 66 +++++---- .../commonmark/test/ListBlockParserTest.java | 8 +- .../commonmark/test/ListTightLooseTest.java | 2 +- .../java/org/commonmark/test/ParserTest.java | 69 +++++----- .../org/commonmark/test/PathologicalTest.java | 25 +--- .../org/commonmark/test/RegressionTest.java | 31 ++--- .../org/commonmark/test/SourceLineTest.java | 19 +-- .../org/commonmark/test/SourceSpanTest.java | 71 +++++----- .../org/commonmark/test/SourceSpansTest.java | 68 +++++----- .../org/commonmark/test/SpecCoreTest.java | 8 +- .../org/commonmark/test/SpecCrLfCoreTest.java | 6 +- .../org/commonmark/test/SpecialInputTest.java | 2 +- .../test/TextContentRendererTest.java | 2 +- .../test/TextContentWriterTest.java | 14 +- .../test/ThematicBreakParserTest.java | 6 +- .../org/commonmark/test/UsageExampleTest.java | 29 ++-- .../org/commonmark/text/CharactersTest.java | 19 ++- pom.xml | 13 +- 64 files changed, 727 insertions(+), 815 deletions(-) diff --git a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java index 67fd69abe..338513f33 100644 --- a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java +++ b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java @@ -6,13 +6,12 @@ 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.List; 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 AutolinkTest extends RenderingTestCase { @@ -71,44 +70,37 @@ public void sourceSpans() { Paragraph paragraph = (Paragraph) document.getFirstChild(); Text abc = (Text) paragraph.getFirstChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 3)), - abc.getSourceSpans()); + assertThat(abc.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 3))); - assertTrue(abc.getNext() instanceof SoftLineBreak); + assertThat(abc.getNext()).isInstanceOf(SoftLineBreak.class); Link one = (Link) abc.getNext().getNext(); - assertEquals("http://example.com/one", one.getDestination()); - assertEquals(List.of(SourceSpan.of(1, 0, 4, 22)), - one.getSourceSpans()); + assertThat(one.getDestination()).isEqualTo("http://example.com/one"); + assertThat(one.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 4, 22))); - assertTrue(one.getNext() instanceof SoftLineBreak); + assertThat(one.getNext()).isInstanceOf(SoftLineBreak.class); Text def = (Text) one.getNext().getNext(); - assertEquals("def ", def.getLiteral()); - assertEquals(List.of(SourceSpan.of(2, 0, 27, 4)), - def.getSourceSpans()); + assertThat(def.getLiteral()).isEqualTo("def "); + assertThat(def.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 0, 27, 4))); Link two = (Link) def.getNext(); - assertEquals("http://example.com/two", two.getDestination()); - assertEquals(List.of(SourceSpan.of(2, 4, 31, 22)), - two.getSourceSpans()); + assertThat(two.getDestination()).isEqualTo("http://example.com/two"); + assertThat(two.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 4, 31, 22))); - assertTrue(two.getNext() instanceof SoftLineBreak); + assertThat(two.getNext()).isInstanceOf(SoftLineBreak.class); Text ghi = (Text) two.getNext().getNext(); - assertEquals("ghi ", ghi.getLiteral()); - assertEquals(List.of(SourceSpan.of(3, 0, 54, 4)), - ghi.getSourceSpans()); + assertThat(ghi.getLiteral()).isEqualTo("ghi "); + assertThat(ghi.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 0, 54, 4))); Link three = (Link) ghi.getNext(); - assertEquals("http://example.com/three", three.getDestination()); - assertEquals(List.of(SourceSpan.of(3, 4, 58, 24)), - three.getSourceSpans()); + assertThat(three.getDestination()).isEqualTo("http://example.com/three"); + assertThat(three.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 4, 58, 24))); Text jkl = (Text) three.getNext(); - assertEquals(" jkl", jkl.getLiteral()); - assertEquals(List.of(SourceSpan.of(3, 28, 82, 4)), - jkl.getSourceSpans()); + assertThat(jkl.getLiteral()).isEqualTo(" jkl"); + assertThat(jkl.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 28, 82, 4))); } @Override diff --git a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteHtmlRendererTest.java b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteHtmlRendererTest.java index ce5fdd51a..bc7d4f74c 100644 --- a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteHtmlRendererTest.java +++ b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteHtmlRendererTest.java @@ -8,7 +8,7 @@ import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.testutil.Asserts; import org.commonmark.testutil.RenderingTestCase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.Set; diff --git a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java index 9d567ece2..be3af6715 100644 --- a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java +++ b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java @@ -4,11 +4,11 @@ import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.commonmark.renderer.markdown.MarkdownRenderer; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class FootnoteMarkdownRendererTest { private static final Set EXTENSIONS = Set.of(FootnotesExtension.builder().inlineFootnotes(true).build()); @@ -43,7 +43,7 @@ public void testInline() { private void assertRoundTrip(String input) { String rendered = parseAndRender(input); - assertEquals(input, rendered); + assertThat(rendered).isEqualTo(input); } private String parseAndRender(String source) { diff --git a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java index bef6d89cb..3fa726d8f 100644 --- a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java +++ b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java @@ -4,15 +4,14 @@ import org.commonmark.node.*; import org.commonmark.parser.IncludeSourceSpans; import org.commonmark.parser.Parser; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.assertj.core.api.Assertions.assertThat; public class FootnotesTest { @@ -24,13 +23,13 @@ public void testDefBlockStart() { for (var s : List.of("1", "a", "^", "*", "\\a", "\uD83D\uDE42", "&0")) { var doc = PARSER.parse("[^" + s + "]: footnote\n"); var def = find(doc, FootnoteDefinition.class); - assertEquals(s, def.getLabel()); + assertThat(def.getLabel()).isEqualTo(s); } for (var s : List.of("", " ", "a b", "]", "\r", "\n", "\t")) { var input = "[^" + s + "]: footnote\n"; var doc = PARSER.parse(input); - assertNull("input: " + input, tryFind(doc, FootnoteDefinition.class)); + assertThat(tryFind(doc, FootnoteDefinition.class)).as("input: " + input).isNull(); } } @@ -40,24 +39,24 @@ public void testDefBlockStartInterrupts() { var doc = PARSER.parse("test\n[^1]: footnote\n"); var paragraph = find(doc, Paragraph.class); var def = find(doc, FootnoteDefinition.class); - assertEquals("test", ((Text) paragraph.getLastChild()).getLiteral()); - assertEquals("1", def.getLabel()); + assertThat(((Text) paragraph.getLastChild()).getLiteral()).isEqualTo("test"); + assertThat(def.getLabel()).isEqualTo("1"); } @Test public void testDefBlockStartIndented() { var doc1 = PARSER.parse(" [^1]: footnote\n"); - assertEquals("1", find(doc1, FootnoteDefinition.class).getLabel()); + assertThat(find(doc1, FootnoteDefinition.class).getLabel()).isEqualTo("1"); var doc2 = PARSER.parse(" [^1]: footnote\n"); - assertNull(tryFind(doc2, FootnoteDefinition.class)); + assertNone(doc2, FootnoteDefinition.class); } @Test public void testDefMultiple() { var doc = PARSER.parse("[^1]: foo\n[^2]: bar\n"); var defs = findAll(doc, FootnoteDefinition.class); - assertEquals("1", defs.get(0).getLabel()); - assertEquals("2", defs.get(1).getLabel()); + assertThat(defs.get(0).getLabel()).isEqualTo("1"); + assertThat(defs.get(1).getLabel()).isEqualTo("2"); } @Test @@ -65,8 +64,8 @@ public void testDefBlockStartAfterLinkReferenceDefinition() { var doc = PARSER.parse("[foo]: /url\n[^1]: footnote\n"); var linkReferenceDef = find(doc, LinkReferenceDefinition.class); var footnotesDef = find(doc, FootnoteDefinition.class); - assertEquals("foo", linkReferenceDef.getLabel()); - assertEquals("1", footnotesDef.getLabel()); + assertThat(linkReferenceDef.getLabel()).isEqualTo("foo"); + assertThat(footnotesDef.getLabel()).isEqualTo("1"); } @Test @@ -90,14 +89,14 @@ public void testDefContainsIndentedCodeBlock() { var doc = PARSER.parse("[^1]:\n code\n"); var def = find(doc, FootnoteDefinition.class); var codeBlock = (IndentedCodeBlock) def.getFirstChild(); - assertEquals("code\n", codeBlock.getLiteral()); + assertThat(codeBlock.getLiteral()).isEqualTo("code\n"); } @Test public void testDefContainsMultipleLines() { var doc = PARSER.parse("[^1]: footnote\nstill\n"); var def = find(doc, FootnoteDefinition.class); - assertEquals("1", def.getLabel()); + assertThat(def.getLabel()).isEqualTo("1"); var paragraph = (Paragraph) def.getFirstChild(); assertText("footnote", paragraph.getFirstChild()); assertText("still", paragraph.getLastChild()); @@ -107,7 +106,7 @@ public void testDefContainsMultipleLines() { public void testDefContainsList() { var doc = PARSER.parse("[^1]: - foo\n - bar\n"); var def = find(doc, FootnoteDefinition.class); - assertEquals("1", def.getLabel()); + assertThat(def.getLabel()).isEqualTo("1"); var list = (BulletList) def.getFirstChild(); var item1 = (ListItem) list.getFirstChild(); var item2 = (ListItem) list.getLastChild(); @@ -120,7 +119,7 @@ public void testDefInterruptedByOthers() { var doc = PARSER.parse("[^1]: footnote\n# Heading\n"); var def = find(doc, FootnoteDefinition.class); var heading = find(doc, Heading.class); - assertEquals("1", def.getLabel()); + assertThat(def.getLabel()).isEqualTo("1"); assertText("Heading", heading.getFirstChild()); } @@ -128,13 +127,13 @@ public void testDefInterruptedByOthers() { public void testReference() { var doc = PARSER.parse("Test [^foo]\n\n[^foo]: /url\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("foo", ref.getLabel()); + assertThat(ref.getLabel()).isEqualTo("foo"); } @Test public void testReferenceNoDefinition() { var doc = PARSER.parse("Test [^foo]\n"); - assertNull(tryFind(doc, FootnoteReference.class)); + assertNone(doc, FootnoteReference.class); } @Test @@ -142,13 +141,13 @@ public void testRefWithEmphasisInside() { // No emphasis inside footnote reference, should just be treated as text var doc = PARSER.parse("Test [^*foo*]\n\n[^*foo*]: def\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("*foo*", ref.getLabel()); - assertNull(ref.getFirstChild()); + assertThat(ref.getLabel()).isEqualTo("*foo*"); + assertThat(ref.getFirstChild()).isNull(); var paragraph = doc.getFirstChild(); var text = (Text) paragraph.getFirstChild(); - assertEquals("Test ", text.getLiteral()); - assertEquals(ref, text.getNext()); - assertEquals(ref, paragraph.getLastChild()); + assertThat(text.getLiteral()).isEqualTo("Test "); + assertThat(text.getNext()).isEqualTo(ref); + assertThat(paragraph.getLastChild()).isEqualTo(ref); } @Test @@ -156,18 +155,18 @@ public void testRefWithEmphasisAround() { // Emphasis around footnote reference, the * inside needs to be removed from emphasis processing var doc = PARSER.parse("Test *abc [^foo*] def*\n\n[^foo*]: def\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("foo*", ref.getLabel()); + assertThat(ref.getLabel()).isEqualTo("foo*"); assertText("abc ", ref.getPrevious()); assertText(" def", ref.getNext()); var em = find(doc, Emphasis.class); - assertEquals(em, ref.getParent()); + assertThat(ref.getParent()).isEqualTo(em); } @Test public void testRefAfterBang() { var doc = PARSER.parse("Test![^foo]\n\n[^foo]: def\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("foo", ref.getLabel()); + assertThat(ref.getLabel()).isEqualTo("foo"); var paragraph = doc.getFirstChild(); assertText("Test!", paragraph.getFirstChild()); } @@ -178,7 +177,7 @@ public void testRefAsLabelOnly() { // resolve as footnotes. If `[foo][^bar]` fails to parse as a bracket, `[^bar]` by itself needs to be tried. var doc = PARSER.parse("Test [foo][^bar]\n\n[^bar]: footnote\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("bar", ref.getLabel()); + assertThat(ref.getLabel()).isEqualTo("bar"); var paragraph = doc.getFirstChild(); assertText("Test [foo]", paragraph.getFirstChild()); } @@ -188,7 +187,7 @@ public void testRefWithEmptyLabel() { // [^bar] is a footnote but [] is just text, because collapsed reference links don't resolve as footnotes var doc = PARSER.parse("Test [^bar][]\n\n[^bar]: footnote\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("bar", ref.getLabel()); + assertThat(ref.getLabel()).isEqualTo("bar"); var paragraph = doc.getFirstChild(); assertText("Test ", paragraph.getFirstChild()); assertText("[]", paragraph.getLastChild()); @@ -198,22 +197,22 @@ public void testRefWithEmptyLabel() { public void testRefWithBracket() { // Not a footnote, [ needs to be escaped var doc = PARSER.parse("Test [^f[oo]\n\n[^f[oo]: /url\n"); - assertNull(tryFind(doc, FootnoteReference.class)); + assertNone(doc, FootnoteReference.class); } @Test public void testRefWithBackslash() { var doc = PARSER.parse("[^\\foo]\n\n[^\\foo]: note\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("\\foo", ref.getLabel()); + assertThat(ref.getLabel()).isEqualTo("\\foo"); var def = find(doc, FootnoteDefinition.class); - assertEquals("\\foo", def.getLabel()); + assertThat(def.getLabel()).isEqualTo("\\foo"); } @Test public void testPreferInlineLink() { var doc = PARSER.parse("Test [^bar](/url)\n\n[^bar]: footnote\n"); - assertNull(tryFind(doc, FootnoteReference.class)); + assertNone(doc, FootnoteReference.class); } @Test @@ -221,7 +220,7 @@ public void testPreferReferenceLink() { // This is tricky because `[^*foo*][foo]` is a valid link already. If `[foo]` was not defined, the first bracket // would be a footnote. var doc = PARSER.parse("Test [^*foo*][foo]\n\n[^*foo*]: /url\n\n[foo]: /url"); - assertNull(tryFind(doc, FootnoteReference.class)); + assertNone(doc, FootnoteReference.class); } @Test @@ -229,7 +228,7 @@ public void testReferenceLinkWithoutDefinition() { // Similar to previous test but there's no definition var doc = PARSER.parse("Test [^*foo*][foo]\n\n[^*foo*]: def\n"); var ref = find(doc, FootnoteReference.class); - assertEquals("*foo*", ref.getLabel()); + assertThat(ref.getLabel()).isEqualTo("*foo*"); var paragraph = (Paragraph) doc.getFirstChild(); assertText("Test ", paragraph.getFirstChild()); assertText("[foo]", paragraph.getLastChild()); @@ -249,12 +248,12 @@ public void testInlineFootnote() { { var doc = parser.parse("Test \\^[not inline footnote]"); - assertNull(tryFind(doc, InlineFootnote.class)); + assertNone(doc, InlineFootnote.class); } { var doc = parser.parse("Test ^[not inline footnote"); - assertNull(tryFind(doc, InlineFootnote.class)); + assertNone(doc, InlineFootnote.class); var t = doc.getFirstChild().getFirstChild(); assertText("Test ^[not inline footnote", t); } @@ -269,7 +268,7 @@ public void testInlineFootnote() { var fn = find(doc, InlineFootnote.class); assertText("test ", fn.getFirstChild()); var code = fn.getFirstChild().getNext(); - assertEquals("bla]", ((Code) code).getLiteral()); + assertThat(((Code) code).getLiteral()).isEqualTo("bla]"); } { @@ -277,7 +276,7 @@ public void testInlineFootnote() { var fn = find(doc, InlineFootnote.class); assertText("with a ", fn.getFirstChild()); var link = fn.getFirstChild().getNext(); - assertEquals("url", ((Link) link).getDestination()); + assertThat(((Link) link).getDestination()).isEqualTo("url"); } } @@ -287,10 +286,14 @@ public void testSourcePositions() { var doc = parser.parse("Test [^foo]\n\n[^foo]: /url\n"); var ref = find(doc, FootnoteReference.class); - assertEquals(ref.getSourceSpans(), List.of(SourceSpan.of(0, 5, 5, 6))); + assertThat(ref.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 5, 5, 6))); var def = find(doc, FootnoteDefinition.class); - assertEquals(def.getSourceSpans(), List.of(SourceSpan.of(2, 0, 13, 12))); + assertThat(def.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 0, 13, 12))); + } + + private static void assertNone(Node parent, Class nodeClass) { + assertThat(tryFind(parent, nodeClass)).as(() -> "Node " + parent + " containing " + nodeClass).isNull(); } private static T find(Node parent, Class nodeClass) { @@ -315,6 +318,6 @@ private static List findAll(Node parent, Class nodeClass) { private static void assertText(String expected, Node node) { var text = (Text) node; - assertEquals(expected, text.getLiteral()); + assertThat(text.getLiteral()).isEqualTo(expected); } } 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 507fc0a88..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,11 +3,11 @@ 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.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class StrikethroughMarkdownRendererTest { @@ -30,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 2ca5471b0..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,30 +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.List; import java.util.Set; -@RunWith(Parameterized.class) +@ParameterizedClass +@MethodSource("data") public class StrikethroughSpecTest extends RenderingTestCase { 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 cb3019957..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,12 +12,12 @@ 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.List; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class StrikethroughTest extends RenderingTestCase { @@ -84,14 +84,14 @@ 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 @@ -104,7 +104,7 @@ public void requireTwoTildesOption() { .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 @@ -117,8 +117,7 @@ public void sourceSpans() { Node document = parser.parse("hey ~~there~~\n"); Paragraph block = (Paragraph) document.getFirstChild(); Node strikethrough = block.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 4, 4, 9)), - strikethrough.getSourceSpans()); + assertThat(strikethrough.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 9))); } @Override 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 16303b58c..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,11 +3,11 @@ 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.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class TableMarkdownRendererTest { @@ -70,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 d5ea8c836..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,30 +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.List; import java.util.Set; -@RunWith(Parameterized.class) +@ParameterizedClass +@MethodSource("data") public class TablesSpecTest extends RenderingTestCase { 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 57f4a4ae5..3135b4d8e 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 @@ -10,13 +10,13 @@ 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.*; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class TablesTest extends RenderingTestCase { @@ -730,7 +730,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" + "\n" + @@ -743,7 +743,7 @@ public void setAttributes(Node node, String tagName, Map attribu "\n" + "\n" + "\n" + - "
Abc2
\n")); + "\n"); } @Test @@ -766,7 +766,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" + "\n" + @@ -779,7 +779,7 @@ public void setAttributes(Node node, String tagName, Map attribu "\n" + "\n" + "\n" + - "
Abc2
\n")); + "\n"); } @Test @@ -791,49 +791,48 @@ public void sourceSpans() { Node document = parser.parse("Abc|Def\n---|---\n|1|2\n 3|four|\n|||\n"); TableBlock block = (TableBlock) document.getFirstChild(); - assertEquals(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)), - 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(List.of(SourceSpan.of(0, 0, 0, 7)), head.getSourceSpans()); + assertThat(head.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7))); TableRow headRow = (TableRow) head.getFirstChild(); - assertEquals(List.of(SourceSpan.of(0, 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(List.of(SourceSpan.of(0, 0, 0, 3)), headRowCell1.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 3)), headRowCell1.getFirstChild().getSourceSpans()); - assertEquals(List.of(SourceSpan.of(0, 4, 4, 3)), headRowCell2.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(0, 4, 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(List.of(SourceSpan.of(2, 0, 16, 4), SourceSpan.of(3, 0, 21, 8), SourceSpan.of(4, 0, 30, 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(List.of(SourceSpan.of(2, 0, 16, 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(List.of(SourceSpan.of(2, 1, 17, 1)), bodyRow1Cell1.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(2, 1, 17, 1)), bodyRow1Cell1.getFirstChild().getSourceSpans()); - assertEquals(List.of(SourceSpan.of(2, 3, 19, 1)), bodyRow1Cell2.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(2, 3, 19, 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(List.of(SourceSpan.of(3, 0, 21, 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(List.of(SourceSpan.of(3, 1, 22, 1)), bodyRow2Cell1.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(3, 1, 22, 1)), bodyRow2Cell1.getFirstChild().getSourceSpans()); - assertEquals(List.of(SourceSpan.of(3, 3, 24, 4)), bodyRow2Cell2.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(3, 3, 24, 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(List.of(SourceSpan.of(4, 0, 30, 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(List.of(), bodyRow3Cell1.getSourceSpans()); - assertEquals(List.of(), bodyRow3Cell2.getSourceSpans()); + assertThat(bodyRow3Cell1.getSourceSpans()).isEqualTo(List.of()); + assertThat(bodyRow3Cell2.getSourceSpans()).isEqualTo(List.of()); } @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 c5ef8cb5a..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 @@ -5,7 +5,7 @@ import org.commonmark.renderer.text.LineBreakRendering; import org.commonmark.renderer.text.TextContentRenderer; import org.commonmark.testutil.Asserts; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; 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 af2ae13cf..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.List; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class HeadingAnchorConfigurationTest { @@ -30,25 +29,25 @@ public void testDefaultConfigurationHasNoAdditions() { HtmlRenderer renderer = HtmlRenderer.builder() .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 872306bed..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,7 +4,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; import java.util.Set; 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 b863bd4b7..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,12 +8,12 @@ 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.List; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class ImageAttributesTest extends RenderingTestCase { @@ -131,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(List.of(SourceSpan.of(0, 0, 0, 19)), - text.getSourceSpans()); + assertThat(text.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 19))); } @Override 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 28f2cd354..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,11 +3,11 @@ 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.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class InsMarkdownRendererTest { @@ -28,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 4603da60b..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,12 +9,12 @@ 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.List; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class InsTest extends RenderingTestCase { @@ -82,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 @@ -102,8 +102,7 @@ public void sourceSpans() { Node document = parser.parse("hey ++there++\n"); Paragraph block = (Paragraph) document.getFirstChild(); Node ins = block.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 4, 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/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 c13e10bb7..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,7 +4,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; import java.util.Set; 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 f46c11b3c..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,14 +6,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.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 = Set.of(YamlFrontMatterExtension.create()); @@ -31,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); } @@ -50,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); } @@ -70,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); } @@ -92,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); } @@ -112,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); } @@ -137,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); } @@ -164,7 +163,7 @@ public void empty() { Map> data = getFrontMatter(input); - assertTrue(data.isEmpty()); + assertThat(data).isEmpty(); assertRendering(input, rendered); } @@ -180,7 +179,7 @@ public void yamlInParagraph() { Map> data = getFrontMatter(input); - assertTrue(data.isEmpty()); + assertThat(data).isEmpty(); assertRendering(input, rendered); } @@ -195,7 +194,7 @@ public void yamlOnSecondLine() { Map> data = getFrontMatter(input); - assertTrue(data.isEmpty()); + assertThat(data).isEmpty(); assertRendering(input, rendered); } @@ -208,7 +207,7 @@ public void nonMatchedStartTag() { Map> data = getFrontMatter(input); - assertTrue(data.isEmpty()); + assertThat(data).isEmpty(); assertRendering(input, rendered); } @@ -222,7 +221,7 @@ public void inList() { Map> data = getFrontMatter(input); - assertTrue(data.isEmpty()); + assertThat(data).isEmpty(); assertRendering(input, rendered); } @@ -240,9 +239,9 @@ public void visitorIgnoresOtherCustomNodes() { document.accept(visitor); Map> data = visitor.getData(); - assertEquals(1, data.size()); - assertTrue(data.containsKey("hello")); - assertEquals(List.of("world"), data.get("hello")); + assertThat(data).hasSize(1); + assertThat(data).containsKey("hello"); + assertThat(data.get("hello")).isEqualTo(List.of("world")); } @Test @@ -261,9 +260,9 @@ public void nodesCanBeModified() { document.accept(visitor); Map> data = visitor.getData(); - assertEquals(1, data.size()); - assertTrue(data.containsKey("see")); - assertEquals(List.of("you"), data.get("see")); + assertThat(data).hasSize(1); + assertThat(data).containsKey("see"); + assertThat(data.get("see")).isEqualTo(List.of("you")); } @Test @@ -275,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 @@ -292,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 @@ -308,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/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/ExtensionsIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java index 64cf60ed9..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. 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 f05efe1c3..9ab5c32a3 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,11 +10,11 @@ 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.List; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class MarkdownRendererIntegrationTest { @@ -40,6 +40,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/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 7fb4881f1..d6a38f4a9 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -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 2fb175772..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,30 +2,22 @@ import org.commonmark.testutil.example.Example; import org.commonmark.testutil.example.ExampleReader; -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; -@RunWith(Parameterized.class) +@ParameterizedClass +@MethodSource("data") public abstract class SpecTestCase { - protected final Example example; + @Parameter + protected Example example; - public SpecTestCase(Example example) { - this.example = example; + static List data() { + return ExampleReader.readExamples(TestResources.getSpec()); } - - @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; - } - } 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 6f5dd6276..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 @@ -7,6 +7,7 @@ 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) { diff --git a/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java b/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java index 45143b852..a834665ff 100644 --- a/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java +++ b/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java @@ -2,17 +2,15 @@ import org.commonmark.node.*; import org.commonmark.parser.block.BlockParserFactory; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.List; import java.util.Set; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; -public class DocumentParserTest { +class DocumentParserTest { private static final List CORE_FACTORIES = List.of( new BlockQuoteParser.Factory(), new HeadingParser.Factory(), @@ -23,28 +21,28 @@ public class DocumentParserTest { new IndentedCodeBlockParser.Factory()); @Test - public void calculateBlockParserFactories_givenAFullListOfAllowedNodes_includesAllCoreFactories() { + void calculateBlockParserFactories_givenAFullListOfAllowedNodes_includesAllCoreFactories() { List customParserFactories = List.of(); var enabledBlockTypes = Set.of(BlockQuote.class, Heading.class, FencedCodeBlock.class, HtmlBlock.class, ThematicBreak.class, ListBlock.class, IndentedCodeBlock.class); List blockParserFactories = DocumentParser.calculateBlockParserFactories(customParserFactories, enabledBlockTypes); - assertThat(blockParserFactories.size(), is(CORE_FACTORIES.size())); + assertThat(blockParserFactories).hasSameSizeAs(CORE_FACTORIES); for (BlockParserFactory factory : CORE_FACTORIES) { - assertTrue(hasInstance(blockParserFactories, factory.getClass())); + assertThat(hasInstance(blockParserFactories, factory.getClass())).isTrue(); } } @Test - public void calculateBlockParserFactories_givenAListOfAllowedNodes_includesAssociatedFactories() { + void calculateBlockParserFactories_givenAListOfAllowedNodes_includesAssociatedFactories() { List customParserFactories = List.of(); Set> nodes = new HashSet<>(); nodes.add(IndentedCodeBlock.class); List blockParserFactories = DocumentParser.calculateBlockParserFactories(customParserFactories, nodes); - assertThat(blockParserFactories.size(), is(1)); - assertTrue(hasInstance(blockParserFactories, IndentedCodeBlockParser.Factory.class)); + assertThat(blockParserFactories).hasSize(1); + assertThat(hasInstance(blockParserFactories, IndentedCodeBlockParser.Factory.class)).isTrue(); } private boolean hasInstance(List blockParserFactories, Class factoryClass) { diff --git a/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java b/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java index 3f22adac6..b69ada0e9 100644 --- a/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java +++ b/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java @@ -3,82 +3,82 @@ import org.commonmark.internal.LinkReferenceDefinitionParser.State; import org.commonmark.node.LinkReferenceDefinition; import org.commonmark.parser.SourceLine; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; -public class LinkReferenceDefinitionParserTest { +class LinkReferenceDefinitionParserTest { private final LinkReferenceDefinitionParser parser = new LinkReferenceDefinitionParser(); @Test - public void testStartLabel() { + void testStartLabel() { assertState("[", State.LABEL, "["); } @Test - public void testStartNoLabel() { + void testStartNoLabel() { // Not a label assertParagraph("a"); // Can not go back to parsing link reference definitions parse("a"); parse("["); - assertEquals(State.PARAGRAPH, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.PARAGRAPH); assertParagraphLines("a\n[", parser); } @Test - public void testEmptyLabel() { + void testEmptyLabel() { assertParagraph("[]: /"); assertParagraph("[ ]: /"); assertParagraph("[ \t\n\u000B\f\r ]: /"); } @Test - public void testLabelColon() { + void testLabelColon() { assertParagraph("[foo] : /"); } @Test - public void testLabel() { + void testLabel() { assertState("[foo]:", State.DESTINATION, "[foo]:"); assertState("[ foo ]:", State.DESTINATION, "[ foo ]:"); } @Test - public void testLabelInvalid() { + void testLabelInvalid() { assertParagraph("[foo[]:"); } @Test - public void testLabelMultiline() { + void testLabelMultiline() { parse("[two"); - assertEquals(State.LABEL, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.LABEL); parse("lines]:"); - assertEquals(State.DESTINATION, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.DESTINATION); parse("/url"); - assertEquals(State.START_TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_TITLE); assertDef(parser.getDefinitions().get(0), "two\nlines", "/url", null); } @Test - public void testLabelStartsWithNewline() { + void testLabelStartsWithNewline() { parse("["); - assertEquals(State.LABEL, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.LABEL); parse("weird]:"); - assertEquals(State.DESTINATION, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.DESTINATION); parse("/url"); - assertEquals(State.START_TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_TITLE); assertDef(parser.getDefinitions().get(0), "\nweird", "/url", null); } @Test - public void testDestination() { + void testDestination() { parse("[foo]: /url"); - assertEquals(State.START_TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_TITLE); assertParagraphLines("", parser); - assertEquals(1, parser.getDefinitions().size()); + assertThat(parser.getDefinitions()).hasSize(1); assertDef(parser.getDefinitions().get(0), "foo", "/url", null); parse("[bar]: "); @@ -86,91 +86,91 @@ public void testDestination() { } @Test - public void testDestinationInvalid() { + void testDestinationInvalid() { assertParagraph("[foo]: "); } @Test - public void testTitle() { + void testTitle() { parse("[foo]: /url 'title'"); - assertEquals(State.START_DEFINITION, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_DEFINITION); assertParagraphLines("", parser); - assertEquals(1, parser.getDefinitions().size()); + assertThat(parser.getDefinitions()).hasSize(1); assertDef(parser.getDefinitions().get(0), "foo", "/url", "title"); } @Test - public void testTitleStartWhitespace() { + void testTitleStartWhitespace() { parse("[foo]: /url"); - assertEquals(State.START_TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_TITLE); assertParagraphLines("", parser); parse(" "); - assertEquals(State.START_DEFINITION, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_DEFINITION); assertParagraphLines(" ", parser); - assertEquals(1, parser.getDefinitions().size()); + assertThat(parser.getDefinitions()).hasSize(1); assertDef(parser.getDefinitions().get(0), "foo", "/url", null); } @Test - public void testTitleMultiline() { + void testTitleMultiline() { parse("[foo]: /url 'two"); - assertEquals(State.TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.TITLE); assertParagraphLines("[foo]: /url 'two", parser); - assertEquals(0, parser.getDefinitions().size()); + assertThat(parser.getDefinitions()).isEmpty(); parse("lines"); - assertEquals(State.TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.TITLE); assertParagraphLines("[foo]: /url 'two\nlines", parser); - assertEquals(0, parser.getDefinitions().size()); + assertThat(parser.getDefinitions()).isEmpty(); parse("'"); - assertEquals(State.START_DEFINITION, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_DEFINITION); assertParagraphLines("", parser); - assertEquals(1, parser.getDefinitions().size()); + assertThat(parser.getDefinitions()).hasSize(1); assertDef(parser.getDefinitions().get(0), "foo", "/url", "two\nlines\n"); } @Test - public void testTitleMultiline2() { + void testTitleMultiline2() { parse("[foo]: /url '"); - assertEquals(State.TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.TITLE); parse("title'"); - assertEquals(State.START_DEFINITION, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_DEFINITION); assertDef(parser.getDefinitions().get(0), "foo", "/url", "\ntitle"); } @Test - public void testTitleMultiline3() { + void testTitleMultiline3() { parse("[foo]: /url"); - assertEquals(State.START_TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_TITLE); // Note that this looks like a valid title until we parse "bad", at which point we need to treat the whole line // as a paragraph line and discard any already parsed title. parse("\"title\" bad"); - assertEquals(State.PARAGRAPH, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.PARAGRAPH); assertDef(parser.getDefinitions().get(0), "foo", "/url", null); } @Test - public void testTitleMultiline4() { + void testTitleMultiline4() { parse("[foo]: /url"); - assertEquals(State.START_TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.START_TITLE); parse("(title"); - assertEquals(State.TITLE, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.TITLE); parse("foo("); - assertEquals(State.PARAGRAPH, parser.getState()); + assertThat(parser.getState()).isEqualTo(State.PARAGRAPH); assertDef(parser.getDefinitions().get(0), "foo", "/url", null); } @Test - public void testTitleInvalid() { + void testTitleInvalid() { assertParagraph("[foo]: /url (invalid("); assertParagraph("[foo]: 'title'"); assertParagraph("[foo]: /url 'title' INVALID"); @@ -188,18 +188,18 @@ private static void assertState(String input, State state, String paragraphConte LinkReferenceDefinitionParser parser = new LinkReferenceDefinitionParser(); // TODO: Should we check things with source spans here? parser.parse(SourceLine.of(input, null)); - assertEquals(state, parser.getState()); + assertThat(parser.getState()).isEqualTo(state); assertParagraphLines(paragraphContent, parser); } private static void assertDef(LinkReferenceDefinition def, String label, String destination, String title) { - assertEquals(label, def.getLabel()); - assertEquals(destination, def.getDestination()); - assertEquals(title, def.getTitle()); + assertThat(def.getLabel()).isEqualTo(label); + assertThat(def.getDestination()).isEqualTo(destination); + assertThat(def.getTitle()).isEqualTo(title); } private static void assertParagraphLines(String expectedContent, LinkReferenceDefinitionParser parser) { String actual = parser.getParagraphLines().getContent(); - assertEquals(expectedContent, actual); + assertThat(actual).isEqualTo(expectedContent); } } diff --git a/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java b/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java index 9433eb7d0..eb2f1a801 100644 --- a/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java +++ b/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java @@ -1,21 +1,21 @@ package org.commonmark.internal.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; -public class EscapingTest { +class EscapingTest { @Test - public void testEscapeHtml() { - assertEquals("nothing to escape", Escaping.escapeHtml("nothing to escape")); - assertEquals("&", Escaping.escapeHtml("&")); - assertEquals("<", Escaping.escapeHtml("<")); - assertEquals(">", Escaping.escapeHtml(">")); - assertEquals(""", Escaping.escapeHtml("\"")); - assertEquals("< start", Escaping.escapeHtml("< start")); - assertEquals("end >", Escaping.escapeHtml("end >")); - assertEquals("< both >", Escaping.escapeHtml("< both >")); - assertEquals("< middle & too >", Escaping.escapeHtml("< middle & too >")); + void testEscapeHtml() { + assertThat(Escaping.escapeHtml("nothing to escape")).isEqualTo("nothing to escape"); + assertThat(Escaping.escapeHtml("&")).isEqualTo("&"); + assertThat(Escaping.escapeHtml("<")).isEqualTo("<"); + assertThat(Escaping.escapeHtml(">")).isEqualTo(">"); + assertThat(Escaping.escapeHtml("\"")).isEqualTo("""); + assertThat(Escaping.escapeHtml("< start")).isEqualTo("< start"); + assertThat(Escaping.escapeHtml("end >")).isEqualTo("end >"); + assertThat(Escaping.escapeHtml("< both >")).isEqualTo("< both >"); + assertThat(Escaping.escapeHtml("< middle & too >")).isEqualTo("< middle & too >"); } } diff --git a/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java b/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java index fc48623a8..b52713846 100644 --- a/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java +++ b/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java @@ -1,6 +1,6 @@ package org.commonmark.internal.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.*; import java.nio.charset.StandardCharsets; @@ -9,13 +9,14 @@ import java.util.Objects; import static java.util.stream.Collectors.joining; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.commonmark.internal.util.LineReader.CHAR_BUFFER_SIZE; -import static org.junit.Assert.*; -public class LineReaderTest { +class LineReaderTest { @Test - public void testReadLine() throws IOException { + void testReadLine() throws IOException { assertLines(); assertLines("", "\n"); @@ -48,21 +49,16 @@ public void testReadLine() throws IOException { } @Test - public void testClose() throws IOException { + void testClose() throws IOException { var reader = new InputStreamReader(new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8))); var lineReader = new LineReader(reader); lineReader.close(); lineReader.close(); - try { - reader.read(); - fail("Expected read to throw after closing reader"); - } catch (IOException e) { - // Expected - } + assertThatThrownBy(reader::read).isInstanceOf(IOException.class); } private void assertLines(String... s) throws IOException { - assertTrue("Expected parts needs to be even (pairs of content and terminator)", s.length % 2 == 0); + assertThat(s.length).as("Expected parts needs to be even (pairs of content and terminator)").isEven(); var input = Arrays.stream(s).filter(Objects::nonNull).collect(joining("")); assertLines(new StringReader(input), s); @@ -77,8 +73,8 @@ private static void assertLines(Reader reader, String... expectedParts) throws I lines.add(line); lines.add(lineReader.getLineTerminator()); } - assertNull(lineReader.getLineTerminator()); - assertEquals(Arrays.asList(expectedParts), lines); + assertThat(lineReader.getLineTerminator()).isNull(); + assertThat(lines).containsExactly(expectedParts); } } diff --git a/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java b/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java index ed97b6f31..d0f45a6bc 100644 --- a/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java +++ b/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java @@ -1,53 +1,51 @@ package org.commonmark.parser; -import org.commonmark.node.CustomNode; -import org.commonmark.node.Heading; -import org.commonmark.node.Image; -import org.commonmark.node.Text; +import org.commonmark.node.*; import org.commonmark.parser.beta.InlineContentParser; import org.commonmark.parser.beta.InlineContentParserFactory; import org.commonmark.parser.beta.InlineParserState; import org.commonmark.parser.beta.ParsedInline; import org.commonmark.test.Nodes; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; -public class InlineContentParserTest { +class InlineContentParserTest { @Test - public void customInlineContentParser() { + void customInlineContentParser() { var parser = Parser.builder().customInlineContentParserFactory(new DollarInlineParser.Factory()).build(); var doc = parser.parse("Test: $hey *there*$ $you$\n\n# Heading $heading$\n"); var inline1 = Nodes.find(doc, DollarInline.class); - assertEquals("hey *there*", inline1.getLiteral()); + assertThat(inline1.getLiteral()).isEqualTo("hey *there*"); var inline2 = (DollarInline) doc.getFirstChild().getLastChild(); - assertEquals("you", inline2.getLiteral()); + assertThat(inline2.getLiteral()).isEqualTo("you"); var heading = Nodes.find(doc, Heading.class); var inline3 = (DollarInline) heading.getLastChild(); - assertEquals("heading", inline3.getLiteral()); + assertThat(inline3.getLiteral()).isEqualTo("heading"); // Parser is created for each inline snippet, which is why the index resets for the second snippet. - assertEquals(0, inline1.getIndex()); - assertEquals(1, inline2.getIndex()); - assertEquals(0, inline3.getIndex()); + assertThat(inline1.getIndex()).isEqualTo(0); + assertThat(inline2.getIndex()).isEqualTo(1); + assertThat(inline3.getIndex()).isEqualTo(0); } @Test - public void bangInlineContentParser() { + void bangInlineContentParser() { // See if using ! for a custom inline content parser works. // ![] is used for images, but if it's not followed by a [, it should be possible to parse it differently. var parser = Parser.builder().customInlineContentParserFactory(new BangInlineParser.Factory()).build(); var doc = parser.parse("![image](url) !notimage"); var image = Nodes.find(doc, Image.class); - assertEquals("url", image.getDestination()); - assertEquals(" ", ((Text) image.getNext()).getLiteral()); - assertEquals(BangInline.class, image.getNext().getNext().getClass()); - assertEquals("notimage", ((Text) image.getNext().getNext().getNext()).getLiteral()); + assertThat(image.getDestination()).isEqualTo("url"); + assertThat(((Text) image.getNext()).getLiteral()).isEqualTo(" "); + // Class + assertThat(image.getNext().getNext()).isInstanceOf(BangInline.class); + assertThat(((Text) image.getNext().getNext().getNext()).getLiteral()).isEqualTo("notimage"); } private static class DollarInline extends CustomNode { diff --git a/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java b/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java index 6209ac9a0..ef8739128 100644 --- a/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java +++ b/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java @@ -4,13 +4,14 @@ import org.commonmark.node.Text; import org.commonmark.parser.Parser; import org.commonmark.test.Nodes; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; + +class LinkProcessorTest { -public class LinkProcessorTest { @Test - public void testLinkMarkerShouldNotBeIncludedByDefault() { + void testLinkMarkerShouldNotBeIncludedByDefault() { // If a link marker is registered but is not processed, the built-in link processor shouldn't consume it. // And I think by default, other processors shouldn't consume it either (by accident). // So requiring processors to opt into including the marker is better than requiring them to opt out, @@ -19,7 +20,7 @@ public void testLinkMarkerShouldNotBeIncludedByDefault() { var parser = Parser.builder().linkMarker('^').build(); var doc = parser.parse("^[test](url)"); var link = Nodes.find(doc, Link.class); - assertEquals("url", link.getDestination()); - assertEquals("^", ((Text) link.getPrevious()).getLiteral()); + assertThat(link.getDestination()).isEqualTo("url"); + assertThat(((Text) link.getPrevious()).getLiteral()).isEqualTo("^"); } } diff --git a/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java b/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java index 14c118ab9..bd74cab0e 100644 --- a/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java +++ b/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java @@ -3,87 +3,87 @@ import org.commonmark.node.SourceSpan; import org.commonmark.parser.SourceLine; import org.commonmark.parser.SourceLines; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; -public class ScannerTest { +class ScannerTest { @Test - public void testNext() { + void testNext() { Scanner scanner = new Scanner(List.of( SourceLine.of("foo bar", null)), 0, 4); - assertEquals('b', scanner.peek()); + assertThat(scanner.peek()).isEqualTo('b'); scanner.next(); - assertEquals('a', scanner.peek()); + assertThat(scanner.peek()).isEqualTo('a'); scanner.next(); - assertEquals('r', scanner.peek()); + assertThat(scanner.peek()).isEqualTo('r'); scanner.next(); - assertEquals('\0', scanner.peek()); + assertThat(scanner.peek()).isEqualTo('\0'); } @Test - public void testMultipleLines() { + void testMultipleLines() { Scanner scanner = new Scanner(List.of( SourceLine.of("ab", null), SourceLine.of("cde", null)), 0, 0); - assertTrue(scanner.hasNext()); - assertEquals('\0', scanner.peekPreviousCodePoint()); - assertEquals('a', scanner.peek()); + assertThat(scanner.hasNext()).isTrue(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('\0'); + assertThat(scanner.peek()).isEqualTo('a'); scanner.next(); - assertTrue(scanner.hasNext()); - assertEquals('a', scanner.peekPreviousCodePoint()); - assertEquals('b', scanner.peek()); + assertThat(scanner.hasNext()).isTrue(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('a'); + assertThat(scanner.peek()).isEqualTo('b'); scanner.next(); - assertTrue(scanner.hasNext()); - assertEquals('b', scanner.peekPreviousCodePoint()); - assertEquals('\n', scanner.peek()); + assertThat(scanner.hasNext()).isTrue(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('b'); + assertThat(scanner.peek()).isEqualTo('\n'); scanner.next(); - assertTrue(scanner.hasNext()); - assertEquals('\n', scanner.peekPreviousCodePoint()); - assertEquals('c', scanner.peek()); + assertThat(scanner.hasNext()).isTrue(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('\n'); + assertThat(scanner.peek()).isEqualTo('c'); scanner.next(); - assertTrue(scanner.hasNext()); - assertEquals('c', scanner.peekPreviousCodePoint()); - assertEquals('d', scanner.peek()); + assertThat(scanner.hasNext()).isTrue(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('c'); + assertThat(scanner.peek()).isEqualTo('d'); scanner.next(); - assertTrue(scanner.hasNext()); - assertEquals('d', scanner.peekPreviousCodePoint()); - assertEquals('e', scanner.peek()); + assertThat(scanner.hasNext()).isTrue(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('d'); + assertThat(scanner.peek()).isEqualTo('e'); scanner.next(); - assertFalse(scanner.hasNext()); - assertEquals('e', scanner.peekPreviousCodePoint()); - assertEquals('\0', scanner.peek()); + assertThat(scanner.hasNext()).isFalse(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('e'); + assertThat(scanner.peek()).isEqualTo('\0'); } @Test - public void testCodePoints() { + void testCodePoints() { Scanner scanner = new Scanner(List.of(SourceLine.of("\uD83D\uDE0A", null)), 0, 0); - assertTrue(scanner.hasNext()); - assertEquals('\0', scanner.peekPreviousCodePoint()); - assertEquals(128522, scanner.peekCodePoint()); + assertThat(scanner.hasNext()).isTrue(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo('\0'); + assertThat(scanner.peekCodePoint()).isEqualTo(128522); scanner.next(); // This jumps chars, not code points. So jump two here scanner.next(); - assertFalse(scanner.hasNext()); - assertEquals(128522, scanner.peekPreviousCodePoint()); - assertEquals('\0', scanner.peekCodePoint()); + assertThat(scanner.hasNext()).isFalse(); + assertThat(scanner.peekPreviousCodePoint()).isEqualTo(128522); + assertThat(scanner.peekCodePoint()).isEqualTo('\0'); } @Test - public void testTextBetween() { + void testTextBetween() { Scanner scanner = new Scanner(List.of( SourceLine.of("ab", SourceSpan.of(10, 3, 13, 2)), SourceLine.of("cde", SourceSpan.of(11, 4, 20, 3))), @@ -139,20 +139,20 @@ public void testTextBetween() { } private void assertSourceLines(SourceLines sourceLines, String expectedContent, SourceSpan... expectedSourceSpans) { - assertEquals(expectedContent, sourceLines.getContent()); - assertEquals(List.of(expectedSourceSpans), sourceLines.getSourceSpans()); + assertThat(sourceLines.getContent()).isEqualTo(expectedContent); + assertThat(sourceLines.getSourceSpans()).isEqualTo(List.of(expectedSourceSpans)); } @Test - public void nextString() { + void nextString() { Scanner scanner = Scanner.of(SourceLines.of(List.of( SourceLine.of("hey ya", null), SourceLine.of("hi", null)))); - assertFalse(scanner.next("hoy")); - assertTrue(scanner.next("hey")); - assertTrue(scanner.next(' ')); - assertFalse(scanner.next("yo")); - assertTrue(scanner.next("ya")); - assertFalse(scanner.next(" ")); + assertThat(scanner.next("hoy")).isFalse(); + assertThat(scanner.next("hey")).isTrue(); + assertThat(scanner.next(' ')).isTrue(); + assertThat(scanner.next("yo")).isFalse(); + assertThat(scanner.next("ya")).isTrue(); + assertThat(scanner.next(" ")).isFalse(); } } diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java index 36a5b528c..36b8adad9 100644 --- a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java @@ -3,15 +3,12 @@ import org.commonmark.node.*; 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.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; +import static org.assertj.core.api.Assertions.assertThat; import static org.commonmark.testutil.Asserts.assertRendering; -import static org.junit.Assert.assertEquals; public class MarkdownRendererTest { @@ -31,7 +28,7 @@ public void testThematicBreaks() { // Apply fallback for null literal ThematicBreak node = new ThematicBreak(); - assertEquals("___", render(node)); + assertThat(render(node)).isEqualTo("___"); } @Test @@ -255,7 +252,7 @@ public void testEmphasis() { Emphasis e2 = new Emphasis(); e1.appendChild(e2); e2.appendChild(new Text("hi")); - assertEquals("*_hi_*\n", render(doc)); + assertThat(render(doc)).isEqualTo("*_hi_*\n"); } @Test @@ -332,12 +329,12 @@ public Set getSpecialCharacters() { MarkdownRenderer renderer = MarkdownRenderer.builder().nodeRendererFactory(nodeRendererFactory).build(); String rendered = renderer.render(parse("# Hello")); - assertEquals("# Custom heading\n", rendered); + assertThat(rendered).isEqualTo("# Custom heading\n"); } private void assertRoundTrip(String input) { String rendered = parseAndRender(input); - assertEquals(input, rendered); + assertThat(rendered).isEqualTo(input); } private String parseAndRender(String source) { diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java index 5df2e5c80..3b88df55d 100644 --- a/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java @@ -6,15 +6,14 @@ import org.commonmark.testutil.TestResources; import org.commonmark.testutil.example.Example; import org.commonmark.testutil.example.ExampleReader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests Markdown rendering using the examples in the spec like this: @@ -63,9 +62,8 @@ public void testCoverage() { System.out.println(); } - int expectedPassed = 652; - assertTrue("Expected at least " + expectedPassed + " examples to pass but was " + passes.size(), passes.size() >= expectedPassed); - assertEquals(0, fails.size()); + assertThat(passes).hasSizeGreaterThanOrEqualTo(652); + assertThat(fails).isEmpty(); } private static void printCountsBySection(List examples) { diff --git a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java index b3b60fa3b..edb6936f4 100644 --- a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java +++ b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java @@ -1,10 +1,9 @@ package org.commonmark.test; import org.commonmark.node.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.assertj.core.api.Assertions.assertThat; public class AbstractVisitorTest { @@ -26,13 +25,13 @@ public void visit(Text text) { assertCode("foo", paragraph.getFirstChild()); assertCode("bar", paragraph.getFirstChild().getNext()); - assertNull(paragraph.getFirstChild().getNext().getNext()); + assertThat(paragraph.getFirstChild().getNext().getNext()).isNull(); assertCode("bar", paragraph.getLastChild()); } private static void assertCode(String expectedLiteral, Node node) { - assertEquals("Expected node to be a Code node: " + node, Code.class, node.getClass()); + assertThat(node).isInstanceOf(Code.class); Code code = (Code) node; - assertEquals(expectedLiteral, code.getLiteral()); + assertThat(code.getLiteral()).isEqualTo(expectedLiteral); } } diff --git a/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java b/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java index a34a32c44..3f2f0d611 100644 --- a/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java +++ b/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java @@ -2,12 +2,12 @@ import org.commonmark.node.*; import org.commonmark.parser.Parser; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class DelimitedTest { @@ -35,20 +35,20 @@ public void visit(StrongEmphasis node) { }; document.accept(visitor); - assertEquals(4, list.size()); + assertThat(list).hasSize(4); Delimited emphasis = list.get(0); Delimited strong = list.get(1); Delimited important = list.get(2); Delimited critical = list.get(3); - assertEquals("*", emphasis.getOpeningDelimiter()); - assertEquals("*", emphasis.getClosingDelimiter()); - assertEquals("**", strong.getOpeningDelimiter()); - assertEquals("**", strong.getClosingDelimiter()); - assertEquals("_", important.getOpeningDelimiter()); - assertEquals("_", important.getClosingDelimiter()); - assertEquals("__", critical.getOpeningDelimiter()); - assertEquals("__", critical.getClosingDelimiter()); + assertThat(emphasis.getOpeningDelimiter()).isEqualTo("*"); + assertThat(emphasis.getClosingDelimiter()).isEqualTo("*"); + assertThat(strong.getOpeningDelimiter()).isEqualTo("**"); + assertThat(strong.getClosingDelimiter()).isEqualTo("**"); + assertThat(important.getOpeningDelimiter()).isEqualTo("_"); + assertThat(important.getClosingDelimiter()).isEqualTo("_"); + assertThat(critical.getOpeningDelimiter()).isEqualTo("__"); + assertThat(critical.getClosingDelimiter()).isEqualTo("__"); } } diff --git a/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java b/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java index 680c40bf2..e4920120d 100644 --- a/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java +++ b/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java @@ -11,12 +11,13 @@ import org.commonmark.renderer.html.HtmlNodeRendererFactory; import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.testutil.RenderingTestCase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Locale; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class DelimiterProcessorTest extends RenderingTestCase { @@ -29,8 +30,8 @@ public void delimiterProcessorWithInvalidDelimiterUse() { .customDelimiterProcessor(new CustomDelimiterProcessor(':', 0)) .customDelimiterProcessor(new CustomDelimiterProcessor(';', -1)) .build(); - assertEquals("

:test:

\n", RENDERER.render(parser.parse(":test:"))); - assertEquals("

;test;

\n", RENDERER.render(parser.parse(";test;"))); + assertThat(RENDERER.render(parser.parse(":test:"))).isEqualTo("

:test:

\n"); + assertThat(RENDERER.render(parser.parse(";test;"))).isEqualTo("

;test;

\n"); } @Test @@ -54,16 +55,17 @@ public void multipleDelimitersWithDifferentLengths() { .customDelimiterProcessor(new OneDelimiterProcessor()) .customDelimiterProcessor(new TwoDelimiterProcessor()) .build(); - assertEquals("

(1)one(/1) (2)two(/2)

\n", RENDERER.render(parser.parse("+one+ ++two++"))); - assertEquals("

(1)(2)both(/2)(/1)

\n", RENDERER.render(parser.parse("+++both+++"))); + assertThat(RENDERER.render(parser.parse("+one+ ++two++"))).isEqualTo("

(1)one(/1) (2)two(/2)

\n"); + assertThat(RENDERER.render(parser.parse("+++both+++"))).isEqualTo("

(1)(2)both(/2)(/1)

\n"); } - @Test(expected = IllegalArgumentException.class) + @Test public void multipleDelimitersWithSameLengthConflict() { - Parser.builder() - .customDelimiterProcessor(new OneDelimiterProcessor()) - .customDelimiterProcessor(new OneDelimiterProcessor()) - .build(); + assertThatThrownBy(() -> + Parser.builder() + .customDelimiterProcessor(new OneDelimiterProcessor()) + .customDelimiterProcessor(new OneDelimiterProcessor()) + .build()).isInstanceOf(IllegalArgumentException.class); } @Override diff --git a/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java b/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java index 774c6ff0e..443b0fa51 100644 --- a/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java @@ -5,9 +5,9 @@ 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 static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class FencedCodeBlockParserTest extends RenderingTestCase { @@ -18,8 +18,8 @@ public class FencedCodeBlockParserTest extends RenderingTestCase { public void backtickInfo() { Node document = PARSER.parse("```info ~ test\ncode\n```"); FencedCodeBlock codeBlock = (FencedCodeBlock) document.getFirstChild(); - assertEquals("info ~ test", codeBlock.getInfo()); - assertEquals("code\n", codeBlock.getLiteral()); + assertThat(codeBlock.getInfo()).isEqualTo("info ~ test"); + assertThat(codeBlock.getLiteral()).isEqualTo("code\n"); } @Test diff --git a/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.java b/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.java index a5b179a81..f7bf35a4c 100644 --- a/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.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; public class HeadingParserTest extends RenderingTestCase { diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java index 965a2f181..8e1fd9790 100644 --- a/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java @@ -1,6 +1,6 @@ package org.commonmark.test; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class HtmlInlineParserTest extends CoreRenderingTestCase { diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java index 8e5a8f30d..413bb9d8a 100644 --- a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java @@ -5,59 +5,54 @@ import org.commonmark.renderer.NodeRenderer; import org.commonmark.renderer.html.*; import org.commonmark.testutil.TestResources; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class HtmlRendererTest { @Test public void htmlAllowingShouldNotEscapeInlineHtml() { String rendered = htmlAllowingRenderer().render(parse("paragraph with inline & html")); - assertEquals("

paragraph with inline & html

\n", rendered); + assertThat(rendered).isEqualTo("

paragraph with inline & html

\n"); } @Test public void htmlAllowingShouldNotEscapeBlockHtml() { String rendered = htmlAllowingRenderer().render(parse("
block &
")); - assertEquals("
block &
\n", rendered); + assertThat(rendered).isEqualTo("
block &
\n"); } @Test public void htmlEscapingShouldEscapeInlineHtml() { String rendered = htmlEscapingRenderer().render(parse("paragraph with inline & html")); // Note that & is not escaped, as it's a normal text node, not part of the inline HTML. - assertEquals("

paragraph with <span id='foo' class="bar">inline & html</span>

\n", rendered); + assertThat(rendered).isEqualTo("

paragraph with <span id='foo' class="bar">inline & html</span>

\n"); } @Test public void htmlEscapingShouldEscapeHtmlBlocks() { String rendered = htmlEscapingRenderer().render(parse("
block &
")); - assertEquals("

<div id='foo' class="bar">block &amp;</div>

\n", rendered); + assertThat(rendered).isEqualTo("

<div id='foo' class="bar">block &amp;</div>

\n"); } @Test public void textEscaping() { String rendered = defaultRenderer().render(parse("escaping: & < > \" '")); - assertEquals("

escaping: & < > " '

\n", rendered); + assertThat(rendered).isEqualTo("

escaping: & < > " '

\n"); } @Test public void characterReferencesWithoutSemicolonsShouldNotBeParsedShouldBeEscaped() { String input = "[example](javascript:alert('XSS'))"; String rendered = defaultRenderer().render(parse(input)); - assertEquals("

example

\n", rendered); + assertThat(rendered).isEqualTo("

example

\n"); } @Test @@ -66,7 +61,7 @@ public void attributeEscaping() { Link link = new Link(); link.setDestination(":"); paragraph.appendChild(link); - assertEquals("

\n", defaultRenderer().render(paragraph)); + assertThat(defaultRenderer().render(paragraph)).isEqualTo("

\n"); } @Test @@ -75,7 +70,7 @@ public void rawUrlsShouldNotFilterDangerousProtocols() { Link link = new Link(); link.setDestination("javascript:alert(5);"); paragraph.appendChild(link); - assertEquals("

\n", rawUrlsRenderer().render(paragraph)); + assertThat(rawUrlsRenderer().render(paragraph)).isEqualTo("

\n"); } @Test @@ -84,13 +79,13 @@ public void sanitizedUrlsShouldSetRelNoFollow() { Link link = new Link(); link.setDestination("/exampleUrl"); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); paragraph = new Paragraph(); link = new Link(); link.setDestination("https://google.com"); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); } @Test @@ -99,26 +94,26 @@ public void sanitizedUrlsShouldAllowSafeProtocols() { Link link = new Link(); link.setDestination("http://google.com"); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); paragraph = new Paragraph(); link = new Link(); link.setDestination("https://google.com"); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); paragraph = new Paragraph(); link = new Link(); link.setDestination("mailto:foo@bar.example.com"); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); String image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAQSURBVBhXY/iPBVBf8P9/AG8TY51nJdgkAAAAAElFTkSuQmCC"; paragraph = new Paragraph(); link = new Link(); link.setDestination(image); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); } @Test @@ -127,45 +122,42 @@ public void sanitizedUrlsShouldFilterDangerousProtocols() { Link link = new Link(); link.setDestination("javascript:alert(5);"); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); paragraph = new Paragraph(); link = new Link(); link.setDestination("ftp://google.com"); paragraph.appendChild(link); - assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); + assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("

\n"); } @Test public void percentEncodeUrlDisabled() { - assertEquals("

a

\n", defaultRenderer().render(parse("[a](foo&bar)"))); - assertEquals("

a

\n", defaultRenderer().render(parse("[a](ä)"))); - assertEquals("

a

\n", defaultRenderer().render(parse("[a](foo%20bar)"))); + assertThat(defaultRenderer().render(parse("[a](foo&bar)"))).isEqualTo("

a

\n"); + assertThat(defaultRenderer().render(parse("[a](ä)"))).isEqualTo("

a

\n"); + assertThat(defaultRenderer().render(parse("[a](foo%20bar)"))).isEqualTo("

a

\n"); } @Test public void percentEncodeUrl() { // Entities are escaped anyway - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](foo&bar)"))); + assertThat(percentEncodingRenderer().render(parse("[a](foo&bar)"))).isEqualTo("

a

\n"); // Existing encoding is preserved - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](foo%20bar)"))); - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](foo%61)"))); + assertThat(percentEncodingRenderer().render(parse("[a](foo%20bar)"))).isEqualTo("

a

\n"); + assertThat(percentEncodingRenderer().render(parse("[a](foo%61)"))).isEqualTo("

a

\n"); // Invalid encoding is escaped - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](foo%)"))); - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](foo%a)"))); - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](foo%a_)"))); - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](foo%xx)"))); + assertThat(percentEncodingRenderer().render(parse("[a](foo%)"))).isEqualTo("

a

\n"); + assertThat(percentEncodingRenderer().render(parse("[a](foo%a)"))).isEqualTo("

a

\n"); + assertThat(percentEncodingRenderer().render(parse("[a](foo%a_)"))).isEqualTo("

a

\n"); + assertThat(percentEncodingRenderer().render(parse("[a](foo%xx)"))).isEqualTo("

a

\n"); // Reserved characters are preserved, except for '[' and ']' - assertEquals("

a

\n", percentEncodingRenderer().render(parse("[a](!*'();:@&=+$,/?#[])"))); + assertThat(percentEncodingRenderer().render(parse("[a](!*'();:@&=+$,/?#[])"))).isEqualTo("

a

\n"); // Unreserved characters are preserved - assertEquals("

a

\n", - percentEncodingRenderer().render(parse("[a](ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~)"))); + assertThat(percentEncodingRenderer().render(parse("[a](ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~)"))).isEqualTo("

a

\n"); // Other characters are percent-encoded (LATIN SMALL LETTER A WITH DIAERESIS) - assertEquals("

a

\n", - percentEncodingRenderer().render(parse("[a](ä)"))); + assertThat(percentEncodingRenderer().render(parse("[a](ä)"))).isEqualTo("

a

\n"); // Other characters are percent-encoded (MUSICAL SYMBOL G CLEF, surrogate pair in UTF-16) - assertEquals("

a

\n", - percentEncodingRenderer().render(parse("[a](\uD834\uDD1E)"))); + assertThat(percentEncodingRenderer().render(parse("[a](\uD834\uDD1E)"))).isEqualTo("

a

\n"); } @Test @@ -192,10 +184,10 @@ public void setAttributes(Node node, String tagName, Map attribu HtmlRenderer renderer = HtmlRenderer.builder().attributeProviderFactory(custom).build(); String rendered = renderer.render(parse("```info\ncontent\n```")); - assertEquals("
content\n
\n", rendered); + assertThat(rendered).isEqualTo("
content\n
\n"); String rendered2 = renderer.render(parse("```evil\"\ncontent\n```")); - assertEquals("
content\n
\n", rendered2); + assertThat(rendered2).isEqualTo("
content\n
\n"); } @Test @@ -217,7 +209,7 @@ public void setAttributes(Node node, String tagName, Map attribu HtmlRenderer renderer = HtmlRenderer.builder().attributeProviderFactory(custom).build(); String rendered = renderer.render(parse("![foo](/url)\n")); - assertEquals("

\n", rendered); + assertThat(rendered).isEqualTo("

\n"); } @Test @@ -240,7 +232,7 @@ public void setAttributes(Node node, String tagName, Map attribu HtmlRenderer renderer = HtmlRenderer.builder().attributeProviderFactory(factory).build(); String rendered = renderer.render(parse("text node")); String secondPass = renderer.render(parse("text node")); - assertEquals(rendered, secondPass); + assertThat(secondPass).isEqualTo(rendered); } @Test @@ -264,30 +256,27 @@ public void render(Node node) { HtmlRenderer renderer = HtmlRenderer.builder().nodeRendererFactory(nodeRendererFactory).build(); String rendered = renderer.render(parse("foo [bar](/url)")); - assertEquals("

foo test

\n", rendered); + assertThat(rendered).isEqualTo("

foo test

\n"); } @Test public void orderedListStartZero() { - assertEquals("
    \n
  1. Test
  2. \n
\n", defaultRenderer().render(parse("0. Test\n"))); + assertThat(defaultRenderer().render(parse("0. Test\n"))).isEqualTo("
    \n
  1. Test
  2. \n
\n"); } @Test public void imageAltTextWithSoftLineBreak() { - assertEquals("

\"foo\nbar\"

\n", - defaultRenderer().render(parse("![foo\nbar](/url)\n"))); + assertThat(defaultRenderer().render(parse("![foo\nbar](/url)\n"))).isEqualTo("

\"foo\nbar\"

\n"); } @Test public void imageAltTextWithHardLineBreak() { - assertEquals("

\"foo\nbar\"

\n", - defaultRenderer().render(parse("![foo \nbar](/url)\n"))); + assertThat(defaultRenderer().render(parse("![foo \nbar](/url)\n"))).isEqualTo("

\"foo\nbar\"

\n"); } @Test public void imageAltTextWithEntities() { - assertEquals("

\"foo

\n", - defaultRenderer().render(parse("![foo ä](/url)\n"))); + assertThat(defaultRenderer().render(parse("![foo ä](/url)\n"))).isEqualTo("

\"foo

\n"); } @Test @@ -304,41 +293,35 @@ public void canRenderContentsOfSingleParagraph() { document.appendChild(current); } - assertEquals("Here I have a test link", - defaultRenderer().render(document)); + assertThat(defaultRenderer().render(document)).isEqualTo("Here I have a test link"); } @Test public void omitSingleParagraphP() { var renderer = HtmlRenderer.builder().omitSingleParagraphP(true).build(); - assertEquals("hi there", renderer.render(parse("hi *there*"))); + assertThat(renderer.render(parse("hi *there*"))).isEqualTo("hi there"); } @Test public void threading() throws Exception { - Parser parser = Parser.builder().build(); - String spec = TestResources.readAsString(TestResources.getSpec()); - final Node document = parser.parse(spec); + var parser = Parser.builder().build(); + var spec = TestResources.readAsString(TestResources.getSpec()); + var document = parser.parse(spec); - final HtmlRenderer htmlRenderer = HtmlRenderer.builder().build(); - String expectedRendering = htmlRenderer.render(document); + var htmlRenderer = HtmlRenderer.builder().build(); + var expectedRendering = htmlRenderer.render(document); // Render in parallel using the same HtmlRenderer instance. - List> futures = new ArrayList<>(); - ExecutorService executorService = Executors.newFixedThreadPool(4); + var futures = new ArrayList>(); + var executorService = Executors.newFixedThreadPool(4); for (int i = 0; i < 40; i++) { - Future future = executorService.submit(new Callable() { - @Override - public String call() throws Exception { - return htmlRenderer.render(document); - } - }); + var future = executorService.submit(() -> htmlRenderer.render(document)); futures.add(future); } - for (Future future : futures) { - String rendering = future.get(); - assertThat(rendering, is(expectedRendering)); + for (var future : futures) { + var rendering = future.get(); + assertThat(rendering).isEqualTo(expectedRendering); } } diff --git a/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java b/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java index 2f2463ccb..c05cac2d2 100644 --- a/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java +++ b/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java @@ -10,13 +10,13 @@ import org.commonmark.parser.Parser; import org.commonmark.parser.delimiter.DelimiterProcessor; import org.commonmark.renderer.html.HtmlRenderer; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class InlineParserContextTest { @@ -30,10 +30,10 @@ public void labelShouldBeOriginalNotNormalized() { String rendered = HtmlRenderer.builder().build().render(parser.parse(input)); // Lookup should pass original label to context - assertEquals(List.of("FooBarBaz"), inlineParserFactory.lookups); + assertThat(inlineParserFactory.lookups).isEqualTo(List.of("FooBarBaz")); // Context should normalize label for finding reference - assertEquals("

link with special label

\n", rendered); + assertThat(rendered).isEqualTo("

link with special label

\n"); } static class CapturingInlineParserFactory implements InlineParserFactory { diff --git a/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java b/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java index 81a71ee69..8410ff028 100644 --- a/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java +++ b/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java @@ -2,13 +2,11 @@ import org.commonmark.node.*; import org.commonmark.parser.Parser; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class LinkReferenceDefinitionNodeTest { @@ -17,12 +15,12 @@ public void testDefinitionWithoutParagraph() { Node document = parse("This is a paragraph with a [foo] link.\n\n[foo]: /url 'title'"); List nodes = Nodes.getChildren(document); - assertThat(nodes.size(), is(2)); - assertThat(nodes.get(0), instanceOf(Paragraph.class)); + assertThat(nodes).hasSize(2); + assertThat(nodes.get(0)).isInstanceOf(Paragraph.class); LinkReferenceDefinition definition = assertDef(nodes.get(1), "foo"); - assertThat(definition.getDestination(), is("/url")); - assertThat(definition.getTitle(), is("title")); + assertThat(definition.getDestination()).isEqualTo("/url"); + assertThat(definition.getTitle()).isEqualTo("title"); } @Test @@ -30,10 +28,10 @@ public void testDefinitionWithParagraph() { Node document = parse("[foo]: /url\nThis is a paragraph with a [foo] link."); List nodes = Nodes.getChildren(document); - assertThat(nodes.size(), is(2)); + assertThat(nodes).hasSize(2); // Note that definition is not part of the paragraph, it's a sibling - assertThat(nodes.get(0), instanceOf(LinkReferenceDefinition.class)); - assertThat(nodes.get(1), instanceOf(Paragraph.class)); + assertThat(nodes.get(0)).isInstanceOf(LinkReferenceDefinition.class); + assertThat(nodes.get(1)).isInstanceOf(Paragraph.class); } @Test @@ -41,8 +39,8 @@ public void testMultipleDefinitions() { Node document = parse("This is a paragraph with a [foo] link.\n\n[foo]: /url\n[bar]: /url"); List nodes = Nodes.getChildren(document); - assertThat(nodes.size(), is(3)); - assertThat(nodes.get(0), instanceOf(Paragraph.class)); + assertThat(nodes).hasSize(3); + assertThat(nodes.get(0)).isInstanceOf(Paragraph.class); assertDef(nodes.get(1), "foo"); assertDef(nodes.get(2), "bar"); } @@ -52,14 +50,14 @@ public void testMultipleDefinitionsWithSameLabel() { Node document = parse("This is a paragraph with a [foo] link.\n\n[foo]: /url1\n[foo]: /url2"); List nodes = Nodes.getChildren(document); - assertThat(nodes.size(), is(3)); - assertThat(nodes.get(0), instanceOf(Paragraph.class)); + assertThat(nodes).hasSize(3); + assertThat(nodes.get(0)).isInstanceOf(Paragraph.class); LinkReferenceDefinition def1 = assertDef(nodes.get(1), "foo"); - assertThat(def1.getDestination(), is("/url1")); + assertThat(def1.getDestination()).isEqualTo("/url1"); // When there's multiple definitions with the same label, the first one "wins", as in reference links will use // that. But we still want to preserve the original definitions in the document. LinkReferenceDefinition def2 = assertDef(nodes.get(2), "foo"); - assertThat(def2.getDestination(), is("/url2")); + assertThat(def2.getDestination()).isEqualTo("/url2"); } @Test @@ -67,42 +65,42 @@ public void testDefinitionOfReplacedBlock() { Node document = parse("[foo]: /url\nHeading\n======="); List nodes = Nodes.getChildren(document); - assertThat(nodes.size(), is(2)); + assertThat(nodes).hasSize(2); assertDef(nodes.get(0), "foo"); - assertThat(nodes.get(1), instanceOf(Heading.class)); + assertThat(nodes.get(1)).isInstanceOf(Heading.class); } @Test public void testDefinitionInListItem() { Node document = parse("* [foo]: /url\n [foo]\n"); - assertThat(document.getFirstChild(), instanceOf(BulletList.class)); + assertThat(document.getFirstChild()).isInstanceOf(BulletList.class); Node item = document.getFirstChild().getFirstChild(); - assertThat(item, instanceOf(ListItem.class)); + assertThat(item).isInstanceOf(ListItem.class); List nodes = Nodes.getChildren(item); - assertThat(nodes.size(), is(2)); + assertThat(nodes).hasSize(2); assertDef(nodes.get(0), "foo"); - assertThat(nodes.get(1), instanceOf(Paragraph.class)); + assertThat(nodes.get(1)).isInstanceOf(Paragraph.class); } @Test public void testDefinitionInListItem2() { Node document = parse("* [foo]: /url\n* [foo]\n"); - assertThat(document.getFirstChild(), instanceOf(BulletList.class)); + assertThat(document.getFirstChild()).isInstanceOf(BulletList.class); List items = Nodes.getChildren(document.getFirstChild()); - assertThat(items.size(), is(2)); + assertThat(items).hasSize(2); Node item1 = items.get(0); Node item2 = items.get(1); - assertThat(item1, instanceOf(ListItem.class)); - assertThat(item2, instanceOf(ListItem.class)); + assertThat(item1).isInstanceOf(ListItem.class); + assertThat(item2).isInstanceOf(ListItem.class); - assertThat(Nodes.getChildren(item1).size(), is(1)); + assertThat(Nodes.getChildren(item1)).hasSize(1); assertDef(item1.getFirstChild(), "foo"); - assertThat(Nodes.getChildren(item2).size(), is(1)); - assertThat(item2.getFirstChild(), instanceOf(Paragraph.class)); + assertThat(Nodes.getChildren(item2)).hasSize(1); + assertThat(item2.getFirstChild()).isInstanceOf(Paragraph.class); } @Test @@ -110,8 +108,8 @@ public void testDefinitionLabelCaseIsPreserved() { Node document = parse("This is a paragraph with a [foo] link.\n\n[fOo]: /url 'title'"); List nodes = Nodes.getChildren(document); - assertThat(nodes.size(), is(2)); - assertThat(nodes.get(0), instanceOf(Paragraph.class)); + assertThat(nodes).hasSize(2); + assertThat(nodes.get(0)).isInstanceOf(Paragraph.class); assertDef(nodes.get(1), "fOo"); } @@ -121,9 +119,9 @@ private static Node parse(String input) { } private static LinkReferenceDefinition assertDef(Node node, String label) { - assertThat(node, instanceOf(LinkReferenceDefinition.class)); + assertThat(node).isInstanceOf(LinkReferenceDefinition.class); LinkReferenceDefinition def = (LinkReferenceDefinition) node; - assertThat(def.getLabel(), is(label)); + assertThat(def.getLabel()).isEqualTo(label); return def; } } diff --git a/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java b/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java index 4a38bc412..02ac3abff 100644 --- a/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java @@ -3,9 +3,9 @@ import org.commonmark.node.ListItem; import org.commonmark.node.Node; import org.commonmark.parser.Parser; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class ListBlockParserTest { @@ -59,7 +59,7 @@ public void testOrderedListIndents() { private void assertListItemIndents(String input, int expectedMarkerIndent, int expectedContentIndent) { Node doc = PARSER.parse(input); ListItem listItem = Nodes.find(doc, ListItem.class); - assertEquals(expectedMarkerIndent, (int) listItem.getMarkerIndent()); - assertEquals(expectedContentIndent, (int) listItem.getContentIndent()); + assertThat((int) listItem.getMarkerIndent()).isEqualTo(expectedMarkerIndent); + assertThat((int) listItem.getContentIndent()).isEqualTo(expectedContentIndent); } } diff --git a/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java b/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java index 4889bb9ab..c6bda31ed 100644 --- a/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java @@ -1,6 +1,6 @@ package org.commonmark.test; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ListTightLooseTest extends CoreRenderingTestCase { diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index 447a50c3d..3b9ef09e9 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -5,7 +5,7 @@ import org.commonmark.parser.block.*; import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.testutil.TestResources; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; @@ -20,9 +20,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class ParserTest { @@ -40,7 +39,7 @@ public void ioReaderTest() throws IOException { Node document2 = parser.parse(spec); HtmlRenderer renderer = HtmlRenderer.builder().escapeHtml(true).build(); - assertEquals(renderer.render(document2), renderer.render(document1)); + assertThat(renderer.render(document1)).isEqualTo(renderer.render(document2)); } @Test @@ -50,9 +49,9 @@ public void customBlockParserFactory() { // The dashes would normally be a ThematicBreak Node document = parser.parse("hey\n\n---\n"); - assertThat(document.getFirstChild(), instanceOf(Paragraph.class)); - assertEquals("hey", ((Text) document.getFirstChild().getFirstChild()).getLiteral()); - assertThat(document.getLastChild(), instanceOf(DashBlock.class)); + assertThat(document.getFirstChild()).isInstanceOf(Paragraph.class); + assertThat(((Text) document.getFirstChild().getFirstChild()).getLiteral()).isEqualTo("hey"); + assertThat(document.getLastChild()).isInstanceOf(DashBlock.class); } @Test @@ -61,24 +60,25 @@ public void enabledBlockTypes() { Parser parser = Parser.builder().build(); // all core parsers by default Node document = parser.parse(given); - assertThat(document.getFirstChild(), instanceOf(Heading.class)); + assertThat(document.getFirstChild()).isInstanceOf(Heading.class); Set> headersOnly = new HashSet<>(); headersOnly.add(Heading.class); parser = Parser.builder().enabledBlockTypes(headersOnly).build(); document = parser.parse(given); - assertThat(document.getFirstChild(), instanceOf(Heading.class)); + assertThat(document.getFirstChild()).isInstanceOf(Heading.class); Set> noCoreTypes = new HashSet<>(); parser = Parser.builder().enabledBlockTypes(noCoreTypes).build(); document = parser.parse(given); - assertThat(document.getFirstChild(), not(instanceOf(Heading.class))); + assertThat(document.getFirstChild()).isNotInstanceOf(Heading.class); } - @Test(expected = IllegalArgumentException.class) + @Test public void enabledBlockTypesThrowsWhenGivenUnknownClass() { // BulletList can't be enabled separately at the moment, only all ListBlock types - Parser.builder().enabledBlockTypes(Set.of(Heading.class, BulletList.class)).build(); + assertThatThrownBy(() -> + Parser.builder().enabledBlockTypes(Set.of(Heading.class, BulletList.class)).build()).isInstanceOf(IllegalArgumentException.class); } @Test @@ -87,19 +87,19 @@ public void indentation() { Parser parser = Parser.builder().build(); Node document = parser.parse(given); - assertThat(document.getFirstChild(), instanceOf(BulletList.class)); + assertThat(document.getFirstChild()).isInstanceOf(BulletList.class); Node list = document.getFirstChild(); // first level list - assertEquals("expect one child", list.getFirstChild(), list.getLastChild()); - assertEquals("1 space", firstText(list.getFirstChild())); + assertThat(list.getLastChild()).as("expect one child").isEqualTo(list.getFirstChild()); + assertThat(firstText(list.getFirstChild())).isEqualTo("1 space"); list = list.getFirstChild().getLastChild(); // second level list - assertEquals("expect one child", list.getFirstChild(), list.getLastChild()); - assertEquals("3 spaces", firstText(list.getFirstChild())); + assertThat(list.getLastChild()).as("expect one child").isEqualTo(list.getFirstChild()); + assertThat(firstText(list.getFirstChild())).isEqualTo("3 spaces"); list = list.getFirstChild().getLastChild(); // third level list - assertEquals("5 spaces", firstText(list.getFirstChild())); - assertEquals("tab + space", firstText(list.getFirstChild().getNext())); + assertThat(firstText(list.getFirstChild())).isEqualTo("5 spaces"); + assertThat(firstText(list.getFirstChild().getNext())).isEqualTo("tab + space"); } @Test @@ -122,39 +122,34 @@ public InlineParser create(InlineParserContext inlineParserContext) { Parser parser = Parser.builder().inlineParserFactory(fakeInlineParserFactory).build(); String input = "**bold** **bold** ~~strikethrough~~"; - assertThat(parser.parse(input).getFirstChild().getFirstChild(), instanceOf(ThematicBreak.class)); + assertThat(parser.parse(input).getFirstChild().getFirstChild()).isInstanceOf(ThematicBreak.class); } @Test public void threading() throws Exception { - final Parser parser = Parser.builder().build(); - final String spec = TestResources.readAsString(TestResources.getSpec()); + var parser = Parser.builder().build(); + var spec = TestResources.readAsString(TestResources.getSpec()); - HtmlRenderer renderer = HtmlRenderer.builder().build(); - String expectedRendering = renderer.render(parser.parse(spec)); + var renderer = HtmlRenderer.builder().build(); + var expectedRendering = renderer.render(parser.parse(spec)); // Parse in parallel using the same Parser instance. - List> futures = new ArrayList<>(); - ExecutorService executorService = Executors.newFixedThreadPool(4); + var futures = new ArrayList>(); + var executorService = Executors.newFixedThreadPool(4); for (int i = 0; i < 40; i++) { - Future future = executorService.submit(new Callable() { - @Override - public Node call() throws Exception { - return parser.parse(spec); - } - }); + var future = executorService.submit(() -> parser.parse(spec)); futures.add(future); } - for (Future future : futures) { - Node node = future.get(); - assertThat(renderer.render(node), is(expectedRendering)); + for (var future : futures) { + var node = future.get(); + assertThat(renderer.render(node)).isEqualTo(expectedRendering); } } private String firstText(Node n) { while (!(n instanceof Text)) { - assertThat(n, notNullValue()); + assertThat(n).isNotNull(); n = n.getFirstChild(); } return ((Text) n).getLiteral(); diff --git a/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java b/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java index ae1310ed2..66d39de23 100644 --- a/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java +++ b/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java @@ -1,34 +1,21 @@ package org.commonmark.test; -import org.junit.FixMethodOrder; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.Stopwatch; -import org.junit.rules.Timeout; -import org.junit.runner.Description; -import org.junit.runners.MethodSorters; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.Timeout; import java.util.concurrent.TimeUnit; /** * Pathological input cases (from commonmark.js). */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Timeout(value = 3, unit = TimeUnit.SECONDS) +@TestMethodOrder(MethodOrderer.MethodName.class) public class PathologicalTest extends CoreRenderingTestCase { private int x = 100_000; - @Rule - public Timeout timeout = new Timeout(3, TimeUnit.SECONDS); - - @Rule - public Stopwatch stopwatch = new Stopwatch() { - @Override - protected void finished(long nanos, Description description) { - System.err.println(description.getDisplayName() + " took " + (nanos / 1000000) + " ms"); - } - }; - @Test public void nestedStrongEmphasis() { // this is limited by the stack size because visitor is recursive diff --git a/commonmark/src/test/java/org/commonmark/test/RegressionTest.java b/commonmark/src/test/java/org/commonmark/test/RegressionTest.java index 94b3a7439..900a6518c 100644 --- a/commonmark/src/test/java/org/commonmark/test/RegressionTest.java +++ b/commonmark/src/test/java/org/commonmark/test/RegressionTest.java @@ -6,18 +6,18 @@ 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.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -@RunWith(Parameterized.class) +@ParameterizedClass +@MethodSource("data") public class RegressionTest extends RenderingTestCase { private static final Parser PARSER = Parser.builder().build(); @@ -26,20 +26,13 @@ public class RegressionTest extends RenderingTestCase { private static final Map OVERRIDDEN_EXAMPLES = getOverriddenExamples(); - private final Example example; + @Parameter + Example example; - public RegressionTest(Example example) { - this.example = example; - } - - @Parameters(name = "{0}") - public static List data() { - List data = new ArrayList<>(); - for (URL regressionResource : TestResources.getRegressions()) { - List examples = ExampleReader.readExamples(regressionResource); - for (Example example : examples) { - data.add(new Object[]{example}); - } + static List data() { + var data = new ArrayList(); + for (var regressionResource : TestResources.getRegressions()) { + data.addAll(ExampleReader.readExamples(regressionResource)); } return data; } diff --git a/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java b/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java index 3fb95d386..5d34bf410 100644 --- a/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java +++ b/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java @@ -2,9 +2,10 @@ import org.commonmark.node.SourceSpan; import org.commonmark.parser.SourceLine; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class SourceLineTest { @@ -25,18 +26,20 @@ public void testSubstring() { assertSourceLine(line.substring(4, 4), "", null); } - @Test(expected = StringIndexOutOfBoundsException.class) + @Test public void testSubstringBeginOutOfBounds() { - SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4)).substring(3, 2); + var sourceLine = SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4)); + assertThatThrownBy(() -> sourceLine.substring(3, 2)).isInstanceOf(StringIndexOutOfBoundsException.class); } - @Test(expected = StringIndexOutOfBoundsException.class) + @Test public void testSubstringEndOutOfBounds() { - SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4)).substring(0, 5); + var sourceLine = SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4)); + assertThatThrownBy(() -> sourceLine.substring(0, 5)).isInstanceOf(StringIndexOutOfBoundsException.class); } private static void assertSourceLine(SourceLine sourceLine, String expectedContent, SourceSpan expectedSourceSpan) { - assertEquals(expectedContent, sourceLine.getContent()); - assertEquals(expectedSourceSpan, sourceLine.getSourceSpan()); + assertThat(sourceLine.getContent()).isEqualTo(expectedContent); + assertThat(sourceLine.getSourceSpan()).isEqualTo(expectedSourceSpan); } } diff --git a/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java b/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java index 57048b90e..f1bb231f4 100644 --- a/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java +++ b/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java @@ -1,10 +1,10 @@ package org.commonmark.test; import org.commonmark.node.SourceSpan; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class SourceSpanTest { @@ -12,52 +12,57 @@ public class SourceSpanTest { public void testSubSpan() { var span = SourceSpan.of(1, 2, 3, 5); - assertSame(span.subSpan(0), span); - assertSame(span.subSpan(0, 5), span); + assertThat(span.subSpan(0)).isSameAs(span); + assertThat(span.subSpan(0, 5)).isSameAs(span); - assertEquals(SourceSpan.of(1, 3, 4, 4), span.subSpan(1)); - assertEquals(SourceSpan.of(1, 4, 5, 3), span.subSpan(2)); - assertEquals(SourceSpan.of(1, 5, 6, 2), span.subSpan(3)); - assertEquals(SourceSpan.of(1, 6, 7, 1), span.subSpan(4)); + assertThat(span.subSpan(1)).isEqualTo(SourceSpan.of(1, 3, 4, 4)); + assertThat(span.subSpan(2)).isEqualTo(SourceSpan.of(1, 4, 5, 3)); + assertThat(span.subSpan(3)).isEqualTo(SourceSpan.of(1, 5, 6, 2)); + assertThat(span.subSpan(4)).isEqualTo(SourceSpan.of(1, 6, 7, 1)); // Not sure if empty spans are useful, but it probably makes sense to mirror how substrings work - assertEquals(SourceSpan.of(1, 7, 8, 0), span.subSpan(5)); - assertEquals("", "abcde".substring(5)); - - assertEquals(SourceSpan.of(1, 2, 3, 5), span.subSpan(0, 5)); - assertEquals(SourceSpan.of(1, 2, 3, 4), span.subSpan(0, 4)); - assertEquals(SourceSpan.of(1, 2, 3, 3), span.subSpan(0, 3)); - assertEquals(SourceSpan.of(1, 2, 3, 2), span.subSpan(0, 2)); - assertEquals(SourceSpan.of(1, 2, 3, 1), span.subSpan(0, 1)); - assertEquals(SourceSpan.of(1, 2, 3, 0), span.subSpan(0, 0)); - assertEquals("a", "abcde".substring(0, 1)); - assertEquals("", "abcde".substring(0, 0)); - - assertEquals(SourceSpan.of(1, 3, 4, 3), span.subSpan(1, 4)); - assertEquals(SourceSpan.of(1, 4, 5, 1), span.subSpan(2, 3)); + assertThat(span.subSpan(5)).isEqualTo(SourceSpan.of(1, 7, 8, 0)); + assertThat("abcde".substring(5)).isEqualTo(""); + + assertThat(span.subSpan(0, 5)).isEqualTo(SourceSpan.of(1, 2, 3, 5)); + assertThat(span.subSpan(0, 4)).isEqualTo(SourceSpan.of(1, 2, 3, 4)); + assertThat(span.subSpan(0, 3)).isEqualTo(SourceSpan.of(1, 2, 3, 3)); + assertThat(span.subSpan(0, 2)).isEqualTo(SourceSpan.of(1, 2, 3, 2)); + assertThat(span.subSpan(0, 1)).isEqualTo(SourceSpan.of(1, 2, 3, 1)); + assertThat(span.subSpan(0, 0)).isEqualTo(SourceSpan.of(1, 2, 3, 0)); + assertThat("abcde".substring(0, 1)).isEqualTo("a"); + assertThat("abcde".substring(0, 0)).isEqualTo(""); + + assertThat(span.subSpan(1, 4)).isEqualTo(SourceSpan.of(1, 3, 4, 3)); + assertThat(span.subSpan(2, 3)).isEqualTo(SourceSpan.of(1, 4, 5, 1)); } - @Test(expected = IndexOutOfBoundsException.class) + @Test public void testSubSpanBeginIndexNegative() { - SourceSpan.of(1, 2, 3, 5).subSpan(-1); + var sourceSpan = SourceSpan.of(1, 2, 3, 5); + assertThatThrownBy(() -> sourceSpan.subSpan(-1)).isInstanceOf(IndexOutOfBoundsException.class); } - @Test(expected = IndexOutOfBoundsException.class) + @Test public void testSubSpanBeginIndexOutOfBounds() { - SourceSpan.of(1, 2, 3, 5).subSpan(6); + var sourceSpan = SourceSpan.of(1, 2, 3, 5); + assertThatThrownBy(() -> sourceSpan.subSpan(6)).isInstanceOf(IndexOutOfBoundsException.class); } - @Test(expected = IndexOutOfBoundsException.class) + @Test public void testSubSpanEndIndexNegative() { - SourceSpan.of(1, 2, 3, 5).subSpan(0, -1); + var sourceSpan = SourceSpan.of(1, 2, 3, 5); + assertThatThrownBy(() -> sourceSpan.subSpan(0, -1)).isInstanceOf(IndexOutOfBoundsException.class); } - @Test(expected = IndexOutOfBoundsException.class) + @Test public void testSubSpanEndIndexOutOfBounds() { - SourceSpan.of(1, 2, 3, 5).subSpan(0, 6); + var sourceSpan = SourceSpan.of(1, 2, 3, 5); + assertThatThrownBy(() -> sourceSpan.subSpan(0, 6)).isInstanceOf(IndexOutOfBoundsException.class); } - @Test(expected = IndexOutOfBoundsException.class) + @Test public void testSubSpanBeginIndexGreaterThanEndIndex() { - SourceSpan.of(1, 2, 3, 5).subSpan(2, 1); + var sourceSpan = SourceSpan.of(1, 2, 3, 5); + assertThatThrownBy(() -> sourceSpan.subSpan(2, 1)).isInstanceOf(IndexOutOfBoundsException.class); } } diff --git a/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java b/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java index 23249d930..f4e9d0a17 100644 --- a/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java +++ b/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java @@ -3,7 +3,7 @@ import org.commonmark.node.*; import org.commonmark.parser.IncludeSourceSpans; import org.commonmark.parser.Parser; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.StringReader; @@ -11,7 +11,7 @@ import java.util.Deque; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SourceSpansTest { @@ -90,7 +90,7 @@ public void fencedCodeBlock() { Node document = PARSER.parse("```\nfoo\n```\nbar\n"); Paragraph paragraph = (Paragraph) document.getLastChild(); - assertEquals(List.of(SourceSpan.of(3, 0, 12, 3)), paragraph.getSourceSpans()); + assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 0, 12, 3))); } @Test @@ -133,7 +133,7 @@ public void listBlock() { Node document = PARSER.parse("* foo\n * bar\n"); ListBlock listBlock = (ListBlock) document.getFirstChild().getFirstChild().getLastChild(); - assertEquals(List.of(SourceSpan.of(1, 2, 8, 5)), listBlock.getSourceSpans()); + assertThat(listBlock.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 2, 8, 5))); } @Test @@ -158,10 +158,10 @@ public void linkReferenceDefinition() { Node document = PARSER.parse("[foo]: /url\ntext\n"); LinkReferenceDefinition linkReferenceDefinition = (LinkReferenceDefinition) document.getFirstChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), linkReferenceDefinition.getSourceSpans()); + assertThat(linkReferenceDefinition.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11))); Paragraph paragraph = (Paragraph) document.getLastChild(); - assertEquals(List.of(SourceSpan.of(1, 0, 12, 4)), paragraph.getSourceSpans()); + assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 4))); } @Test @@ -169,8 +169,8 @@ public void linkReferenceDefinitionMultiple() { var doc = PARSER.parse("[foo]: /foo\n[bar]: /bar\n"); var def1 = (LinkReferenceDefinition) doc.getFirstChild(); var def2 = (LinkReferenceDefinition) doc.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), def1.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(1, 0, 12, 11)), def2.getSourceSpans()); + assertThat(def1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11))); + assertThat(def2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 11))); } @Test @@ -178,8 +178,8 @@ public void linkReferenceDefinitionWithTitle() { var doc = PARSER.parse("[1]: #not-code \"Text\"\n[foo]: /foo\n"); var def1 = (LinkReferenceDefinition) doc.getFirstChild(); var def2 = (LinkReferenceDefinition) doc.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 21)), def1.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(1, 0, 22, 11)), def2.getSourceSpans()); + assertThat(def1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 21))); + assertThat(def2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 22, 11))); } @Test @@ -187,8 +187,8 @@ public void linkReferenceDefinitionWithTitleInvalid() { var doc = PARSER.parse("[foo]: /url\n\"title\" ok\n"); var def = Nodes.find(doc, LinkReferenceDefinition.class); var paragraph = Nodes.find(doc, Paragraph.class); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), def.getSourceSpans()); - assertEquals(List.of(SourceSpan.of(1, 0, 12, 10)), paragraph.getSourceSpans()); + assertThat(def.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11))); + assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 10))); } @Test @@ -198,10 +198,10 @@ public void linkReferenceDefinitionHeading() { Node document = PARSER.parse("[foo]: /url\nHeading\n===\n"); LinkReferenceDefinition linkReferenceDefinition = (LinkReferenceDefinition) document.getFirstChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), linkReferenceDefinition.getSourceSpans()); + assertThat(linkReferenceDefinition.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11))); Heading heading = (Heading) document.getLastChild(); - assertEquals(List.of(SourceSpan.of(1, 0, 12, 7), SourceSpan.of(2, 0, 20, 3)), heading.getSourceSpans()); + assertThat(heading.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 7), SourceSpan.of(2, 0, 20, 3))); } @Test @@ -212,13 +212,13 @@ public void lazyContinuationLines() { var doc = PARSER.parse("> > > foo\nbar\n"); var bq1 = (BlockQuote) doc.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 3)), bq1.getSourceSpans()); + assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 3))); var bq2 = (BlockQuote) bq1.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 3)), bq2.getSourceSpans()); + assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 3))); var bq3 = (BlockQuote) bq2.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 3)), bq3.getSourceSpans()); + assertThat(bq3.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 3))); var paragraph = (Paragraph) bq3.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 3)), paragraph.getSourceSpans()); + assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 3))); } { @@ -226,13 +226,13 @@ public void lazyContinuationLines() { var doc = PARSER.parse("> > > foo\nbars\n"); var bq1 = (BlockQuote) doc.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 4)), bq1.getSourceSpans()); + assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 4))); var bq2 = (BlockQuote) bq1.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 4)), bq2.getSourceSpans()); + assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 4))); var bq3 = (BlockQuote) bq2.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 4)), bq3.getSourceSpans()); + assertThat(bq3.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 4))); var paragraph = (Paragraph) bq3.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 4)), paragraph.getSourceSpans()); + assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 4))); } { @@ -240,15 +240,15 @@ public void lazyContinuationLines() { var doc = PARSER.parse("> 1. > Blockquote\ncontinued here."); var bq1 = (BlockQuote) doc.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 17), SourceSpan.of(1, 0, 18, 15)), bq1.getSourceSpans()); + assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 17), SourceSpan.of(1, 0, 18, 15))); var orderedList = (OrderedList) bq1.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15)), orderedList.getSourceSpans()); + assertThat(orderedList.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15))); var listItem = (ListItem) orderedList.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15)), listItem.getSourceSpans()); + assertThat(listItem.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15))); var bq2 = (BlockQuote) listItem.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 5, 5, 12), SourceSpan.of(1, 0, 18, 15)), bq2.getSourceSpans()); + assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 5, 5, 12), SourceSpan.of(1, 0, 18, 15))); var paragraph = (Paragraph) bq2.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 7, 7, 10), SourceSpan.of(1, 0, 18, 15)), paragraph.getSourceSpans()); + assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 7, 7, 10), SourceSpan.of(1, 0, 18, 15))); } { @@ -256,11 +256,11 @@ public void lazyContinuationLines() { var doc = PARSER.parse("> > foo\n> bar\n"); var bq1 = (BlockQuote) doc.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 0, 0, 7), SourceSpan.of(1, 0, 8, 5)), bq1.getSourceSpans()); + assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7), SourceSpan.of(1, 0, 8, 5))); var bq2 = (BlockQuote) bq1.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 2, 2, 5), SourceSpan.of(1, 2, 10, 3)), bq2.getSourceSpans()); + assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 5), SourceSpan.of(1, 2, 10, 3))); var paragraph = (Paragraph) bq2.getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 4, 4, 3), SourceSpan.of(1, 2, 10, 3)), paragraph.getSourceSpans()); + assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 3), SourceSpan.of(1, 2, 10, 3))); } } @@ -354,7 +354,7 @@ public void inlineEmphasis() { Node document = INLINES_PARSER.parse("*hey**"); Node lastText = document.getFirstChild().getLastChild(); - assertEquals(List.of(SourceSpan.of(0, 5, 5, 1)), lastText.getSourceSpans()); + assertThat(lastText.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 5, 5, 1))); } @Test @@ -381,8 +381,8 @@ public void differentLineTerminators() { private void assertVisualize(String source, String expected) { var doc = PARSER.parse(source); - assertEquals(expected, SourceSpanRenderer.renderWithLineColumn(doc, source)); - assertEquals(expected, SourceSpanRenderer.renderWithInputIndex(doc, source)); + assertThat(SourceSpanRenderer.renderWithLineColumn(doc, source)).isEqualTo(expected); + assertThat(SourceSpanRenderer.renderWithInputIndex(doc, source)).isEqualTo(expected); } private static void assertSpans(String input, Class nodeClass, SourceSpan... expectedSourceSpans) { @@ -405,7 +405,7 @@ private static void assertInlineSpans(String input, Class nodeCl private static void assertSpans(Node rootNode, Class nodeClass, SourceSpan... expectedSourceSpans) { Node node = findNode(rootNode, nodeClass); - assertEquals(List.of(expectedSourceSpans), node.getSourceSpans()); + assertThat(node.getSourceSpans()).isEqualTo(List.of(expectedSourceSpans)); } private static Node findNode(Node rootNode, Class nodeClass) { diff --git a/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java b/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java index 8d284c6f9..fefd8fb30 100644 --- a/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java +++ b/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java @@ -7,10 +7,10 @@ import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.testutil.SpecTestCase; import org.commonmark.testutil.example.Example; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.fail; import static org.commonmark.testutil.Asserts.assertRendering; -import static org.junit.Assert.fail; public class SpecCoreTest extends SpecTestCase { @@ -18,10 +18,6 @@ public class SpecCoreTest extends SpecTestCase { // The spec says URL-escaping is optional, but the examples assume that it's enabled. private static final HtmlRenderer RENDERER = HtmlRenderer.builder().percentEncodeUrls(true).build(); - public SpecCoreTest(Example example) { - super(example); - } - @Test public void testTextNodesContiguous() { final String source = example.getSource(); diff --git a/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java b/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java index ab0103e5a..47ca3da4e 100644 --- a/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java +++ b/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java @@ -4,7 +4,7 @@ import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.testutil.SpecTestCase; import org.commonmark.testutil.example.Example; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.commonmark.testutil.Asserts.assertRendering; @@ -17,10 +17,6 @@ public class SpecCrLfCoreTest extends SpecTestCase { // The spec says URL-escaping is optional, but the examples assume that it's enabled. private static final HtmlRenderer RENDERER = HtmlRenderer.builder().percentEncodeUrls(true).build(); - public SpecCrLfCoreTest(Example example) { - super(example); - } - @Test public void testHtmlRendering() { assertRendering(example.getSource(), example.getHtml(), render(example.getSource())); diff --git a/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java b/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java index 770a1aa21..2ebac1711 100644 --- a/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java +++ b/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java @@ -1,6 +1,6 @@ package org.commonmark.test; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SpecialInputTest extends CoreRenderingTestCase { diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java index 93ca87d93..bc443e0e2 100644 --- a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java @@ -9,7 +9,7 @@ import org.commonmark.renderer.text.TextContentRenderer; import org.commonmark.parser.Parser; import org.commonmark.testutil.Asserts; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java index 0be668a70..a9f37792e 100644 --- a/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java +++ b/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java @@ -1,9 +1,9 @@ package org.commonmark.test; import org.commonmark.renderer.text.TextContentWriter; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; public class TextContentWriterTest { @@ -14,7 +14,7 @@ public void whitespace() throws Exception { writer.write("foo"); writer.whitespace(); writer.write("bar"); - assertEquals("foo bar", stringBuilder.toString()); + assertThat(stringBuilder.toString()).isEqualTo("foo bar"); } @Test @@ -24,7 +24,7 @@ public void colon() throws Exception { writer.write("foo"); writer.colon(); writer.write("bar"); - assertEquals("foo:bar", stringBuilder.toString()); + assertThat(stringBuilder.toString()).isEqualTo("foo:bar"); } @Test @@ -34,7 +34,7 @@ public void line() throws Exception { writer.write("foo"); writer.line(); writer.write("bar"); - assertEquals("foo\nbar", stringBuilder.toString()); + assertThat(stringBuilder.toString()).isEqualTo("foo\nbar"); } @Test @@ -42,7 +42,7 @@ public void writeStripped() throws Exception { StringBuilder stringBuilder = new StringBuilder(); TextContentWriter writer = new TextContentWriter(stringBuilder); writer.writeStripped("foo\n bar"); - assertEquals("foo bar", stringBuilder.toString()); + assertThat(stringBuilder.toString()).isEqualTo("foo bar"); } @Test @@ -50,6 +50,6 @@ public void write() throws Exception { StringBuilder stringBuilder = new StringBuilder(); TextContentWriter writer = new TextContentWriter(stringBuilder); writer.writeStripped("foo bar"); - assertEquals("foo bar", stringBuilder.toString()); + assertThat(stringBuilder.toString()).isEqualTo("foo bar"); } } diff --git a/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java b/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java index 2b15f8add..1d564cca2 100644 --- a/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java @@ -2,9 +2,9 @@ import org.commonmark.node.ThematicBreak; import org.commonmark.parser.Parser; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class ThematicBreakParserTest { @@ -20,6 +20,6 @@ public void testLiteral() { private static void assertLiteral(String expected, String input) { var tb = Nodes.find(PARSER.parse(input), ThematicBreak.class); - assertEquals(expected, tb.getLiteral()); + assertThat(tb.getLiteral()).isEqualTo(expected); } } diff --git a/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java index 63901a49b..20cd9f5ab 100644 --- a/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java +++ b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java @@ -6,8 +6,8 @@ import org.commonmark.renderer.NodeRenderer; import org.commonmark.renderer.html.*; import org.commonmark.renderer.markdown.MarkdownRenderer; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.IOException; @@ -16,7 +16,7 @@ import java.util.Map; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class UsageExampleTest { @@ -25,7 +25,7 @@ public void parseAndRender() { Parser parser = Parser.builder().build(); Node document = parser.parse("This is *Markdown*"); HtmlRenderer renderer = HtmlRenderer.builder().escapeHtml(true).build(); - assertEquals("

This is Markdown

\n", renderer.render(document)); + assertThat(renderer.render(document)).isEqualTo("

This is Markdown

\n"); } @Test @@ -37,11 +37,11 @@ public void renderToMarkdown() { heading.appendChild(new Text("My title")); document.appendChild(heading); - assertEquals("## My title\n", renderer.render(document)); + assertThat(renderer.render(document)).isEqualTo("## My title\n"); } @Test - @Ignore + @Disabled public void parseReaderRender() throws IOException { Parser parser = Parser.builder().build(); try (InputStreamReader reader = new InputStreamReader(new FileInputStream("file.md"), StandardCharsets.UTF_8)) { @@ -56,7 +56,7 @@ public void visitor() { Node node = parser.parse("Example\n=======\n\nSome more text"); WordCountVisitor visitor = new WordCountVisitor(); node.accept(visitor); - assertEquals(4, visitor.wordCount); + assertThat(visitor.wordCount).isEqualTo(4); } @Test @@ -67,11 +67,11 @@ public void sourcePositions() { var doc = parser.parse(source); var emphasis = doc.getLastChild().getLastChild(); var s = emphasis.getSourceSpans().get(0); - assertEquals(2, s.getLineIndex()); - assertEquals(4, s.getColumnIndex()); - assertEquals(9, s.getInputIndex()); - assertEquals(5, s.getLength()); - assertEquals("*baz*", source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength())); + assertThat(s.getLineIndex()).isEqualTo(2); + assertThat(s.getColumnIndex()).isEqualTo(4); + assertThat(s.getInputIndex()).isEqualTo(9); + assertThat(s.getLength()).isEqualTo(5); + assertThat(source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength())).isEqualTo("*baz*"); } @Test @@ -87,8 +87,7 @@ public AttributeProvider create(AttributeProviderContext context) { .build(); Node document = parser.parse("![text](/url.png)"); - assertEquals("

\"text\"

\n", - renderer.render(document)); + assertThat(renderer.render(document)).isEqualTo("

\"text\"

\n"); } @Test @@ -104,7 +103,7 @@ public NodeRenderer create(HtmlNodeRendererContext context) { .build(); Node document = parser.parse("Example:\n\n code"); - assertEquals("

Example:

\n
code\n
\n", renderer.render(document)); + assertThat(renderer.render(document)).isEqualTo("

Example:

\n
code\n
\n"); } class WordCountVisitor extends AbstractVisitor { diff --git a/commonmark/src/test/java/org/commonmark/text/CharactersTest.java b/commonmark/src/test/java/org/commonmark/text/CharactersTest.java index a362cf53c..99f510cb7 100644 --- a/commonmark/src/test/java/org/commonmark/text/CharactersTest.java +++ b/commonmark/src/test/java/org/commonmark/text/CharactersTest.java @@ -1,9 +1,8 @@ package org.commonmark.text; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class CharactersTest { @@ -18,17 +17,17 @@ public void isPunctuation() { }; for (char c : chars) { - assertTrue("Expected to be punctuation: " + c, Characters.isPunctuationCodePoint(c)); + assertThat(Characters.isPunctuationCodePoint(c)).as("Expected to be punctuation: " + c).isTrue(); } } @Test public void isBlank() { - assertTrue(Characters.isBlank("")); - assertTrue(Characters.isBlank(" ")); - assertTrue(Characters.isBlank("\t")); - assertTrue(Characters.isBlank(" \t")); - assertFalse(Characters.isBlank("a")); - assertFalse(Characters.isBlank("\f")); + assertThat(Characters.isBlank("")).isTrue(); + assertThat(Characters.isBlank(" ")).isTrue(); + assertThat(Characters.isBlank("\t")).isTrue(); + assertThat(Characters.isBlank(" \t")).isTrue(); + assertThat(Characters.isBlank("a")).isFalse(); + assertThat(Characters.isBlank("\f")).isFalse(); } } diff --git a/pom.xml b/pom.xml index 29ca4ead2..db4003e51 100644 --- a/pom.xml +++ b/pom.xml @@ -79,7 +79,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.5.3 @@ -182,9 +182,14 @@ - junit - junit - 4.13.1 + org.junit.jupiter + junit-jupiter + 5.13.1 + + + org.assertj + assertj-core + 3.27.3 org.openjdk.jmh From add32d6e3aa3035dc3e4f59babfaa6760f275989 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 15 Jun 2025 21:25:51 +1000 Subject: [PATCH 20/89] Bump maven plugins --- pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index db4003e51..d2cc86712 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.14.0 11 @@ -47,7 +47,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.4.1 + 3.4.2 ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -57,12 +57,12 @@ org.apache.maven.plugins maven-install-plugin - 3.1.1 + 3.1.4 org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.11.2 *.internal,*.internal.* @@ -237,7 +237,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.4 + 3.2.7 sign-artifacts @@ -264,7 +264,7 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.13 From bb57a5f0921ab7e46f44128c875720617452564a Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 20 Jun 2025 21:55:30 +1000 Subject: [PATCH 21/89] Parse tables without blank line before GitHub doesn't mention it in their spec but it does work, e.g.: text |a| |---| |b| Parses as a paragraph with "text", and a table. --- CHANGELOG.md | 2 + .../gfm/tables/internal/TableBlockParser.java | 6 +- .../commonmark/ext/gfm/tables/TablesTest.java | 58 +++++++- .../commonmark/internal/BlockStartImpl.java | 13 ++ .../commonmark/internal/DocumentParser.java | 41 +++--- .../commonmark/internal/HeadingParser.java | 2 +- .../LinkReferenceDefinitionParser.java | 19 +++ .../commonmark/internal/ParagraphParser.java | 4 + .../commonmark/parser/block/BlockStart.java | 41 ++++++ .../parser/block/MatchedBlockParser.java | 3 +- .../test/BlockParserFactoryTest.java | 127 ++++++++++++++++++ .../java/org/commonmark/test/ParserTest.java | 41 ------ 12 files changed, 286 insertions(+), 71 deletions(-) create mode 100644 commonmark/src/test/java/org/commonmark/test/BlockParserFactoryTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 0808edb72..7ae5ffed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ with the exception that 0.x versions can break between minor versions. ### Added - More documentation with examples for `Node` classes ### Changed +- GitHub tables: Tables are now parsed even if there's no blank line before the + table heading, matching GitHub's behavior. ### Fixed - `MarkdownRenderer`: Fix precedence for `nodeRendererFactory`: Factories passed to the builder can now override rendering for core node types. 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/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 3135b4d8e..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; @@ -79,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" + @@ -702,6 +696,26 @@ public void danglingPipe() { "

|

\n"); } + @Test + public void interruptsParagraph() { + assertRendering("text\n" + + "|a |\n" + + "|---|\n" + + "|b |", "

text

\n" + + "
\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
a
b
\n"); + } + @Test public void attributeProviderIsApplied() { AttributeProviderFactory factory = new AttributeProviderFactory() { @@ -835,6 +849,36 @@ public void sourceSpans() { 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 protected String render(String source) { return RENDERER.render(PARSER.parse(source)); 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/DocumentParser.java b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java index 6059cc51c..d935f8d27 100644 --- a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java +++ b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java @@ -270,9 +270,15 @@ private void parseLine(String ln, int inputIndex) { } 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()) { @@ -498,24 +504,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 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() { diff --git a/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java b/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java index 3bc9ba5c4..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()); } } 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/ParagraphParser.java b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java index 93a2dd593..27eb1e647 100644 --- a/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java +++ b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java @@ -79,4 +79,8 @@ public void parseInlines(InlineParser inlineParser) { public SourceLines getParagraphLines() { return linkReferenceDefinitionParser.getParagraphLines(); } + + public List removeLines(int lines) { + return linkReferenceDefinitionParser.removeLines(lines); + } } diff --git a/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java b/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java index d9e7a2b49..c41f1caa3 100644 --- a/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java +++ b/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java @@ -10,18 +10,59 @@ public abstract class BlockStart { protected BlockStart() { } + /** + * Result for when there is no block start. + */ public static BlockStart none() { return null; } + /** + * Start block(s) with the specified parser(s). + */ public static BlockStart of(BlockParser... blockParsers) { return new BlockStartImpl(blockParsers); } + /** + * Continue parsing at the specified index. + * + * @param newIndex the new index, see {@link ParserState#getIndex()} + */ public abstract BlockStart atIndex(int newIndex); + /** + * Continue parsing at the specified column (for tab handling). + * + * @param newColumn the new column, see {@link ParserState#getColumn()} + */ public abstract BlockStart atColumn(int newColumn); + /** + * @deprecated use {@link #replaceParagraphLines(int)} instead; please raise an issue if that doesn't work for you + * for some reason. + */ + @Deprecated public abstract BlockStart replaceActiveBlockParser(); + /** + * Replace a number of lines from the current paragraph (as returned by + * {@link MatchedBlockParser#getParagraphLines()}) with the new block. + *

+ * This is useful for parsing blocks that start with normal paragraphs and only have special marker syntax in later + * lines, e.g. in this: + *

+     * Foo
+     * ===
+     * 
+ * The Foo line is initially parsed as a normal paragraph, then === is parsed as a heading + * marker, replacing the 1 paragraph line before. The end result is a single Heading block. + *

+ * Note that source spans from the replaced lines are automatically added to the new block. + * + * @param lines the number of lines to replace (at least 1); use {@link Integer#MAX_VALUE} to replace the whole + * paragraph + */ + public abstract BlockStart replaceParagraphLines(int lines); + } diff --git a/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java b/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java index 1f2bcfb2a..c4619d8c2 100644 --- a/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java +++ b/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java @@ -12,7 +12,8 @@ public interface MatchedBlockParser { BlockParser getMatchedBlockParser(); /** - * Returns the current paragraph lines if the matched block is a paragraph. + * Returns the current paragraph lines if the matched block is a paragraph. If you want to use some or all of the + * lines for starting a new block instead, use {@link BlockStart#replaceParagraphLines(int)}. * * @return paragraph content or an empty list */ diff --git a/commonmark/src/test/java/org/commonmark/test/BlockParserFactoryTest.java b/commonmark/src/test/java/org/commonmark/test/BlockParserFactoryTest.java new file mode 100644 index 000000000..b733d7970 --- /dev/null +++ b/commonmark/src/test/java/org/commonmark/test/BlockParserFactoryTest.java @@ -0,0 +1,127 @@ +package org.commonmark.test; + +import org.commonmark.node.*; +import org.commonmark.parser.IncludeSourceSpans; +import org.commonmark.parser.InlineParser; +import org.commonmark.parser.Parser; +import org.commonmark.parser.SourceLines; +import org.commonmark.parser.block.*; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BlockParserFactoryTest { + + @Test + public void customBlockParserFactory() { + var parser = Parser.builder().customBlockParserFactory(new DashBlockParser.Factory()).build(); + + // The dashes would normally be a ThematicBreak + var doc = parser.parse("hey\n\n---\n"); + + assertThat(doc.getFirstChild()).isInstanceOf(Paragraph.class); + assertThat(((Text) doc.getFirstChild().getFirstChild()).getLiteral()).isEqualTo("hey"); + assertThat(doc.getLastChild()).isInstanceOf(DashBlock.class); + } + + @Test + public void replaceActiveBlockParser() { + var parser = Parser.builder() + .customBlockParserFactory(new StarHeadingBlockParser.Factory()) + .includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES) + .build(); + + var doc = parser.parse("a\nbc\n***\n"); + + var heading = doc.getFirstChild(); + assertThat(heading).isInstanceOf(StarHeading.class); + assertThat(heading.getNext()).isNull(); + var a = heading.getFirstChild(); + assertThat(a).isInstanceOf(Text.class); + assertThat(((Text) a).getLiteral()).isEqualTo("a"); + var bc = a.getNext().getNext(); + assertThat(bc).isInstanceOf(Text.class); + assertThat(((Text) bc).getLiteral()).isEqualTo("bc"); + assertThat(bc.getNext()).isNull(); + + assertThat(heading.getSourceSpans()).isEqualTo(List.of( + SourceSpan.of(0, 0, 0, 1), + SourceSpan.of(1, 0, 2, 2), + SourceSpan.of(2, 0, 5, 3))); + assertThat(a.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 1))); + assertThat(bc.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 2, 2))); + } + + private static class DashBlock extends CustomBlock { + } + + private static class DashBlockParser extends AbstractBlockParser { + + private DashBlock dash = new DashBlock(); + + @Override + public Block getBlock() { + return dash; + } + + @Override + public BlockContinue tryContinue(ParserState parserState) { + return BlockContinue.none(); + } + + static class Factory extends AbstractBlockParserFactory { + + @Override + public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { + if (state.getLine().getContent().equals("---")) { + return BlockStart.of(new DashBlockParser()); + } + return BlockStart.none(); + } + } + } + + private static class StarHeading extends CustomBlock { + } + + private static class StarHeadingBlockParser extends AbstractBlockParser { + + private final SourceLines content; + private final StarHeading heading = new StarHeading(); + + StarHeadingBlockParser(SourceLines content) { + this.content = content; + } + + @Override + public Block getBlock() { + return heading; + } + + @Override + public BlockContinue tryContinue(ParserState parserState) { + return BlockContinue.none(); + } + + @Override + public void parseInlines(InlineParser inlineParser) { + inlineParser.parse(content, heading); + } + + static class Factory extends AbstractBlockParserFactory { + + @Override + public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { + var lines = matchedBlockParser.getParagraphLines(); + if (state.getLine().getContent().toString().startsWith("***")) { + return BlockStart.of(new StarHeadingBlockParser(lines)) + .replaceActiveBlockParser(); + } else { + return BlockStart.none(); + } + } + } + } +} diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index 3b9ef09e9..c119b5e2d 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -42,18 +42,6 @@ public void ioReaderTest() throws IOException { assertThat(renderer.render(document1)).isEqualTo(renderer.render(document2)); } - @Test - public void customBlockParserFactory() { - Parser parser = Parser.builder().customBlockParserFactory(new DashBlockParserFactory()).build(); - - // The dashes would normally be a ThematicBreak - Node document = parser.parse("hey\n\n---\n"); - - assertThat(document.getFirstChild()).isInstanceOf(Paragraph.class); - assertThat(((Text) document.getFirstChild().getFirstChild()).getLiteral()).isEqualTo("hey"); - assertThat(document.getLastChild()).isInstanceOf(DashBlock.class); - } - @Test public void enabledBlockTypes() { String given = "# heading 1\n\nnot a heading"; @@ -154,33 +142,4 @@ private String firstText(Node n) { } return ((Text) n).getLiteral(); } - - private static class DashBlock extends CustomBlock { - } - - private static class DashBlockParser extends AbstractBlockParser { - - private DashBlock dash = new DashBlock(); - - @Override - public Block getBlock() { - return dash; - } - - @Override - public BlockContinue tryContinue(ParserState parserState) { - return BlockContinue.none(); - } - } - - private static class DashBlockParserFactory extends AbstractBlockParserFactory { - - @Override - public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { - if (state.getLine().getContent().equals("---")) { - return BlockStart.of(new DashBlockParser()); - } - return BlockStart.none(); - } - } } From 971aaa9edeaa7ef161e4553b001be91bf2d83cea Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 20 Jun 2025 22:32:23 +1000 Subject: [PATCH 22/89] MarkdownRenderer: Fix ordered list with long start number (fixes #382) --- CHANGELOG.md | 2 ++ .../renderer/markdown/CoreMarkdownNodeRenderer.java | 2 +- .../commonmark/renderer/markdown/MarkdownRendererTest.java | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae5ffed2..88a333502 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ with the exception that 0.x versions can break between minor versions. ### Fixed - `MarkdownRenderer`: Fix precedence for `nodeRendererFactory`: Factories passed to the builder can now override rendering for core node types. +- `MarkdownRenderer`: Fix exception with ordered lists with a long first number + followed by a shorter one (#382) - Fix warning in Eclipse about "missing 'requires transitive'" - Fix Android incompatibility with `requireNonNullElseGet` diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java index 32510feaf..5a81676f4 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java @@ -257,7 +257,7 @@ public void visit(ListItem listItem) { throw new IllegalStateException("Unknown list holder type: " + listHolder); } Integer contentIndent = listItem.getContentIndent(); - String spaces = contentIndent != null ? repeat(" ", contentIndent - marker.length()) : " "; + String spaces = contentIndent != null ? repeat(" ", Math.max(contentIndent - marker.length(), 1)) : " "; writer.writePrefix(marker); writer.writePrefix(spaces); writer.pushPrefix(repeat(" ", marker.length() + spaces.length())); diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java index 36b8adad9..6a468a08e 100644 --- a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java @@ -176,6 +176,13 @@ public void testOrderedListItemsFromAst() { assertRendering("", "2) Test\n", render(doc)); } + @Test + public void testOrderedListItemsWithStartNumberLongerThanLaterNumber() { + var source = "10001.\n20.\n"; + var doc = parse(source); + assertRendering(source, "10001. \n10002. \n", render(doc)); + } + // Inlines @Test From 5e6b740271587bb0881a5f065d56f05f8ae7ed9f Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 20 Jun 2025 22:45:24 +1000 Subject: [PATCH 23/89] Migrate from OSSRH to Central Publisher Portal --- .github/workflows/release.yml | 13 +++++++------ pom.xml | 27 ++++++++------------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4727b103d..c0531ca55 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,11 +19,12 @@ jobs: - name: Set up Maven Central repository uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 24 distribution: 'zulu' - server-id: ossrh - server-username: MAVEN_USERNAME # env variable to use for username in release - server-password: MAVEN_PASSWORD # env variable to use for password in release + # See https://central.sonatype.org/publish/publish-portal-maven/ + server-id: central + server-username: CENTRAL_USERNAME # env variable to use for username in release + server-password: CENTRAL_PASSWORD # env variable to use for password in release gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable to use for passphrase in release @@ -37,6 +38,6 @@ jobs: mvn -B -Dusername=${{ secrets.GH_USERNAME }} -Dpassword=${{ secrets.GH_ACCESS_TOKEN }} release:prepare mvn -B release:perform env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }} + CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} diff --git a/pom.xml b/pom.xml index d2cc86712..533d2dc0b 100644 --- a/pom.xml +++ b/pom.xml @@ -85,22 +85,22 @@ + - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 true - ossrh - https://oss.sonatype.org/ - true - 10 + central + true + published org.apache.maven.plugins maven-release-plugin - 3.0.1 + 3.1.1 true false @@ -307,15 +307,4 @@ HEAD - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - From adcf2f232c9fcf55e29770c78a9285d5446517f3 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 20 Jun 2025 22:51:36 +1000 Subject: [PATCH 24/89] Prepare CHANGELOG for release 0.25 --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88a333502..3d103521e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. -## [Unreleased] +## [0.25.0] - 2025-06-20 ### Added +- Include OSGi metadata in jars (`META-INF/MANIFEST.MF` files) - More documentation with examples for `Node` classes ### Changed - GitHub tables: Tables are now parsed even if there's no blank line before the @@ -473,7 +474,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. -[Unreleased]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...main +[0.25.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...commonmark-parent-0.25.0 [0.24.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.23.0...commonmark-parent-0.24.0 [0.23.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.22.0...commonmark-parent-0.23.0 [0.22.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.21.0...commonmark-parent-0.22.0 From 6354f0244746a5e6cd6b44f0be61e153681f2270 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 20 Jun 2025 22:52:07 +1000 Subject: [PATCH 25/89] Prepare for version 0.25.0 Ran `mvn versions:set -DnewVersion=0.25.0-SNAPSHOT` --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 22 +++++++++++----------- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 7f6a5cbc3..03dba9cb4 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index d34e4e2f9..5463aa0f1 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 1fd1269bf..a3d7e3ab5 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index d261a1d24..64eee7d5c 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index e4a62b2b3..7ebe055a5 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 55b0dce20..593d32f37 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 9dbe2cf06..a94cdc229 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 3926c8b5e..6856a9064 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.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 9e329e0d3..e492ede62 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.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index c5e572291..072883792 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index d6a38f4a9..55c415dc0 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 17f3bc747..98e33b403 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index d2cc86712..e7a18f915 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,52 +132,52 @@ org.commonmark commonmark - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-ins - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT org.commonmark commonmark-test-util - 0.24.1-SNAPSHOT + 0.25.0-SNAPSHOT From 99e6050ae12e862e39f4a0b81108ec155d4817af Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:57:31 +0000 Subject: [PATCH 26/89] [maven-release-plugin] prepare release commonmark-parent-0.25.0 --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 24 ++++++++++++------------ 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 03dba9cb4..aab6e8beb 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 5463aa0f1..12ddbf41f 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index a3d7e3ab5..e6b203427 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 64eee7d5c..fc88b6351 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 7ebe055a5..9674f0baa 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 593d32f37..7475feef3 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index a94cdc229..9a885e6b8 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 6856a9064..38b3569de 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.25.0-SNAPSHOT + 0.25.0 commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index e492ede62..7cc681e1d 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.25.0-SNAPSHOT + 0.25.0 commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 072883792..67f4e6da0 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 55c415dc0..136aa3d90 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 98e33b403..b369b1bda 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark diff --git a/pom.xml b/pom.xml index 5ef080181..60fbf80e0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.25.0-SNAPSHOT + 0.25.0 commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,52 +132,52 @@ org.commonmark commonmark - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-autolink - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-image-attributes - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-ins - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-gfm-strikethrough - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-gfm-tables - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-heading-anchor - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-task-list-items - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-ext-yaml-front-matter - 0.25.0-SNAPSHOT + 0.25.0 org.commonmark commonmark-test-util - 0.25.0-SNAPSHOT + 0.25.0 @@ -304,7 +304,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - HEAD + commonmark-parent-0.25.0 From 8d5918d4efd3ad1dca4626af8a516872f7aa6121 Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:57:32 +0000 Subject: [PATCH 27/89] [maven-release-plugin] prepare for next development iteration --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 24 ++++++++++++------------ 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index aab6e8beb..d0d86d572 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 12ddbf41f..d965c409e 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index e6b203427..05f290120 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index fc88b6351..6445eb34a 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 9674f0baa..a1b137d73 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 7475feef3..3f36b72bf 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 9a885e6b8..0c04f6311 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 38b3569de..051fc45bb 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.25.0 + 0.25.1-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 7cc681e1d..114d860c0 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.25.0 + 0.25.1-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 67f4e6da0..d5e108b0a 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 136aa3d90..edad4a84d 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index b369b1bda..22e9795db 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index 60fbf80e0..6fd0c8df9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.25.0 + 0.25.1-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,52 +132,52 @@ org.commonmark commonmark - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-ins - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.25.0 + 0.25.1-SNAPSHOT org.commonmark commonmark-test-util - 0.25.0 + 0.25.1-SNAPSHOT @@ -304,7 +304,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - commonmark-parent-0.25.0 + HEAD From c577edfe08e523ab806a111f629a518c4de660be Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 20 Jun 2025 23:12:27 +1000 Subject: [PATCH 28/89] README: Bump version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 73e69d9df..fa632a96d 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Coordinates for core library (see all on [Maven Central]): org.commonmark commonmark - 0.24.0 + 0.25.0 ``` @@ -291,7 +291,7 @@ First, add an additional dependency (see [Maven Central] for others): org.commonmark commonmark-ext-gfm-tables - 0.24.0 + 0.25.0 ``` From 33737b7d5dd65153f2d022f7b5770f0fa0630296 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 20 Jun 2025 23:15:52 +1000 Subject: [PATCH 29/89] CHANGELOG: Add issue links --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d103521e..346c0d6b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,18 +8,18 @@ with the exception that 0.x versions can break between minor versions. ## [0.25.0] - 2025-06-20 ### Added -- Include OSGi metadata in jars (`META-INF/MANIFEST.MF` files) -- More documentation with examples for `Node` classes +- Include OSGi metadata in jars (`META-INF/MANIFEST.MF` files) (#378) +- More documentation with examples for `Node` classes (#370) ### Changed - GitHub tables: Tables are now parsed even if there's no blank line before the - table heading, matching GitHub's behavior. + table heading, matching GitHub's behavior. (#381) ### Fixed - `MarkdownRenderer`: Fix precedence for `nodeRendererFactory`: Factories passed - to the builder can now override rendering for core node types. + to the builder can now override rendering for core node types. (#368) - `MarkdownRenderer`: Fix exception with ordered lists with a long first number followed by a shorter one (#382) -- Fix warning in Eclipse about "missing 'requires transitive'" -- Fix Android incompatibility with `requireNonNullElseGet` +- Fix warning in Eclipse about "missing 'requires transitive'" (#358) +- Fix Android incompatibility with `requireNonNullElseGet` (#369) ## [0.24.0] - 2024-10-21 ### Added From 3111fed6c73dc6e961c590c58973b44e12fe5464 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 1 Aug 2025 17:04:54 +1000 Subject: [PATCH 30/89] footnotes: Fix multiple paragraphs separated by blank lines --- CHANGELOG.md | 6 ++++++ .../internal/FootnoteBlockParser.java | 4 ++++ .../FootnoteMarkdownNodeRenderer.java | 2 -- .../FootnoteMarkdownRendererTest.java | 14 ++++++++++++- .../ext/footnotes/FootnotesTest.java | 20 +++++++++++++++++++ commonmark-integration-test/pom.xml | 4 ++++ .../commonmark/integration/Extensions.java | 2 ++ .../MarkdownRendererIntegrationTest.java | 12 ++--------- pom.xml | 5 +++++ 9 files changed, 56 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 346c0d6b0..8068d3c02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. +## Unreleased +### Fixed +- footnotes: Fix parsing of footnote definitions containing multiple paragraphs + separated by blank lines. Before it only worked if paragraphs were separated + by lines of 4 spaces. (#388) + ## [0.25.0] - 2025-06-20 ### Added - Include OSGi metadata in jars (`META-INF/MANIFEST.MF` files) (#378) diff --git a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteBlockParser.java b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteBlockParser.java index 59ee5529e..110bdef20 100644 --- a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteBlockParser.java +++ b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteBlockParser.java @@ -39,6 +39,10 @@ public BlockContinue tryContinue(ParserState parserState) { if (parserState.getIndent() >= 4) { // It looks like content needs to be indented by 4 so that it's part of a footnote (instead of starting a new block). return BlockContinue.atColumn(4); + } else if (parserState.isBlank()) { + // A blank line doesn't finish a footnote yet. If there's another line with indent >= 4 after it, + // that should result in another paragraph in this footnote definition. + return BlockContinue.atIndex(parserState.getIndex()); } else { // We're not continuing to give other block parsers a chance to interrupt this definition. // But if no other block parser applied (including another FootnotesBlockParser), we will diff --git a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java index eddf6f2ea..3dcf4fc83 100644 --- a/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java +++ b/commonmark-ext-footnotes/src/main/java/org/commonmark/ext/footnotes/internal/FootnoteMarkdownNodeRenderer.java @@ -55,9 +55,7 @@ private void renderDefinition(FootnoteDefinition def) { writer.raw("]: "); writer.pushPrefix(" "); - writer.pushTight(true); renderChildren(def); - writer.popTight(); writer.popPrefix(); } diff --git a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java index be3af6715..2f1125a02 100644 --- a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java +++ b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnoteMarkdownRendererTest.java @@ -23,7 +23,7 @@ public void testSimple() { @Test public void testUnreferenced() { // Whether a reference has a corresponding definition or vice versa shouldn't matter for Markdown rendering. - assertRoundTrip("Test [^foo]\n\n[^foo]: one\n[^bar]: two\n"); + assertRoundTrip("Test [^foo]\n\n[^foo]: one\n\n[^bar]: two\n"); } @Test @@ -36,6 +36,18 @@ public void testBackslashInLabel() { assertRoundTrip("[^\\foo]\n\n[^\\foo]: note\n"); } + @Test + public void testMultipleLines() { + assertRoundTrip("Test [^1]\n\n[^1]: footnote l1\n footnote l2\n"); + } + + @Test + public void testMultipleParagraphs() { + // Note that the line between p1 and p2 could be blank too (instead of 4 spaces), but we currently don't + // preserve that information. + assertRoundTrip("Test [^1]\n\n[^1]: footnote p1\n \n footnote p2\n"); + } + @Test public void testInline() { assertRoundTrip("^[test *foo*]\n"); diff --git a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java index 3fa726d8f..2477dbdf8 100644 --- a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java +++ b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java @@ -102,6 +102,26 @@ public void testDefContainsMultipleLines() { assertText("still", paragraph.getLastChild()); } + @Test + public void testDefContainsMultipleParagraphs() { + var doc = PARSER.parse("[^1]: footnote p1\n\n footnote p2\n"); + var def = find(doc, FootnoteDefinition.class); + assertThat(def.getLabel()).isEqualTo("1"); + var p1 = (Paragraph) def.getFirstChild(); + assertText("footnote p1", p1.getFirstChild()); + var p2 = (Paragraph) p1.getNext(); + assertText("footnote p2", p2.getFirstChild()); + } + + @Test + public void testDefFollowedByParagraph() { + var doc = PARSER.parse("[^1]: footnote\n\nnormal paragraph\n"); + var def = find(doc, FootnoteDefinition.class); + assertThat(def.getLabel()).isEqualTo("1"); + assertText("footnote", def.getFirstChild().getFirstChild()); + assertText("normal paragraph", def.getNext().getFirstChild()); + } + @Test public void testDefContainsList() { var doc = PARSER.parse("[^1]: - foo\n - bar\n"); diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index d5e108b0a..b495a85af 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -20,6 +20,10 @@ org.commonmark commonmark-ext-autolink + + org.commonmark + commonmark-ext-footnotes + org.commonmark commonmark-ext-ins 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 ee7ee5290..8df0408cb 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,6 +2,7 @@ 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.strikethrough.StrikethroughExtension; import org.commonmark.ext.gfm.tables.TablesExtension; @@ -15,6 +16,7 @@ public class Extensions { static final List ALL_EXTENSIONS = List.of( AutolinkExtension.create(), + FootnotesExtension.create(), ImageAttributesExtension.create(), InsExtension.create(), StrikethroughExtension.create(), 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 9ab5c32a3..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 @@ -18,16 +18,8 @@ public class MarkdownRendererIntegrationTest { - private static final List EXTENSIONS = List.of( - 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() { diff --git a/pom.xml b/pom.xml index 6fd0c8df9..d036b1452 100644 --- a/pom.xml +++ b/pom.xml @@ -139,6 +139,11 @@ commonmark-ext-autolink 0.25.1-SNAPSHOT + + org.commonmark + commonmark-ext-footnotes + 0.25.1-SNAPSHOT + org.commonmark commonmark-ext-image-attributes From bd50c7d587c435c80eeb1f59d2c41fed3efefb33 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 1 Aug 2025 22:04:03 +1000 Subject: [PATCH 31/89] Prepare CHANGELOG for version 0.25.1 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8068d3c02..b04e11ad8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. -## Unreleased +## [0.25.1] - 2025-08-01 ### Fixed - footnotes: Fix parsing of footnote definitions containing multiple paragraphs separated by blank lines. Before it only worked if paragraphs were separated @@ -480,6 +480,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. +[0.25.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.0...commonmark-parent-0.25.1 [0.25.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...commonmark-parent-0.25.0 [0.24.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.23.0...commonmark-parent-0.24.0 [0.23.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.22.0...commonmark-parent-0.23.0 From b703d6a3561a6eb00f6de8dfab85cf0301cb7a2d Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:07:57 +0000 Subject: [PATCH 32/89] [maven-release-plugin] prepare release commonmark-parent-0.25.1 --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index d0d86d572..a1f5f6eb6 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index d965c409e..a5979764e 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 05f290120..07a0a03d7 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 6445eb34a..30df41254 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index a1b137d73..8a87e9207 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 3f36b72bf..3e29fdd80 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 0c04f6311..af7ac7a54 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 051fc45bb..b9185381b 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.25.1-SNAPSHOT + 0.25.1 commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 114d860c0..651c0e2f8 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.25.1-SNAPSHOT + 0.25.1 commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index b495a85af..8300a25f7 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index edad4a84d..58453821d 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 22e9795db..a58676447 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark diff --git a/pom.xml b/pom.xml index d036b1452..e02b49942 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.25.1-SNAPSHOT + 0.25.1 commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-autolink - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-footnotes - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-image-attributes - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-ins - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-gfm-strikethrough - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-gfm-tables - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-heading-anchor - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-task-list-items - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-ext-yaml-front-matter - 0.25.1-SNAPSHOT + 0.25.1 org.commonmark commonmark-test-util - 0.25.1-SNAPSHOT + 0.25.1 @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - HEAD + commonmark-parent-0.25.1 From 3c151053d720c99372d2d4c5c6db667135563ecf Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:07:58 +0000 Subject: [PATCH 33/89] [maven-release-plugin] prepare for next development iteration --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index a1f5f6eb6..360ad9781 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index a5979764e..31a92ca77 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 07a0a03d7..d051a0ee0 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 30df41254..740460ada 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 8a87e9207..1270cd1c6 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 3e29fdd80..f392c8ea4 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index af7ac7a54..1b797791f 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index b9185381b..f3e626f89 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.25.1 + 0.25.2-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 651c0e2f8..93d5e3e2c 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.25.1 + 0.25.2-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 8300a25f7..810919ce6 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 58453821d..753bb00de 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index a58676447..26edd4ffd 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index e02b49942..53289614e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.25.1 + 0.25.2-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-ins - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.25.1 + 0.25.2-SNAPSHOT org.commonmark commonmark-test-util - 0.25.1 + 0.25.2-SNAPSHOT @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - commonmark-parent-0.25.1 + HEAD From 9926b761eb2e9192d80cda46917ea2f0f0f2c7dc Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 1 Aug 2025 22:27:12 +1000 Subject: [PATCH 34/89] README: Bump version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa632a96d..62f5affe3 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Coordinates for core library (see all on [Maven Central]): org.commonmark commonmark - 0.25.0 + 0.25.1 ``` @@ -291,7 +291,7 @@ First, add an additional dependency (see [Maven Central] for others): org.commonmark commonmark-ext-gfm-tables - 0.25.0 + 0.25.1 ``` From 70da759095d7da0cf39fbc2198250b718030652f Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Wed, 10 Sep 2025 09:08:15 -0700 Subject: [PATCH 35/89] Add Tests for Inline Alt Text --- .../java/org/commonmark/test/HtmlRendererTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java index 413bb9d8a..02d970949 100644 --- a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java @@ -279,6 +279,16 @@ public void imageAltTextWithEntities() { assertThat(defaultRenderer().render(parse("![foo ä](/url)\n"))).isEqualTo("

\"foo

\n"); } + @Test + public void imageAltTextWithInlines() { + assertThat(defaultRenderer().render(parse("![_foo_ **bar** [link](/url)](/url)\n"))).isEqualTo("

\"foo

\n"); + } + + @Test + public void imageAltTextWithCode() { + assertThat(defaultRenderer().render(parse("![`foo` bar](/url)\n"))).isEqualTo("

\"foo

\n"); + } + @Test public void canRenderContentsOfSingleParagraph() { Node paragraphs = parse("Here I have a test [link](http://www.google.com)"); From cc4740f42fc50c91a82b1d63fb6fc84df4039be7 Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Wed, 10 Sep 2025 09:12:14 -0700 Subject: [PATCH 36/89] Add Visitor for Code Spans --- .../org/commonmark/renderer/html/CoreHtmlNodeRenderer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java index 0603aa013..5c536558e 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java @@ -311,6 +311,11 @@ public void visit(Text text) { sb.append(text.getLiteral()); } + @Override + public void visit(Code code) { + sb.append(code.getLiteral()); + } + @Override public void visit(SoftLineBreak softLineBreak) { sb.append('\n'); From 77cadac10faa5cdccf0c8b4c0205d486d648f572 Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Wed, 10 Sep 2025 09:20:33 -0700 Subject: [PATCH 37/89] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b04e11ad8..8020b3ce5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. +## Unreleased +### Fixed + +- Fix rendering of image alt text to include contents of code spans (`` `code` ``). (#398) + ## [0.25.1] - 2025-08-01 ### Fixed - footnotes: Fix parsing of footnote definitions containing multiple paragraphs From 926013c7a626c4ed5ad5602de7eb7c3dfc4f3366 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 12 Sep 2025 22:50:47 +1000 Subject: [PATCH 38/89] Disallow link when a footnote is nested inside it Fixes #400. --- CHANGELOG.md | 9 +++++- .../ext/footnotes/FootnotesTest.java | 23 ++++++++++++++ .../commonmark/internal/InlineParserImpl.java | 31 +++++++++++++------ 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8020b3ce5..f41d904a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,16 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html with the exception that 0.x versions can break between minor versions. ## Unreleased +### Changed +- A `LinkProcessor` using `replaceWith` now also stops outer links from being + parsed as links, same as with `wrapTextIn`. This prevents nested links, see + footnotes change below. ### Fixed - - Fix rendering of image alt text to include contents of code spans (`` `code` ``). (#398) +- footnotes: Fix footnotes nested within links. Before, both the link and the + footnote reference would be parsed and lead to nested `` elements, which + is disallowed. Now, only the footnote is parsed and the outer link becomes + plain text; this matches the behavior of links. (#400) ## [0.25.1] - 2025-08-01 ### Fixed diff --git a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java index 2477dbdf8..7763cedb4 100644 --- a/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java +++ b/commonmark-ext-footnotes/src/test/java/org/commonmark/ext/footnotes/FootnotesTest.java @@ -254,6 +254,29 @@ public void testReferenceLinkWithoutDefinition() { assertText("[foo]", paragraph.getLastChild()); } + @Test + public void testFootnoteInLink() { + // Expected to behave the same way as a link within a link, see https://spec.commonmark.org/0.31.2/#example-518 + // i.e. the first (inner) link is parsed, which means the outer one becomes plain text, as nesting links is not + // allowed. + var doc = PARSER.parse("[link with footnote ref [^1]](https://example.com)\n\n[^1]: footnote\n"); + var ref = find(doc, FootnoteReference.class); + assertThat(ref.getLabel()).isEqualTo("1"); + var paragraph = doc.getFirstChild(); + assertText("[link with footnote ref ", paragraph.getFirstChild()); + assertText("](https://example.com)", paragraph.getLastChild()); + } + + @Test + public void testFootnoteWithMarkerInLink() { + var doc = PARSER.parse("[link with footnote ref ![^1]](https://example.com)\n\n[^1]: footnote\n"); + var ref = find(doc, FootnoteReference.class); + assertThat(ref.getLabel()).isEqualTo("1"); + var paragraph = doc.getFirstChild(); + assertText("[link with footnote ref !", paragraph.getFirstChild()); + assertText("](https://example.com)", paragraph.getLastChild()); + } + @Test public void testInlineFootnote() { var extension = FootnotesExtension.builder().inlineFootnotes(true).build(); diff --git a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java index 44f153e00..c4d9fc656 100644 --- a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java +++ b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java @@ -440,16 +440,9 @@ private Node wrapBracket(Bracket opener, Node wrapperNode, boolean includeMarker opener.bracketNode.unlink(); removeLastBracket(); - // Links within links are not allowed. We found this link, so there can be no other link around it. + // Links within links are not allowed. We found this link, so there can be no other links around it. if (opener.markerNode == null) { - 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; - } + disallowPreviousLinks(); } return wrapperNode; @@ -475,6 +468,15 @@ private Node replaceBracket(Bracket opener, Node node, boolean includeMarker) { 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; } @@ -489,6 +491,17 @@ 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. */ From c126ad3ba97a90cd2a1eed4b75881745ac896bff Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sat, 13 Sep 2025 10:11:01 +1000 Subject: [PATCH 39/89] Add docs to ListItem and its appendChild about tight lists --- .../java/org/commonmark/node/ListItem.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/commonmark/src/main/java/org/commonmark/node/ListItem.java b/commonmark/src/main/java/org/commonmark/node/ListItem.java index e488e8fbe..c4d1214e7 100644 --- a/commonmark/src/main/java/org/commonmark/node/ListItem.java +++ b/commonmark/src/main/java/org/commonmark/node/ListItem.java @@ -2,6 +2,10 @@ /** * 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 */ @@ -57,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); + } } From 47e21c3237045bef6dd9dfda8691677fabf5e703 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sat, 13 Sep 2025 10:25:24 +1000 Subject: [PATCH 40/89] Prepare CHANGELOG for version 0.26.0 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f41d904a9..c184b9ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. -## Unreleased +## [0.26.0] - 2025-09-13 ### Changed - A `LinkProcessor` using `replaceWith` now also stops outer links from being parsed as links, same as with `wrapTextIn`. This prevents nested links, see @@ -492,6 +492,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. +[0.26.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.1...commonmark-parent-0.26.0 [0.25.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.0...commonmark-parent-0.25.1 [0.25.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...commonmark-parent-0.25.0 [0.24.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.23.0...commonmark-parent-0.24.0 From 729ba3fb04d28c856420f74aebf1b1e40df62fff Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sat, 13 Sep 2025 10:25:45 +1000 Subject: [PATCH 41/89] Prepare for version 0.26.0 Ran `mvn versions:set -DnewVersion=0.26.0-SNAPSHOT` --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 24 ++++++++++++------------ 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 360ad9781..1295112c3 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 31a92ca77..c49312a04 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index d051a0ee0..c94d3a3e0 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 740460ada..2d89b1ea1 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 1270cd1c6..7e812d002 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index f392c8ea4..ef495cf45 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 1b797791f..15f9609e1 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index f3e626f89..70dc878ab 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.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 93d5e3e2c..46eb4b695 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.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 810919ce6..7cb3bf2cb 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 753bb00de..32ead5f20 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 26edd4ffd..518f711bb 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index 53289614e..c322420ae 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-ins - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT org.commonmark commonmark-test-util - 0.25.2-SNAPSHOT + 0.26.0-SNAPSHOT From 6874637cc60a219165259b857a07a0cad28b223a Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:31:09 +0000 Subject: [PATCH 42/89] [maven-release-plugin] prepare release commonmark-parent-0.26.0 --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 1295112c3..810275c2c 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index c49312a04..af6e6b25c 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index c94d3a3e0..831d04666 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 2d89b1ea1..0ee142010 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 7e812d002..f4cf8ac9d 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index ef495cf45..61617e1d2 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 15f9609e1..baad9b751 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 70dc878ab..ab20cb22e 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.26.0-SNAPSHOT + 0.26.0 commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 46eb4b695..3fb85460c 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.26.0-SNAPSHOT + 0.26.0 commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 7cb3bf2cb..d264e0b3a 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 32ead5f20..344bbc447 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 518f711bb..38a1c58e4 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark diff --git a/pom.xml b/pom.xml index c322420ae..cb00d01bf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.26.0-SNAPSHOT + 0.26.0 commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-autolink - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-footnotes - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-image-attributes - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-ins - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-gfm-strikethrough - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-gfm-tables - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-heading-anchor - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-task-list-items - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-ext-yaml-front-matter - 0.26.0-SNAPSHOT + 0.26.0 org.commonmark commonmark-test-util - 0.26.0-SNAPSHOT + 0.26.0 @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - HEAD + commonmark-parent-0.26.0 From 868e006d03158c814bc51ef2c0cbb6cbe0f7e23c Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:31:10 +0000 Subject: [PATCH 43/89] [maven-release-plugin] prepare for next development iteration --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 810275c2c..5bb143122 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index af6e6b25c..4947e33b9 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 831d04666..37216c971 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 0ee142010..8acc77a5d 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index f4cf8ac9d..b0207beeb 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 61617e1d2..89efac64f 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index baad9b751..03be87d08 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index ab20cb22e..e772677c5 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.26.0 + 0.26.1-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 3fb85460c..b39f253ec 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.26.0 + 0.26.1-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index d264e0b3a..3ca1d58da 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 344bbc447..f7c405069 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 38a1c58e4..fb5ddb08c 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index cb00d01bf..926e275c9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.26.0 + 0.26.1-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-ins - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.26.0 + 0.26.1-SNAPSHOT org.commonmark commonmark-test-util - 0.26.0 + 0.26.1-SNAPSHOT @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - commonmark-parent-0.26.0 + HEAD From 44ebb2e07d71ff3a62ac876d40fe03d2c07b8fd6 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sat, 13 Sep 2025 10:41:07 +1000 Subject: [PATCH 44/89] README: Bump version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 62f5affe3..79c2a4923 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Coordinates for core library (see all on [Maven Central]): org.commonmark commonmark - 0.25.1 + 0.26.0 ``` @@ -291,7 +291,7 @@ First, add an additional dependency (see [Maven Central] for others): org.commonmark commonmark-ext-gfm-tables - 0.25.1 + 0.26.0 ``` From ddf81a0dd83e72631c434620a3005c999befa0e6 Mon Sep 17 00:00:00 2001 From: Sebastian Thomschke Date: Sat, 20 Sep 2025 00:39:22 +0200 Subject: [PATCH 45/89] README: Add Used By Eclipse Previewer Plugin --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 79c2a4923..c61c7bdc0 100644 --- a/README.md +++ b/README.md @@ -453,6 +453,7 @@ Some users of this library (feel free to raise a PR if you want to be added): * [Open Note](https://github.com/YangDai2003/OpenNote-Compose) a markdown editor and note-taking app for Android * [Quarkus Roq](https://github.com/quarkiverse/quarkus-roq/) The Roq Static Site Generator allows to easily create a static website or blog using Quarkus super-powers. * [Lucee](https://github.com/lucee/lucee) +* [Previewer](https://github.com/sebthom/previewer-eclipse-plugin) an extensible Eclipse plugin that previews Markdown and other text based formats. See also -------- From 3208a83f640680586c31087612406cfbfc12e4df Mon Sep 17 00:00:00 2001 From: rdestefa <360fanelite@gmail.com> Date: Fri, 3 Oct 2025 21:57:52 -0700 Subject: [PATCH 46/89] Allow Detection of WWW Autolinks --- .../ext/autolink/AutolinkExtension.java | 68 ++++++++++++++++++- .../commonmark/ext/autolink/AutolinkType.java | 16 +++++ .../internal/AutolinkPostProcessor.java | 44 ++++++++++-- .../commonmark/ext/autolink/AutolinkTest.java | 18 +++++ 4 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java index e5926c7bb..115676295 100644 --- a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java @@ -1,5 +1,8 @@ package org.commonmark.ext.autolink; +import java.util.EnumSet; +import java.util.Set; + import org.commonmark.Extension; import org.commonmark.ext.autolink.internal.AutolinkPostProcessor; import org.commonmark.parser.Parser; @@ -18,16 +21,75 @@ */ public class AutolinkExtension implements Parser.ParserExtension { - private AutolinkExtension() { + private final Set linkTypes; + + private AutolinkExtension(Builder builder) { + this.linkTypes = builder.linkTypes; } + /** + * @return the extension with default options + */ public static Extension create() { - return new AutolinkExtension(); + return builder().build(); + } + + /** + * @return a builder to configure the behavior of the extension. + */ + public static Builder builder() { + return new Builder(); } @Override public void extend(Parser.Builder parserBuilder) { - parserBuilder.postProcessor(new AutolinkPostProcessor()); + parserBuilder.postProcessor(new AutolinkPostProcessor(linkTypes)); } + public static class Builder { + + private Set linkTypes = EnumSet.of(AutolinkType.URL, AutolinkType.EMAIL); + + /** + * @param linkTypes the link types that should be converted. By default, {@link AutolinkType#URL} + * and {@link AutolinkType#EMAIL} are converted. + * @return {@code this} + */ + public Builder linkTypes(AutolinkType... linkTypes) { + if (linkTypes == null) { + throw new NullPointerException("linkTypes must not be null"); + } + + if (linkTypes.length == 0) { + throw new IllegalArgumentException("linkTypes must not be empty"); + } + + return this.linkTypes(Set.of(linkTypes)); + } + + /** + * @param linkTypes the link types that should be converted. By default, {@link AutolinkType#URL} + * and {@link AutolinkType#EMAIL} are converted. + * @return {@code this} + */ + public Builder linkTypes(Set linkTypes) { + if (linkTypes == null) { + throw new NullPointerException("linkTypes must not be null"); + } + + if (linkTypes.isEmpty()) { + throw new IllegalArgumentException("linkTypes must not be empty"); + } + + this.linkTypes = EnumSet.copyOf(linkTypes); + return this; + } + + /** + * @return a configured extension + */ + public Extension build() { + return new AutolinkExtension(this); + } + } } diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java new file mode 100644 index 000000000..3e397a42f --- /dev/null +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java @@ -0,0 +1,16 @@ +package org.commonmark.ext.autolink; + +public enum AutolinkType { + /** + * URL such as {@code http://example.com} + */ + URL, + /** + * Email address such as {@code foo@example.com} + */ + EMAIL, + /** + * URL such as {@code www.example.com} + */ + WWW +} diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java index ee8847911..33f70bf21 100644 --- a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java @@ -1,5 +1,6 @@ package org.commonmark.ext.autolink.internal; +import org.commonmark.ext.autolink.AutolinkType; import org.commonmark.node.*; import org.commonmark.parser.PostProcessor; import org.nibor.autolink.LinkExtractor; @@ -11,9 +12,40 @@ public class AutolinkPostProcessor implements PostProcessor { - private LinkExtractor linkExtractor = LinkExtractor.builder() - .linkTypes(EnumSet.of(LinkType.URL, LinkType.EMAIL)) - .build(); + private final LinkExtractor linkExtractor; + + public AutolinkPostProcessor() { + this(EnumSet.of(AutolinkType.URL, AutolinkType.EMAIL)); + } + + public AutolinkPostProcessor(Set linkTypes) { + if (linkTypes == null) { + throw new NullPointerException("linkTypes must not be null"); + } + + if (linkTypes.isEmpty()) { + throw new IllegalArgumentException("linkTypes must not be empty"); + } + + EnumSet types = EnumSet.noneOf(LinkType.class); + for (AutolinkType linkType : linkTypes) { + switch (linkType) { + case URL: + types.add(LinkType.URL); + break; + case EMAIL: + types.add(LinkType.EMAIL); + break; + case WWW: + types.add(LinkType.WWW); + break; + } + } + + this.linkExtractor = LinkExtractor.builder() + .linkTypes(types) + .build(); + } @Override public Node process(Node node) { @@ -67,8 +99,12 @@ private static Text createTextNode(String literal, Span span, SourceSpan sourceS } private static String getDestination(LinkSpan linkSpan, String linkText) { - if (linkSpan.getType() == LinkType.EMAIL) { + LinkType type = linkSpan.getType(); + + if (type == LinkType.EMAIL) { return "mailto:" + linkText; + } else if (type == LinkType.WWW) { + return "http://" + linkText; } else { return linkText; } diff --git a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java index 338513f33..81d898a31 100644 --- a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java +++ b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java @@ -19,6 +19,12 @@ public class AutolinkTest extends RenderingTestCase { private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build(); private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build(); + private static final Set WWW_EXTENSIONS = Set.of(AutolinkExtension.builder() + .linkTypes(AutolinkType.URL, AutolinkType.EMAIL, AutolinkType.WWW) + .build()); + private static final Parser WWW_PARSER = Parser.builder().extensions(WWW_EXTENSIONS).build(); + private static final HtmlRenderer WWW_RENDERER = HtmlRenderer.builder().extensions(WWW_EXTENSIONS).build(); + @Test public void oneTextNode() { assertRendering("foo http://one.org/ bar http://two.org/", @@ -57,6 +63,18 @@ public void dontLinkTextWithinLinks() { "

http://example.com

\n"); } + @Test + public void wwwLinksDontWorkByDefault() { + assertRendering("www.example.com", + "

www.example.com

\n"); + } + + @Test + public void wwwLinks() { + String html = WWW_RENDERER.render(WWW_PARSER.parse("www.example.com")); + assertThat(html).isEqualTo("

www.example.com

\n"); + } + @Test public void sourceSpans() { Parser parser = Parser.builder() From 675d2c45805a1089ea3f1bd78f0d3864fecfb78f Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Mon, 6 Oct 2025 01:55:58 -0700 Subject: [PATCH 47/89] Address PR Comments --- .../org/commonmark/ext/autolink/AutolinkExtension.java | 4 ---- .../java/org/commonmark/ext/autolink/AutolinkType.java | 3 +++ .../ext/autolink/internal/AutolinkPostProcessor.java | 10 +++------- .../java/org/commonmark/ext/autolink/AutolinkTest.java | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java index 115676295..99c4f6d40 100644 --- a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java @@ -60,10 +60,6 @@ public Builder linkTypes(AutolinkType... linkTypes) { throw new NullPointerException("linkTypes must not be null"); } - if (linkTypes.length == 0) { - throw new IllegalArgumentException("linkTypes must not be empty"); - } - return this.linkTypes(Set.of(linkTypes)); } diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java index 3e397a42f..2c8c6574f 100644 --- a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkType.java @@ -1,5 +1,8 @@ package org.commonmark.ext.autolink; +/** + * The types of strings that can be automatically turned into links. + */ public enum AutolinkType { /** * URL such as {@code http://example.com} diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java index 33f70bf21..4ce070fd8 100644 --- a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java @@ -14,10 +14,6 @@ public class AutolinkPostProcessor implements PostProcessor { private final LinkExtractor linkExtractor; - public AutolinkPostProcessor() { - this(EnumSet.of(AutolinkType.URL, AutolinkType.EMAIL)); - } - public AutolinkPostProcessor(Set linkTypes) { if (linkTypes == null) { throw new NullPointerException("linkTypes must not be null"); @@ -27,7 +23,7 @@ public AutolinkPostProcessor(Set linkTypes) { throw new IllegalArgumentException("linkTypes must not be empty"); } - EnumSet types = EnumSet.noneOf(LinkType.class); + var types = EnumSet.noneOf(LinkType.class); for (AutolinkType linkType : linkTypes) { switch (linkType) { case URL: @@ -99,12 +95,12 @@ private static Text createTextNode(String literal, Span span, SourceSpan sourceS } private static String getDestination(LinkSpan linkSpan, String linkText) { - LinkType type = linkSpan.getType(); + var type = linkSpan.getType(); if (type == LinkType.EMAIL) { return "mailto:" + linkText; } else if (type == LinkType.WWW) { - return "http://" + linkText; + return "https://" + linkText; } else { return linkText; } diff --git a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java index 81d898a31..ada6c1580 100644 --- a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java +++ b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java @@ -72,7 +72,7 @@ public void wwwLinksDontWorkByDefault() { @Test public void wwwLinks() { String html = WWW_RENDERER.render(WWW_PARSER.parse("www.example.com")); - assertThat(html).isEqualTo("

www.example.com

\n"); + assertThat(html).isEqualTo("

www.example.com

\n"); } @Test From 4186529fe603de45fd68003aef0f78354f09879a Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:13:32 -0700 Subject: [PATCH 48/89] Add Changelog Entry --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c184b9ba6..5de44520a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. +## Unreleased +### Added +- Autolink extension: Now supports configuration of different link types that + should be recognized and converted to links. See `AutolinkExtension#builder` + | Type | Default? | Description | + | `URL` | Yes | URL with a protocol such as `https://example.com` | + | `EMAIL` | Yes | Email address such as `foo@example.com` | + | `WWW` | No | An address beginning with `www` such as `www.example.com` | + ## [0.26.0] - 2025-09-13 ### Changed - A `LinkProcessor` using `replaceWith` now also stops outer links from being From 64f76d9727682af819c9492a636040e8fcb6a4aa Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:15:12 -0700 Subject: [PATCH 49/89] Fix Changelog Typo --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5de44520a..bed5058b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ with the exception that 0.x versions can break between minor versions. - Autolink extension: Now supports configuration of different link types that should be recognized and converted to links. See `AutolinkExtension#builder` | Type | Default? | Description | + |---------|----------|-----------------------------------------------------------| | `URL` | Yes | URL with a protocol such as `https://example.com` | | `EMAIL` | Yes | Email address such as `foo@example.com` | | `WWW` | No | An address beginning with `www` such as `www.example.com` | From e49094cf8e0912b79737e6fed66a8e245b8285e0 Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Mon, 6 Oct 2025 22:10:13 -0700 Subject: [PATCH 50/89] Address PR Comments 2 --- CHANGELOG.md | 20 ++++++++++++++----- .../ext/autolink/AutolinkExtension.java | 10 +++++----- .../internal/AutolinkPostProcessor.java | 3 ++- .../commonmark/ext/autolink/AutolinkTest.java | 18 ++++++++--------- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bed5058b3..74989fe2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,21 @@ with the exception that 0.x versions can break between minor versions. ### Added - Autolink extension: Now supports configuration of different link types that should be recognized and converted to links. See `AutolinkExtension#builder` - | Type | Default? | Description | - |---------|----------|-----------------------------------------------------------| - | `URL` | Yes | URL with a protocol such as `https://example.com` | - | `EMAIL` | Yes | Email address such as `foo@example.com` | - | `WWW` | No | An address beginning with `www` such as `www.example.com` | + + | Type | Default? | Description | + |---------|----------|--------------------------------------------------------| + | `URL` | Yes | URL with a protocol such as `https://example.com` | + | `EMAIL` | Yes | Email address such as `foo@example.com` | + | `WWW` | Yes | Address beginning with `www` such as `www.example.com` | + + > [!NOTE] + > + > This changes the behavior of `AutolinkExtension.create()` to now also include + > `WWW` links by default. To re-enable the previous behavior, use: + > + > ```java + > AutolinkExtension.builder().linkTypes(AutolinkType.URL, AutolinkType.EMAIL).build(); + > ``` ## [0.26.0] - 2025-09-13 ### Changed diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java index 99c4f6d40..7d5a74f30 100644 --- a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java @@ -48,11 +48,11 @@ public void extend(Parser.Builder parserBuilder) { public static class Builder { - private Set linkTypes = EnumSet.of(AutolinkType.URL, AutolinkType.EMAIL); + private Set linkTypes = EnumSet.allOf(AutolinkType.class); /** - * @param linkTypes the link types that should be converted. By default, {@link AutolinkType#URL} - * and {@link AutolinkType#EMAIL} are converted. + * @param linkTypes the link types that should be converted. By default, + * all {@link AutolinkType}s are converted. * @return {@code this} */ public Builder linkTypes(AutolinkType... linkTypes) { @@ -64,8 +64,8 @@ public Builder linkTypes(AutolinkType... linkTypes) { } /** - * @param linkTypes the link types that should be converted. By default, {@link AutolinkType#URL} - * and {@link AutolinkType#EMAIL} are converted. + * @param linkTypes the link types that should be converted. By default, + * all {@link AutolinkType}s are converted. * @return {@code this} */ public Builder linkTypes(Set linkTypes) { diff --git a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java index 4ce070fd8..a381c2f19 100644 --- a/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java +++ b/commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java @@ -100,7 +100,8 @@ private static String getDestination(LinkSpan linkSpan, String linkText) { if (type == LinkType.EMAIL) { return "mailto:" + linkText; } else if (type == LinkType.WWW) { - return "https://" + linkText; + // Use http instead of https (see https://github.github.com/gfm/#extended-www-autolink) + return "http://" + linkText; } else { return linkText; } diff --git a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java index ada6c1580..1b93cb10c 100644 --- a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java +++ b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java @@ -19,11 +19,11 @@ public class AutolinkTest extends RenderingTestCase { private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build(); private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build(); - private static final Set WWW_EXTENSIONS = Set.of(AutolinkExtension.builder() - .linkTypes(AutolinkType.URL, AutolinkType.EMAIL, AutolinkType.WWW) + private static final Set NO_WWW_EXTENSIONS = Set.of(AutolinkExtension.builder() + .linkTypes(AutolinkType.URL, AutolinkType.EMAIL) .build()); - private static final Parser WWW_PARSER = Parser.builder().extensions(WWW_EXTENSIONS).build(); - private static final HtmlRenderer WWW_RENDERER = HtmlRenderer.builder().extensions(WWW_EXTENSIONS).build(); + private static final Parser NO_WWW_PARSER = Parser.builder().extensions(NO_WWW_EXTENSIONS).build(); + private static final HtmlRenderer NO_WWW_RENDERER = HtmlRenderer.builder().extensions(NO_WWW_EXTENSIONS).build(); @Test public void oneTextNode() { @@ -64,15 +64,15 @@ public void dontLinkTextWithinLinks() { } @Test - public void wwwLinksDontWorkByDefault() { + public void wwwLinks() { assertRendering("www.example.com", - "

www.example.com

\n"); + "

www.example.com

\n"); } @Test - public void wwwLinks() { - String html = WWW_RENDERER.render(WWW_PARSER.parse("www.example.com")); - assertThat(html).isEqualTo("

www.example.com

\n"); + public void noWwwLinks() { + String html = NO_WWW_RENDERER.render(NO_WWW_PARSER.parse("www.example.com")); + assertThat(html).isEqualTo("

www.example.com

\n"); } @Test From 53bf47d5cf6e7854dee03af658b1f7960c585dcf Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Mon, 6 Oct 2025 22:17:58 -0700 Subject: [PATCH 51/89] Fix Test Failure --- CHANGELOG.md | 14 ++++++-------- .../org/commonmark/ext/autolink/AutolinkTest.java | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74989fe2b..40945d940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,15 +16,13 @@ with the exception that 0.x versions can break between minor versions. | `URL` | Yes | URL with a protocol such as `https://example.com` | | `EMAIL` | Yes | Email address such as `foo@example.com` | | `WWW` | Yes | Address beginning with `www` such as `www.example.com` | + + Note that This changes the behavior of `AutolinkExtension.create()` to now also + include `WWW` links by default. To re-enable the previous behavior, use: - > [!NOTE] - > - > This changes the behavior of `AutolinkExtension.create()` to now also include - > `WWW` links by default. To re-enable the previous behavior, use: - > - > ```java - > AutolinkExtension.builder().linkTypes(AutolinkType.URL, AutolinkType.EMAIL).build(); - > ``` + ```java + AutolinkExtension.builder().linkTypes(AutolinkType.URL, AutolinkType.EMAIL).build(); + ``` ## [0.26.0] - 2025-09-13 ### Changed diff --git a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java index 1b93cb10c..82c3899fc 100644 --- a/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java +++ b/commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java @@ -66,7 +66,7 @@ public void dontLinkTextWithinLinks() { @Test public void wwwLinks() { assertRendering("www.example.com", - "

www.example.com

\n"); + "

www.example.com

\n"); } @Test From 889709c57578369685f4ece88917c1ffa8df44c2 Mon Sep 17 00:00:00 2001 From: Ryan DeStefano <67760716+rdestefa@users.noreply.github.com> Date: Mon, 6 Oct 2025 22:20:40 -0700 Subject: [PATCH 52/89] Typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40945d940..9cb1bae6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ with the exception that 0.x versions can break between minor versions. | `EMAIL` | Yes | Email address such as `foo@example.com` | | `WWW` | Yes | Address beginning with `www` such as `www.example.com` | - Note that This changes the behavior of `AutolinkExtension.create()` to now also + Note that this changes the behavior of `AutolinkExtension.create()` to now also include `WWW` links by default. To re-enable the previous behavior, use: ```java From 76b2168d9b8ad2b186cb05e1d985a37629b32318 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 12 Oct 2025 20:59:44 +1100 Subject: [PATCH 53/89] Prepare CHANGELOG for version 0.27.0 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cb1bae6b..31499759a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. -## Unreleased +## [0.27.0] - 2025-10-12 ### Added - Autolink extension: Now supports configuration of different link types that should be recognized and converted to links. See `AutolinkExtension#builder` @@ -510,6 +510,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. +[0.27.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.26.0...commonmark-parent-0.27.0 [0.26.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.1...commonmark-parent-0.26.0 [0.25.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.0...commonmark-parent-0.25.1 [0.25.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...commonmark-parent-0.25.0 From c55b0ecf5655d7265dfc60dbf454d1d78282b84a Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 12 Oct 2025 21:00:33 +1100 Subject: [PATCH 54/89] Prepare for version 0.27.0 Ran `mvn versions:set -DnewVersion=0.27.0-SNAPSHOT` --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 24 ++++++++++++------------ 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 5bb143122..4dd71fb86 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 4947e33b9..f6c7a654b 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 37216c971..78803d540 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 8acc77a5d..c8c54bd5b 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index b0207beeb..832a1a3f3 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 89efac64f..190daf471 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 03be87d08..6ed456a8b 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index e772677c5..4e5181ac2 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.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index b39f253ec..8341ee316 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.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 3ca1d58da..38573a3ee 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index f7c405069..8366b07b9 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index fb5ddb08c..cf242faf5 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index 926e275c9..235d1ab01 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-ins - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT org.commonmark commonmark-test-util - 0.26.1-SNAPSHOT + 0.27.0-SNAPSHOT From de91f05a96061f5afe5b2c107610e27c1e9c2703 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 12 Oct 2025 21:03:24 +1100 Subject: [PATCH 55/89] Test on Java 25 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e94dabbba..b32794271 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [11, 17, 21, 24] + java: [11, 17, 21, 25] steps: - name: Checkout sources uses: actions/checkout@v4 From 70f6819d263a506ea20f4d0fb18631ca21bab2e6 Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Sun, 12 Oct 2025 10:12:46 +0000 Subject: [PATCH 56/89] [maven-release-plugin] prepare release commonmark-parent-0.27.0 --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 4dd71fb86..7bbfbf99a 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index f6c7a654b..6aa86a6f8 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 78803d540..d8d8c3397 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index c8c54bd5b..98dfac272 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 832a1a3f3..a3883ed73 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 190daf471..7f7ce5002 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 6ed456a8b..2e8790c4a 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 4e5181ac2..323723d59 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.27.0-SNAPSHOT + 0.27.0 commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 8341ee316..e08948c6c 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.27.0-SNAPSHOT + 0.27.0 commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 38573a3ee..18d9f0394 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 8366b07b9..e0e0e97f8 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index cf242faf5..2528b4074 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark diff --git a/pom.xml b/pom.xml index 235d1ab01..9ce4f2b28 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.27.0-SNAPSHOT + 0.27.0 commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-autolink - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-footnotes - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-image-attributes - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-ins - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-gfm-strikethrough - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-gfm-tables - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-heading-anchor - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-task-list-items - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-ext-yaml-front-matter - 0.27.0-SNAPSHOT + 0.27.0 org.commonmark commonmark-test-util - 0.27.0-SNAPSHOT + 0.27.0 @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - HEAD + commonmark-parent-0.27.0 From f4f327084d298145b714c1cef6d1ccaf355f6ab8 Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Sun, 12 Oct 2025 10:12:48 +0000 Subject: [PATCH 57/89] [maven-release-plugin] prepare for next development iteration --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 7bbfbf99a..5b6989097 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 6aa86a6f8..09d962e41 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index d8d8c3397..77bea3f36 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 98dfac272..00dd10420 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index a3883ed73..f3ede6535 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 7f7ce5002..384bd785f 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 2e8790c4a..d9789dfe6 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 323723d59..bbedcf976 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.27.0 + 0.27.1-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index e08948c6c..b4aea9395 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.27.0 + 0.27.1-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 18d9f0394..0f9f3b73b 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index e0e0e97f8..016bd600b 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 2528b4074..e5983e81a 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index 9ce4f2b28..7818b0ba4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.27.0 + 0.27.1-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-ins - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.27.0 + 0.27.1-SNAPSHOT org.commonmark commonmark-test-util - 0.27.0 + 0.27.1-SNAPSHOT @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - commonmark-parent-0.27.0 + HEAD From 5fa3b86044b577cd826a3bc2c89a2a644f23061c Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 12 Oct 2025 21:27:24 +1100 Subject: [PATCH 58/89] README: Bump version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c61c7bdc0..2e5aea22c 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Coordinates for core library (see all on [Maven Central]): org.commonmark commonmark - 0.26.0 + 0.27.0 ``` @@ -291,7 +291,7 @@ First, add an additional dependency (see [Maven Central] for others): org.commonmark commonmark-ext-gfm-tables - 0.26.0 + 0.27.0 ``` From 7c0d35a5e175d2d4afad9c3c783a7cb141cbf13f Mon Sep 17 00:00:00 2001 From: David Gerber Date: Sun, 12 Oct 2025 22:14:53 +0200 Subject: [PATCH 59/89] Add library user to the README Xeres is a Peer-to-Peer application that uses commonmark-java --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e5aea22c..702cc3abc 100644 --- a/README.md +++ b/README.md @@ -453,7 +453,8 @@ Some users of this library (feel free to raise a PR if you want to be added): * [Open Note](https://github.com/YangDai2003/OpenNote-Compose) a markdown editor and note-taking app for Android * [Quarkus Roq](https://github.com/quarkiverse/quarkus-roq/) The Roq Static Site Generator allows to easily create a static website or blog using Quarkus super-powers. * [Lucee](https://github.com/lucee/lucee) -* [Previewer](https://github.com/sebthom/previewer-eclipse-plugin) an extensible Eclipse plugin that previews Markdown and other text based formats. +* [Previewer](https://github.com/sebthom/previewer-eclipse-plugin) an extensible Eclipse plugin that previews Markdown and other text based formats. +* [Xeres](https://xeres.io) a Peer-to-Peer application where all user generated content is done with markdown See also -------- From fd334350cb1d32fe745f50ca17c6096642ccbab2 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 13 Jan 2026 17:26:18 +0100 Subject: [PATCH 60/89] Fix line break parsing after hard line break Fixes #415. --- CHANGELOG.md | 6 ++++++ .../org/commonmark/internal/InlineParserImpl.java | 4 +++- .../commonmark/test/CoreRenderingTestCase.java | 3 ++- .../org/commonmark/test/SpecialInputTest.java | 15 +++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31499759a..e194b9b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. +## [Unreleased] +### Fixed +- Line(s) after a hard line break would sometimes also get an unwanted hard + line break, e.g. if they ended in emphasis or other non-text inlines (#415) + ## [0.27.0] - 2025-10-12 ### Added - Autolink extension: Now supports configuration of different link types that @@ -510,6 +515,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. +[Unreleased]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.0...main [0.27.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.26.0...commonmark-parent-0.27.0 [0.26.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.1...commonmark-parent-0.26.0 [0.25.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.0...commonmark-parent-0.25.1 diff --git a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java index c4d9fc656..fef30c0f7 100644 --- a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java +++ b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java @@ -598,7 +598,9 @@ static 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(); diff --git a/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java b/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java index 38f319e1c..2303d2617 100644 --- a/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java +++ b/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java @@ -11,6 +11,7 @@ public class CoreRenderingTestCase extends RenderingTestCase { @Override protected String render(String source) { - return RENDERER.render(PARSER.parse(source)); + var node = PARSER.parse(source); + return RENDERER.render(node); } } diff --git a/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java b/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java index 2ebac1711..45cd3aea2 100644 --- a/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java +++ b/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java @@ -210,4 +210,19 @@ public void htmlBlockInterruptingList() { "\n" + "\n"); } + + @Test + public void emphasisAfterHardLineBreak() { + assertRendering("Hello \n" + + "**Bar**\n" + + "Foo\n", "

Hello
\n" + + "Bar\n" + + "Foo

\n"); + + assertRendering("Hello \n" + + "**Bar** \n" + + "Foo\n", "

Hello
\n" + + "Bar
\n" + + "Foo

\n"); + } } From 2753bf2b36f5048be2125d5a5bd2ef71ee35a79b Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Wed, 14 Jan 2026 14:21:56 +0100 Subject: [PATCH 61/89] TextContentRenderer: Fix nested lists on the same line --- CHANGELOG.md | 1 + .../internal/renderer/text/ListHolder.java | 14 ------ .../text/CoreTextContentNodeRenderer.java | 28 ++++++++++-- .../renderer/text/TextContentWriter.java | 44 ++++++++++++++++++- .../test/TextContentRendererTest.java | 23 +++++++--- 5 files changed, 85 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e194b9b74..d091306ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ with the exception that 0.x versions can break between minor versions. ### Fixed - Line(s) after a hard line break would sometimes also get an unwanted hard line break, e.g. if they ended in emphasis or other non-text inlines (#415) +- `TextContentRenderer` (for plain text): Fix nested lists on the same line (#413) ## [0.27.0] - 2025-10-12 ### Added 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 index cb06d4a9d..14ca5b9af 100644 --- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java +++ b/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java @@ -1,27 +1,13 @@ 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/renderer/text/CoreTextContentNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java index 68b1fbce5..2a27571c6 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java @@ -161,19 +161,30 @@ public void visit(Link link) { @Override public void visit(ListItem listItem) { if (listHolder != null && listHolder instanceof OrderedListHolder) { - OrderedListHolder orderedListHolder = (OrderedListHolder) listHolder; - String indent = stripNewlines() ? "" : orderedListHolder.getIndent(); - textContent.write(indent + orderedListHolder.getCounter() + orderedListHolder.getDelimiter() + " "); + var orderedListHolder = (OrderedListHolder) listHolder; + var marker = orderedListHolder.getCounter() + orderedListHolder.getDelimiter(); + var spaces = " "; + textContent.write(marker); + textContent.write(spaces); + textContent.pushPrefix(repeat(" ", marker.length() + spaces.length())); visitChildren(listItem); textContent.block(); + textContent.popPrefix(); orderedListHolder.increaseCounter(); } else if (listHolder != null && listHolder instanceof BulletListHolder) { BulletListHolder bulletListHolder = (BulletListHolder) listHolder; if (!stripNewlines()) { - textContent.write(bulletListHolder.getIndent() + bulletListHolder.getMarker() + " "); + var marker = bulletListHolder.getMarker(); + var spaces = " "; + textContent.write(marker); + textContent.write(spaces); + textContent.pushPrefix(repeat(" ", marker.length() + spaces.length())); } visitChildren(listItem); textContent.block(); + if (!stripNewlines()) { + textContent.popPrefix(); + } } } @@ -268,4 +279,13 @@ private static String stripTrailingNewline(String s) { return s; } } + + // Keep for Android compat (String.repeat only available on Android 12 and later) + private static String repeat(String s, int count) { + var sb = new StringBuilder(s.length() * count); + for (int i = 0; i < count; i++) { + sb.append(s); + } + return sb.toString(); + } } diff --git a/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java b/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java index 2b9f35070..1fb482785 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java +++ b/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java @@ -8,6 +8,7 @@ public class TextContentWriter { private final Appendable buffer; private final LineBreakRendering lineBreakRendering; + private final LinkedList prefixes = new LinkedList<>(); private final LinkedList tight = new LinkedList<>(); private String blockSeparator = null; @@ -36,6 +37,7 @@ public void colon() { public void line() { append('\n'); + writePrefixes(); } public void block() { @@ -61,6 +63,32 @@ public void write(char c) { append(c); } + /** + * Push a prefix onto the top of the stack. All prefixes are written at the beginning of each line, until the + * prefix is popped again. + * + * @param prefix the raw prefix string + */ + public void pushPrefix(String prefix) { + prefixes.addLast(prefix); + } + + /** + * Write a prefix. + * + * @param prefix the raw prefix string to write + */ + public void writePrefix(String prefix) { + write(prefix); + } + + /** + * Remove the last prefix from the top of the stack. + */ + public void popPrefix() { + prefixes.removeLast(); + } + /** * Change whether blocks are tight or loose. Loose is the default where blocks are separated by a blank line. Tight * is where blocks are not separated by a blank line. Tight blocks are used in lists, if there are no blank lines @@ -84,12 +112,26 @@ private boolean isTight() { return !tight.isEmpty() && tight.getLast(); } + private void writePrefixes() { + for (String prefix : prefixes) { + append(prefix); + } + } + /** * If a block separator has been enqueued with {@link #block()} but not yet written, write it now. */ private void flushBlockSeparator() { if (blockSeparator != null) { - append(blockSeparator); + if (blockSeparator.equals("\n") || blockSeparator.equals("\n\n")) { + for (int i = 0; i < blockSeparator.length(); i++) { + var sep = blockSeparator.charAt(i); + append(sep); + writePrefixes(); + } + } else { + append(blockSeparator); + } blockSeparator = null; } } diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java index bc443e0e2..46757e0c3 100644 --- a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java @@ -116,14 +116,14 @@ public void textContentLists() { assertSeparate(s, "bar\n\n1. foo\n 1. bar\n2. foo"); assertStripped(s, "bar 1. foo 1. bar 2. foo"); - s = "bar\n* foo\n - bar\n* foo"; - assertCompact(s, "bar\n* foo\n - bar\n* foo"); - assertSeparate(s, "bar\n\n* foo\n - bar\n* foo"); + s = "bar\n* foo\n - bar\n* foo"; + assertCompact(s, "bar\n* foo\n - bar\n* foo"); + assertSeparate(s, "bar\n\n* foo\n - bar\n* foo"); assertStripped(s, "bar foo bar foo"); - s = "bar\n* foo\n 1. bar\n 2. bar\n* foo"; - assertCompact(s, "bar\n* foo\n 1. bar\n 2. bar\n* foo"); - assertSeparate(s, "bar\n\n* foo\n 1. bar\n 2. bar\n* foo"); + s = "bar\n* foo\n 1. bar\n 2. bar\n* foo"; + assertCompact(s, "bar\n* foo\n 1. bar\n 2. bar\n* foo"); + assertSeparate(s, "bar\n\n* foo\n 1. bar\n 2. bar\n* foo"); assertStripped(s, "bar foo 1. bar 2. bar foo"); s = "bar\n1. foo\n * bar\n * bar\n2. foo"; @@ -196,6 +196,17 @@ public void textContentHtml() { assertAll(html, html); } + @Test + public void testContentNestedLists() { + var s = "List:\n" + + "1. 2) 3. \n" + + "end"; + assertCompact(s, s); + + var s2 = "1. A\n 1) B\n 1. Test"; + assertCompact(s2, s2); + } + @Test public void testOverrideNodeRendering() { var nodeRendererFactory = new TextContentNodeRendererFactory() { From 67f8405f4e92086321af75c7bf7718ef2362dc1a Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Wed, 14 Jan 2026 14:26:28 +0100 Subject: [PATCH 62/89] Make list holder classes innner classes --- .../renderer/text/BulletListHolder.java | 16 ------ .../internal/renderer/text/ListHolder.java | 13 ----- .../renderer/text/OrderedListHolder.java | 26 ---------- .../text/CoreTextContentNodeRenderer.java | 51 +++++++++++++++++-- 4 files changed, 48 insertions(+), 58 deletions(-) delete mode 100644 commonmark/src/main/java/org/commonmark/internal/renderer/text/BulletListHolder.java delete mode 100644 commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java delete mode 100644 commonmark/src/main/java/org/commonmark/internal/renderer/text/OrderedListHolder.java 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 14ca5b9af..000000000 --- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.commonmark.internal.renderer.text; - -public abstract class ListHolder { - private final ListHolder parent; - - ListHolder(ListHolder parent) { - this.parent = parent; - } - - public ListHolder getParent() { - return parent; - } -} 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/renderer/text/CoreTextContentNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java index 2a27571c6..ee564cbdb 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java @@ -1,8 +1,5 @@ package org.commonmark.renderer.text; -import org.commonmark.internal.renderer.text.BulletListHolder; -import org.commonmark.internal.renderer.text.ListHolder; -import org.commonmark.internal.renderer.text.OrderedListHolder; import org.commonmark.node.*; import org.commonmark.renderer.NodeRenderer; @@ -288,4 +285,52 @@ private static String repeat(String s, int count) { } return sb.toString(); } + + private static class BulletListHolder extends ListHolder { + private final String marker; + + public BulletListHolder(ListHolder parent, BulletList list) { + super(parent); + marker = list.getMarker(); + } + + public String getMarker() { + return marker; + } + } + + private abstract static class ListHolder { + private final ListHolder parent; + + ListHolder(ListHolder parent) { + this.parent = parent; + } + + public ListHolder getParent() { + return parent; + } + } + + private static 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++; + } + } } From c946bfe1ac61697a6085315de45c96e2f22b2893 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Wed, 14 Jan 2026 14:42:14 +0100 Subject: [PATCH 63/89] Make nested brackets optimization more effective again --- .../java/org/commonmark/internal/InlineParserImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java index fef30c0f7..44422f421 100644 --- a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java +++ b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java @@ -383,14 +383,13 @@ private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) { // - Collapsed: `[foo][]` (foo is both the text and label) // - Shortcut: `[foo]` (foo is both the text and label) - String text = scanner.getSource(opener.contentPosition, beforeClose).getContent(); - // Starting position is after the closing `]` - Position afterClose = scanner.position(); + 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 `]`. @@ -401,7 +400,7 @@ private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) { // failed to be parsed as an inline link/image before. // See if there's a link label like `[bar]` or `[]` - String label = parseLinkLabel(scanner); + var label = parseLinkLabel(scanner); if (label == null) { // No label, rewind back scanner.setPosition(afterClose); @@ -413,6 +412,7 @@ private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) { return null; } + var text = scanner.getSource(opener.contentPosition, beforeClose).getContent(); return new LinkInfoImpl(opener.markerNode, opener.bracketNode, text, label, null, null, afterClose); } From eb7bac0c12a65c77fb5988cb36e3370fc2b05def Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Wed, 14 Jan 2026 14:48:54 +0100 Subject: [PATCH 64/89] Prepare CHANGELOG for version 0.27.1 --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d091306ae..b973b700f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. -## [Unreleased] +## [0.27.1] - 2026-01-14 ### Fixed - Line(s) after a hard line break would sometimes also get an unwanted hard line break, e.g. if they ended in emphasis or other non-text inlines (#415) - `TextContentRenderer` (for plain text): Fix nested lists on the same line (#413) +- Fix minor performance regression with pathological input (deeply nested + brackets) that was introduced in version 0.23.0. ## [0.27.0] - 2025-10-12 ### Added @@ -516,7 +518,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. -[Unreleased]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.0...main +[0.27.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.0...commonmark-parent-0.27.1 [0.27.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.26.0...commonmark-parent-0.27.0 [0.26.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.1...commonmark-parent-0.26.0 [0.25.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.0...commonmark-parent-0.25.1 From cded1b17fd6c557e7bd162b8cbe46e3e21f2adfe Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:55:01 +0000 Subject: [PATCH 65/89] [maven-release-plugin] prepare release commonmark-parent-0.27.1 --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 5b6989097..9bc18a017 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 09d962e41..1f3ac5bed 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 77bea3f36..1f82a3c60 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 00dd10420..575e83046 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index f3ede6535..3bc414ba8 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 384bd785f..347fef760 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index d9789dfe6..a3e95a71d 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index bbedcf976..2afe91d63 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.27.1-SNAPSHOT + 0.27.1 commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index b4aea9395..4d202c514 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.27.1-SNAPSHOT + 0.27.1 commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 0f9f3b73b..52e62c511 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 016bd600b..63a1a504b 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index e5983e81a..2aeafff1f 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark diff --git a/pom.xml b/pom.xml index 7818b0ba4..fea84fd5d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.27.1-SNAPSHOT + 0.27.1 commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-autolink - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-footnotes - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-image-attributes - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-ins - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-gfm-strikethrough - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-gfm-tables - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-heading-anchor - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-task-list-items - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-ext-yaml-front-matter - 0.27.1-SNAPSHOT + 0.27.1 org.commonmark commonmark-test-util - 0.27.1-SNAPSHOT + 0.27.1 @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - HEAD + commonmark-parent-0.27.1 From 502da7839116625c3ed5454decd20b5b084388d6 Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:55:02 +0000 Subject: [PATCH 66/89] [maven-release-plugin] prepare for next development iteration --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 9bc18a017..a99fd0b8c 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 1f3ac5bed..8bb88d74a 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 1f82a3c60..f6cedc69a 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 575e83046..4e94f623c 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 3bc414ba8..707948a10 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 347fef760..456ac04a2 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index a3e95a71d..a06c27b29 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 2afe91d63..0fc164672 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.27.1 + 0.27.2-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 4d202c514..860cfbac7 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.27.1 + 0.27.2-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 52e62c511..3433b42d4 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 63a1a504b..699adb3de 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 2aeafff1f..76dad6c83 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index fea84fd5d..b3a8e2b9f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.27.1 + 0.27.2-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -132,57 +132,57 @@ org.commonmark commonmark - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-ins - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.27.1 + 0.27.2-SNAPSHOT org.commonmark commonmark-test-util - 0.27.1 + 0.27.2-SNAPSHOT @@ -309,7 +309,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - commonmark-parent-0.27.1 + HEAD From 23f3db80c53bbb9b7d67ab7748350c5bdf2364d6 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Wed, 14 Jan 2026 15:04:28 +0100 Subject: [PATCH 67/89] README: Bump version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 702cc3abc..a917167f6 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Coordinates for core library (see all on [Maven Central]): org.commonmark commonmark - 0.27.0 + 0.27.1 ``` @@ -291,7 +291,7 @@ First, add an additional dependency (see [Maven Central] for others): org.commonmark commonmark-ext-gfm-tables - 0.27.0 + 0.27.1 ``` From af45936db125ed9e29b2229551c41425e8bd8162 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:55:37 +0000 Subject: [PATCH 68/89] Bump org.assertj:assertj-core from 3.27.3 to 3.27.7 Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.27.3 to 3.27.7. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.27.3...assertj-build-3.27.7) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.7 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b3a8e2b9f..d55d1fd85 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ org.assertj assertj-core - 3.27.3 + 3.27.7 org.openjdk.jmh From 057f13dd8e9472a45c714ad0c06a1a425ec21f61 Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Tue, 3 Mar 2026 17:02:16 +0100 Subject: [PATCH 69/89] Add GFM alerts extension Co-Authored-By: Claude Opus 4.6 --- commonmark-ext-gfm-alerts/README.md | 70 +++ commonmark-ext-gfm-alerts/pom.xml | 27 + .../src/main/java/module-info.java | 5 + .../org/commonmark/ext/gfm/alerts/Alert.java | 19 + .../ext/gfm/alerts/AlertsExtension.java | 117 +++++ .../internal/AlertHtmlNodeRenderer.java | 77 +++ .../internal/AlertMarkdownNodeRenderer.java | 38 ++ .../alerts/internal/AlertNodeRenderer.java | 22 + .../alerts/internal/AlertPostProcessor.java | 170 ++++++ .../src/main/javadoc/overview.html | 6 + .../alerts/AlertsMarkdownRendererTest.java | 73 +++ .../ext/gfm/alerts/AlertsSpecTest.java | 43 ++ .../commonmark/ext/gfm/alerts/AlertsTest.java | 140 +++++ .../gfm/alerts/examples/AlertsExample.java | 94 ++++ .../src/test/resources/alerts-spec.txt | 492 ++++++++++++++++++ pom.xml | 1 + 16 files changed, 1394 insertions(+) create mode 100644 commonmark-ext-gfm-alerts/README.md create mode 100644 commonmark-ext-gfm-alerts/pom.xml create mode 100644 commonmark-ext-gfm-alerts/src/main/java/module-info.java create mode 100644 commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/Alert.java create mode 100644 commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java create mode 100644 commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java create mode 100644 commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertMarkdownNodeRenderer.java create mode 100644 commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java create mode 100644 commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java create mode 100644 commonmark-ext-gfm-alerts/src/main/javadoc/overview.html create mode 100644 commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsMarkdownRendererTest.java create mode 100644 commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java create mode 100644 commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java create mode 100644 commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java create mode 100644 commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt diff --git a/commonmark-ext-gfm-alerts/README.md b/commonmark-ext-gfm-alerts/README.md new file mode 100644 index 000000000..b70584e9b --- /dev/null +++ b/commonmark-ext-gfm-alerts/README.md @@ -0,0 +1,70 @@ +# commonmark-ext-gfm-alerts + +Extension for [commonmark-java](https://github.com/commonmark/commonmark-java) that adds support for [GitHub Flavored Markdown alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts). + +Enables highlighting important information using blockquote syntax with five standard alert types: NOTE, TIP, IMPORTANT, WARNING, and CAUTION. + +## Usage + +#### Markdown Syntax + +```markdown +> [!NOTE] +> Useful information + +> [!WARNING] +> Critical information +``` + +#### Standard GFM Types + +```java +Extension extension = AlertsExtension.create(); +Parser parser = Parser.builder().extensions(List.of(extension)).build(); +HtmlRenderer renderer = HtmlRenderer.builder().extensions(List.of(extension)).build(); +``` + +#### Custom Alert Types + +Add custom types beyond the five standard GFM types: + +```java +Extension extension = AlertsExtension.builder() + .addCustomType("INFO", "Information") + .build(); +``` + +Custom types must be UPPERCASE and cannot override standard types. + +#### Styling + +Alerts render as `
` elements with CSS classes: + +```html +
+

Note

+

Content

+
+``` + +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 CSS `::before` pseudo-elements with GitHub's [Octicons](https://primer.style/octicons/) (info, light-bulb, report, alert, stop icons). + +## 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..5235af6b2 --- /dev/null +++ b/commonmark-ext-gfm-alerts/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.commonmark + commonmark-parent + 0.27.2-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/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..63a69f746 --- /dev/null +++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java @@ -0,0 +1,117 @@ +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.HashSet; +import java.util.LinkedHashMap; +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 LinkedHashMap<>(builder.customTypes); + } + + public static Extension create() { + return builder().build(); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public void extend(Parser.Builder parserBuilder) { + Set 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 LinkedHashMap<>(); + + /** + * 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())) { + 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..202c20290 --- /dev/null +++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java @@ -0,0 +1,77 @@ +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) { + String type = alert.getType(); + String cssClass = type.toLowerCase(); + + htmlWriter.line(); + Map 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", 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) { + if (customTypeTitles.containsKey(type)) { + return customTypeTitles.get(type); + } + 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) { + Node node = parent.getFirstChild(); + while (node != null) { + Node 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..edc988c94 --- /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) { + Node node = parent.getFirstChild(); + while (node != null) { + Node 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..dcb1a25eb --- /dev/null +++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java @@ -0,0 +1,22 @@ +package org.commonmark.ext.gfm.alerts.internal; + +import org.commonmark.ext.gfm.alerts.Alert; +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(org.commonmark.node.Node node) { + Alert 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..0d02c3527 --- /dev/null +++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java @@ -0,0 +1,170 @@ +package org.commonmark.ext.gfm.alerts.internal; + +import org.commonmark.ext.gfm.alerts.Alert; +import org.commonmark.node.*; +import org.commonmark.parser.PostProcessor; + +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AlertPostProcessor implements PostProcessor { + + // Case-insensitive matching for alert type (GitHub supports any case) + 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) { + AlertVisitor visitor = new AlertVisitor(allowedTypes); + document.accept(visitor); + return document; + } + + private static class AlertVisitor extends AbstractVisitor { + + private final Set allowedTypes; + + AlertVisitor(Set allowedTypes) { + this.allowedTypes = allowedTypes; + } + + @Override + public void visit(BlockQuote blockQuote) { + // Only convert top-level block quotes (direct children of Document). + // This matches GitHub's behavior where alerts are only detected at the document level. + if (blockQuote.getParent() instanceof Document) { + if (tryConvertToAlert(blockQuote)) { + return; + } + } + visitChildren(blockQuote); + } + + private boolean tryConvertToAlert(BlockQuote blockQuote) { + Node firstChild = blockQuote.getFirstChild(); + if (!(firstChild instanceof Paragraph)) { + return false; + } + + Paragraph paragraph = (Paragraph) firstChild; + Node firstInline = paragraph.getFirstChild(); + if (!(firstInline instanceof Text)) { + return false; + } + + Text textNode = (Text) firstInline; + String literal = textNode.getLiteral(); + + // The alert marker can be the entire text node content, or just the first line + // before a line break. We need to check both cases. + // Trailing spaces on the marker line create a HardLineBreak instead of SoftLineBreak. + String markerText; + Node afterMarker = firstInline.getNext(); + if (afterMarker instanceof SoftLineBreak || afterMarker instanceof HardLineBreak || afterMarker == null) { + markerText = literal; + } else { + // Text followed by something other than a line break on same line - not an alert + return false; + } + + Matcher matcher = ALERT_PATTERN.matcher(markerText); + if (!matcher.matches()) { + return false; + } + + String type = matcher.group(1).toUpperCase(); + if (!allowedTypes.contains(type)) { + return false; + } + + // Must have content after the marker line. An alert with ONLY the marker + // and no content is a normal blockquote on GitHub. + boolean hasContentAfterMarker; + if (afterMarker instanceof SoftLineBreak || afterMarker instanceof HardLineBreak) { + // There's a line break after marker - check if there's content after it + hasContentAfterMarker = afterMarker.getNext() != null || paragraph.getNext() != null; + } else { + // Marker is the only thing in this text node + hasContentAfterMarker = paragraph.getNext() != null; + } + + if (!hasContentAfterMarker) { + return false; + } + + // Check if the content after the marker is only whitespace + if (isContentWhitespaceOnly(paragraph, firstInline)) { + return false; + } + + // Valid alert. Create Alert node and transfer children. + Alert alert = new Alert(type); + + blockQuote.insertAfter(alert); + + // Remove the marker text and line break from the first paragraph + if (afterMarker instanceof SoftLineBreak || afterMarker instanceof HardLineBreak) { + afterMarker.unlink(); + } + firstInline.unlink(); + + // If paragraph is now empty, remove it + if (paragraph.getFirstChild() == null) { + paragraph.unlink(); + } + + // Move remaining children from blockquote to alert + Node child = blockQuote.getFirstChild(); + while (child != null) { + Node next = child.getNext(); + alert.appendChild(child); + child = next; + } + + blockQuote.unlink(); + return true; + } + + private boolean isContentWhitespaceOnly(Paragraph firstParagraph, Node markerNode) { + // Check inline nodes after the marker in the first paragraph + Node next = markerNode.getNext(); + while (next != null) { + if (next instanceof Text) { + if (!((Text) next).getLiteral().trim().isEmpty()) { + return false; + } + } else if (!(next instanceof SoftLineBreak) && !(next instanceof HardLineBreak)) { + return false; + } + next = next.getNext(); + } + + // Check block-level siblings after the first paragraph + Node block = firstParagraph.getNext(); + while (block != null) { + if (block instanceof Paragraph) { + Node child = block.getFirstChild(); + while (child != null) { + if (child instanceof Text && !((Text) child).getLiteral().trim().isEmpty()) { + return false; + } else if (!(child instanceof Text) && !(child instanceof SoftLineBreak) && !(child instanceof HardLineBreak)) { + return false; + } + child = child.getNext(); + } + } else { + return false; + } + block = block.getNext(); + } + + return true; + } + } +} 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..fc77674df --- /dev/null +++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java @@ -0,0 +1,43 @@ +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(); + private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).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..dba1628cb --- /dev/null +++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java @@ -0,0 +1,94 @@ +package org.commonmark.ext.gfm.alerts.examples; + +import org.commonmark.Extension; +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)); + + // Create the alerts extension with default settings + Extension extension = AlertsExtension.create(); + + Parser parser = Parser.builder() + .extensions(List.of(extension)) + .build(); + + HtmlRenderer renderer = HtmlRenderer.builder() + .extensions(List.of(extension)) + .build(); + + // Example markdown with all standard alert types + String 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"; + + String 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)); + + // Create extension with custom types + Extension extension = AlertsExtension.builder() + .addCustomType("INFO", "Information") + .addCustomType("SUCCESS", "Success") + .addCustomType("DANGER", "Danger") + .build(); + + Parser parser = Parser.builder() + .extensions(List.of(extension)) + .build(); + + HtmlRenderer renderer = HtmlRenderer.builder() + .extensions(List.of(extension)) + .build(); + + // Example markdown with custom alert types + String markdown = "# Custom Alert Types\n\n" + + "> [!INFO]\n" + + "> This is a custom information alert.\n\n" + + "> [!SUCCESS]\n" + + "> Operation completed successfully!\n\n" + + "> [!DANGER]\n" + + "> This action is dangerous and irreversible.\n\n" + + "> [!NOTE]\n" + + "> Standard types still work alongside custom types.\n"; + + String 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.txt b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt new file mode 100644 index 000000000..ef2809336 --- /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 +. +
+

Note

+

This is a note

+
+```````````````````````````````` + +```````````````````````````````` example alert +> [!TIP] +> This is a tip +. +
+

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 +. +
+

Note

+

Content

+
+```````````````````````````````` + +```````````````````````````````` example alert +> [!Note] +> Content +. +
+

Note

+

Content

+
+```````````````````````````````` + +## Alert content + +Marker alone in first paragraph, blank line, then content: + +```````````````````````````````` example alert +> [!NOTE] +> +> Content +. +
+

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 +. +
+

Note

+

Check out this link 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 +. +
+

Note

+

This is a note

+
+```````````````````````````````` + +Trailing tabs after marker: + +```````````````````````````````` example alert +> [!WARNING]→→ +> Be careful +. +
+

Warning

+

Be careful

+
+```````````````````````````````` + +Leading spaces before blockquote marker: + +```````````````````````````````` example alert + > [!IMPORTANT] + > Content +. +
+

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 +. +
+

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 +. +
    +
  • +
    +

    [!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

+
+```````````````````````````````` \ No newline at end of file diff --git a/pom.xml b/pom.xml index d55d1fd85..58c3ead2c 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ commonmark commonmark-ext-autolink commonmark-ext-footnotes + commonmark-ext-gfm-alerts commonmark-ext-gfm-strikethrough commonmark-ext-gfm-tables commonmark-ext-heading-anchor From 5815c3c0b17f52b53e65465fd8ff65da8e6304fa Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Fri, 27 Mar 2026 17:39:20 +0100 Subject: [PATCH 70/89] Add spec generator script and match GitHub softbreak rendering Add a jbang script (generate-alerts-spec.java) that generates alerts-spec.txt from a template (alerts-spec-template.md) by rendering each example through the GitHub Markdown API and normalizing the HTML. Configure AlertsSpecTest with softbreak("
") to match GitHub's rendering, reducing the normalizations needed in the generator. Co-Authored-By: Claude Opus 4.6 --- .../ext/gfm/alerts/AlertsSpecTest.java | 3 +- .../test/resources/alerts-spec-template.md | 280 ++++++++++++++++++ .../src/test/resources/alerts-spec.txt | 28 +- .../test/resources/generate-alerts-spec.java | 111 +++++++ 4 files changed, 407 insertions(+), 15 deletions(-) create mode 100644 commonmark-ext-gfm-alerts/src/test/resources/alerts-spec-template.md create mode 100644 commonmark-ext-gfm-alerts/src/test/resources/generate-alerts-spec.java 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 index fc77674df..8155d8009 100644 --- 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 @@ -21,7 +21,8 @@ public class AlertsSpecTest extends RenderingTestCase { private static final Set EXTENSIONS = Set.of(AlertsExtension.create()); private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build(); - private static final HtmlRenderer RENDERER = HtmlRenderer.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; 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 index ef2809336..6f041fee4 100644 --- a/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt +++ b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt @@ -219,7 +219,7 @@ Unknown type: > Some text .
-

[!INVALID] +

[!INVALID]
Some text

```````````````````````````````` @@ -231,7 +231,7 @@ Unconfigured custom type is not an alert: > Should be blockquote .
-

[!INFO] +

[!INFO]
Should be blockquote

```````````````````````````````` @@ -265,7 +265,7 @@ Extra space inside marker: > Should be blockquote .
-

[! NOTE] +

[! NOTE]
Should be blockquote

```````````````````````````````` @@ -277,7 +277,7 @@ Missing brackets: > Should be blockquote .
-

!NOTE +

!NOTE
Should be blockquote

```````````````````````````````` @@ -289,7 +289,7 @@ Missing exclamation mark: > Should be blockquote .
-

[NOTE] +

[NOTE]
Should be blockquote

```````````````````````````````` @@ -405,7 +405,7 @@ Nested alert inside alert renders as blockquote:

Note

This is a note

-

[!WARNING] +

[!WARNING]
Nested content

@@ -436,7 +436,7 @@ Alert inside list item stays as blockquote:
  • -

    [!NOTE] +

    [!NOTE]
    Test

  • @@ -453,8 +453,8 @@ Alert marker in content is treated as text: .

    Note

    -

    This is a note -[!WARNING] +

    This is a note
    +[!WARNING]
    This is still part of the note

    ```````````````````````````````` @@ -471,8 +471,8 @@ Lazy continuation .

    Note

    -

    First line -Lazy continuation +

    First line
    +Lazy continuation
    Continues alert

    ```````````````````````````````` @@ -485,8 +485,8 @@ Alert type after regular blockquote content is not an alert: > More text .
    -

    Regular blockquote -[!NOTE] +

    Regular blockquote
    +[!NOTE]
    More text

    -```````````````````````````````` \ No newline at end of file +```````````````````````````````` 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 From f3be489803f3446e610f2948fd77d4e821a5cefd Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Mon, 30 Mar 2026 10:55:31 +0200 Subject: [PATCH 71/89] Address review feedback: simplify PostProcessor and clean up code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace AbstractVisitor with direct Document child iteration (only top-level block quotes are alerts, no need to walk the full tree) - Remove unnecessary isContentWhitespaceOnly method (the parser never produces content nodes for whitespace-only block quote continuations) - Remove redundant markerText/literal variables, use textNode.getLiteral() - Restructure afterMarker checks to avoid 3x instanceof repetition - Add Locale.ROOT to toUpperCase() calls (Turkish locale safety) - Use var throughout all main source files - Use single get()+null check instead of containsKey()+get() in renderer - Add proper Node import in AlertNodeRenderer, remove FQN references - LinkedHashMap → HashMap in AlertsExtension (ordering not needed) - Update README: fix outdated custom type docs, align examples with AlertsExample.java - Add screenshots showing rendered alerts with and without icons Co-Authored-By: Claude Opus 4.6 --- commonmark-ext-gfm-alerts/README.md | 18 +- .../screenshots/alerts-with-icons.png | Bin 0 -> 20571 bytes .../screenshots/alerts.png | Bin 0 -> 19824 bytes .../ext/gfm/alerts/AlertsExtension.java | 11 +- .../internal/AlertHtmlNodeRenderer.java | 15 +- .../internal/AlertMarkdownNodeRenderer.java | 4 +- .../alerts/internal/AlertNodeRenderer.java | 7 +- .../alerts/internal/AlertPostProcessor.java | 198 ++++++------------ .../gfm/alerts/examples/AlertsExample.java | 41 ++-- 9 files changed, 116 insertions(+), 178 deletions(-) create mode 100644 commonmark-ext-gfm-alerts/screenshots/alerts-with-icons.png create mode 100644 commonmark-ext-gfm-alerts/screenshots/alerts.png diff --git a/commonmark-ext-gfm-alerts/README.md b/commonmark-ext-gfm-alerts/README.md index b70584e9b..2368812e5 100644 --- a/commonmark-ext-gfm-alerts/README.md +++ b/commonmark-ext-gfm-alerts/README.md @@ -19,9 +19,9 @@ Enables highlighting important information using blockquote syntax with five sta #### Standard GFM Types ```java -Extension extension = AlertsExtension.create(); -Parser parser = Parser.builder().extensions(List.of(extension)).build(); -HtmlRenderer renderer = HtmlRenderer.builder().extensions(List.of(extension)).build(); +var extension = AlertsExtension.create(); +var parser = Parser.builder().extensions(List.of(extension)).build(); +var renderer = HtmlRenderer.builder().extensions(List.of(extension)).build(); ``` #### Custom Alert Types @@ -29,12 +29,12 @@ HtmlRenderer renderer = HtmlRenderer.builder().extensions(List.of(extension)).bu Add custom types beyond the five standard GFM types: ```java -Extension extension = AlertsExtension.builder() - .addCustomType("INFO", "Information") +var extension = AlertsExtension.builder() + .addCustomType("BUG", "Known Bug") .build(); ``` -Custom types must be UPPERCASE and cannot override standard types. +Custom types must be UPPERCASE. Standard type titles can also be overridden for localization. #### Styling @@ -63,7 +63,11 @@ Basic CSS example: .markdown-alert-caution { border-color: #cf222e; background-color: #ffebe9; } ``` -Icons can be added using CSS `::before` pseudo-elements with GitHub's [Octicons](https://primer.style/octicons/) (info, light-bulb, report, alert, stop icons). +![Alerts](screenshots/alerts.png) + +Icons can be added using GitHub's [Octicons](https://primer.style/octicons/): + +![Alerts with icons](screenshots/alerts-with-icons.png) ## License 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 0000000000000000000000000000000000000000..47da9402b557d4f1d8fac1f1d9dd7b2cf73b4b14 GIT binary patch literal 20571 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 literal 0 HcmV?d00001 diff --git a/commonmark-ext-gfm-alerts/screenshots/alerts.png b/commonmark-ext-gfm-alerts/screenshots/alerts.png new file mode 100644 index 0000000000000000000000000000000000000000..83d4009f017bea3a0fe5d9fcc1aee6c1908964e8 GIT binary patch literal 19824 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 literal 0 HcmV?d00001 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 index 63a69f746..3990034d2 100644 --- 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 @@ -13,8 +13,9 @@ 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.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -34,7 +35,7 @@ public class AlertsExtension implements Parser.ParserExtension, HtmlRenderer.Htm private final Map customTypes; private AlertsExtension(Builder builder) { - this.customTypes = new LinkedHashMap<>(builder.customTypes); + this.customTypes = new HashMap<>(builder.customTypes); } public static Extension create() { @@ -47,7 +48,7 @@ public static Builder builder() { @Override public void extend(Parser.Builder parserBuilder) { - Set allowedTypes = new HashSet<>(STANDARD_TYPES); + var allowedTypes = new HashSet<>(STANDARD_TYPES); allowedTypes.addAll(customTypes.keySet()); parserBuilder.postProcessor(new AlertPostProcessor(allowedTypes)); } @@ -81,7 +82,7 @@ public Set getSpecialCharacters() { * Builder for configuring the alerts extension. */ public static class Builder { - private final Map customTypes = new LinkedHashMap<>(); + private final Map customTypes = new HashMap<>(); /** * Adds a custom alert type with a display title. @@ -100,7 +101,7 @@ public Builder addCustomType(String type, String title) { if (title == null || title.isEmpty()) { throw new IllegalArgumentException("Title must not be null or empty"); } - if (!type.equals(type.toUpperCase())) { + if (!type.equals(type.toUpperCase(Locale.ROOT))) { throw new IllegalArgumentException("Type must be uppercase: " + type); } customTypes.put(type, title); 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 index 202c20290..763b2e217 100644 --- 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 @@ -22,11 +22,11 @@ public AlertHtmlNodeRenderer(HtmlNodeRendererContext context, Map attributes = new LinkedHashMap<>(); + var attributes = new LinkedHashMap(); attributes.put("class", "markdown-alert markdown-alert-" + cssClass); attributes.put("data-alert-type", cssClass); @@ -47,8 +47,9 @@ protected void renderAlert(Alert alert) { } private String getAlertTitle(String type) { - if (customTypeTitles.containsKey(type)) { - return customTypeTitles.get(type); + var customTypeTitle = customTypeTitles.get(type); + if (customTypeTitle != null) { + return customTypeTitle; } switch (type) { case "NOTE": @@ -67,9 +68,9 @@ private String getAlertTitle(String type) { } private void renderChildren(Node parent) { - Node node = parent.getFirstChild(); + var node = parent.getFirstChild(); while (node != null) { - Node next = node.getNext(); + 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 index edc988c94..e3da62aea 100644 --- 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 @@ -28,9 +28,9 @@ protected void renderAlert(Alert alert) { } private void renderChildren(Node parent) { - Node node = parent.getFirstChild(); + var node = parent.getFirstChild(); while (node != null) { - Node next = node.getNext(); + 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 index dcb1a25eb..45b34bb46 100644 --- 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 @@ -1,6 +1,7 @@ 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; @@ -8,13 +9,13 @@ public abstract class AlertNodeRenderer implements NodeRenderer { @Override - public Set> getNodeTypes() { + public Set> getNodeTypes() { return Set.of(Alert.class); } @Override - public void render(org.commonmark.node.Node node) { - Alert alert = (Alert) node; + public void render(Node node) { + var alert = (Alert) node; renderAlert(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 index 0d02c3527..b524adc05 100644 --- 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 @@ -1,16 +1,21 @@ package org.commonmark.ext.gfm.alerts.internal; import org.commonmark.ext.gfm.alerts.Alert; -import org.commonmark.node.*; +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.Matcher; import java.util.regex.Pattern; public class AlertPostProcessor implements PostProcessor { - // Case-insensitive matching for alert type (GitHub supports any case) + // 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; @@ -21,150 +26,85 @@ public AlertPostProcessor(Set allowedTypes) { @Override public Node process(Node document) { - AlertVisitor visitor = new AlertVisitor(allowedTypes); - document.accept(visitor); + // 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 static class AlertVisitor extends AbstractVisitor { - - private final Set allowedTypes; - - AlertVisitor(Set allowedTypes) { - this.allowedTypes = allowedTypes; + private void tryConvertToAlert(BlockQuote blockQuote) { + var firstChild = blockQuote.getFirstChild(); + if (!(firstChild instanceof Paragraph)) { + return; } - @Override - public void visit(BlockQuote blockQuote) { - // Only convert top-level block quotes (direct children of Document). - // This matches GitHub's behavior where alerts are only detected at the document level. - if (blockQuote.getParent() instanceof Document) { - if (tryConvertToAlert(blockQuote)) { - return; - } - } - visitChildren(blockQuote); + var paragraph = (Paragraph) firstChild; + var firstInline = paragraph.getFirstChild(); + if (!(firstInline instanceof Text)) { + return; } - private boolean tryConvertToAlert(BlockQuote blockQuote) { - Node firstChild = blockQuote.getFirstChild(); - if (!(firstChild instanceof Paragraph)) { - return false; - } - - Paragraph paragraph = (Paragraph) firstChild; - Node firstInline = paragraph.getFirstChild(); - if (!(firstInline instanceof Text)) { - return false; - } + var textNode = (Text) firstInline; - Text textNode = (Text) firstInline; - String literal = textNode.getLiteral(); - - // The alert marker can be the entire text node content, or just the first line - // before a line break. We need to check both cases. - // Trailing spaces on the marker line create a HardLineBreak instead of SoftLineBreak. - String markerText; - Node afterMarker = firstInline.getNext(); - if (afterMarker instanceof SoftLineBreak || afterMarker instanceof HardLineBreak || afterMarker == null) { - markerText = literal; - } else { - // Text followed by something other than a line break on same line - not an alert - return false; - } - - Matcher matcher = ALERT_PATTERN.matcher(markerText); - if (!matcher.matches()) { - return false; - } - - String type = matcher.group(1).toUpperCase(); - if (!allowedTypes.contains(type)) { - return false; - } + // 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; + } - // Must have content after the marker line. An alert with ONLY the marker - // and no content is a normal blockquote on GitHub. - boolean hasContentAfterMarker; - if (afterMarker instanceof SoftLineBreak || afterMarker instanceof HardLineBreak) { - // There's a line break after marker - check if there's content after it - hasContentAfterMarker = afterMarker.getNext() != null || paragraph.getNext() != null; - } else { - // Marker is the only thing in this text node - hasContentAfterMarker = paragraph.getNext() != null; - } + var matcher = ALERT_PATTERN.matcher(textNode.getLiteral()); + if (!matcher.matches()) { + return; + } - if (!hasContentAfterMarker) { - return false; - } + var type = matcher.group(1).toUpperCase(Locale.ROOT); + if (!allowedTypes.contains(type)) { + return; + } - // Check if the content after the marker is only whitespace - if (isContentWhitespaceOnly(paragraph, firstInline)) { - return false; + // 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; } - - // Valid alert. Create Alert node and transfer children. - Alert alert = new Alert(type); - - blockQuote.insertAfter(alert); - - // Remove the marker text and line break from the first paragraph - if (afterMarker instanceof SoftLineBreak || afterMarker instanceof HardLineBreak) { - afterMarker.unlink(); + afterMarker.unlink(); + } else { + // Marker is the only thing in the paragraph + if (paragraph.getNext() == null) { + return; } - firstInline.unlink(); + } - // If paragraph is now empty, remove it - if (paragraph.getFirstChild() == null) { - paragraph.unlink(); - } + // Valid alert. Create Alert node and transfer children. + var alert = new Alert(type); + blockQuote.insertAfter(alert); - // Move remaining children from blockquote to alert - Node child = blockQuote.getFirstChild(); - while (child != null) { - Node next = child.getNext(); - alert.appendChild(child); - child = next; - } + // Remove the marker text from the first paragraph + firstInline.unlink(); - blockQuote.unlink(); - return true; + // If paragraph is now empty, remove it + if (paragraph.getFirstChild() == null) { + paragraph.unlink(); } - private boolean isContentWhitespaceOnly(Paragraph firstParagraph, Node markerNode) { - // Check inline nodes after the marker in the first paragraph - Node next = markerNode.getNext(); - while (next != null) { - if (next instanceof Text) { - if (!((Text) next).getLiteral().trim().isEmpty()) { - return false; - } - } else if (!(next instanceof SoftLineBreak) && !(next instanceof HardLineBreak)) { - return false; - } - next = next.getNext(); - } - - // Check block-level siblings after the first paragraph - Node block = firstParagraph.getNext(); - while (block != null) { - if (block instanceof Paragraph) { - Node child = block.getFirstChild(); - while (child != null) { - if (child instanceof Text && !((Text) child).getLiteral().trim().isEmpty()) { - return false; - } else if (!(child instanceof Text) && !(child instanceof SoftLineBreak) && !(child instanceof HardLineBreak)) { - return false; - } - child = child.getNext(); - } - } else { - return false; - } - block = block.getNext(); - } - - return true; + // 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/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 index dba1628cb..34b78385c 100644 --- 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 @@ -1,6 +1,5 @@ package org.commonmark.ext.gfm.alerts.examples; -import org.commonmark.Extension; import org.commonmark.ext.gfm.alerts.AlertsExtension; import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; @@ -22,19 +21,17 @@ private static void standardTypesExample() { System.out.println("STANDARD GFM ALERT TYPES"); System.out.println("=".repeat(60)); - // Create the alerts extension with default settings - Extension extension = AlertsExtension.create(); + var extension = AlertsExtension.create(); - Parser parser = Parser.builder() + var parser = Parser.builder() .extensions(List.of(extension)) .build(); - HtmlRenderer renderer = HtmlRenderer.builder() + var renderer = HtmlRenderer.builder() .extensions(List.of(extension)) .build(); - // Example markdown with all standard alert types - String markdown = "# GFM Alerts Demo\n\n" + + var markdown = "# GFM Alerts Demo\n\n" + "> [!NOTE]\n" + "> Highlights information that users should take into account.\n\n" + "> [!TIP]\n" + @@ -46,7 +43,7 @@ private static void standardTypesExample() { "> [!CAUTION]\n" + "> Advises about risks or negative outcomes.\n"; - String html = renderer.render(parser.parse(markdown)); + var html = renderer.render(parser.parse(markdown)); System.out.println("Markdown Input:"); System.out.println(markdown); @@ -58,33 +55,27 @@ private static void customTypesExample() { System.out.println("CUSTOM ALERT TYPES"); System.out.println("=".repeat(60)); - // Create extension with custom types - Extension extension = AlertsExtension.builder() - .addCustomType("INFO", "Information") - .addCustomType("SUCCESS", "Success") - .addCustomType("DANGER", "Danger") + var extension = AlertsExtension.builder() + .addCustomType("BUG", "Known Bug") .build(); - Parser parser = Parser.builder() + var parser = Parser.builder() .extensions(List.of(extension)) .build(); - HtmlRenderer renderer = HtmlRenderer.builder() + var renderer = HtmlRenderer.builder() .extensions(List.of(extension)) .build(); - // Example markdown with custom alert types - String markdown = "# Custom Alert Types\n\n" + - "> [!INFO]\n" + - "> This is a custom information alert.\n\n" + - "> [!SUCCESS]\n" + - "> Operation completed successfully!\n\n" + - "> [!DANGER]\n" + - "> This action is dangerous and irreversible.\n\n" + + var markdown = "# Custom Alert Types\n\n" + "> [!NOTE]\n" + - "> Standard types still work alongside custom types.\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"; - String html = renderer.render(parser.parse(markdown)); + var html = renderer.render(parser.parse(markdown)); System.out.println("Markdown Input:"); System.out.println(markdown); From f8f7684fb67d92d679ff87da4a32e89c4588fdd3 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Mon, 30 Mar 2026 19:24:09 +1100 Subject: [PATCH 72/89] add node depth limit --- .../org/commonmark/node/AbstractVisitor.java | 23 ++++++- .../commonmark/test/AbstractVisitorTest.java | 64 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java b/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java index 7edd635d7..b7389ceb9 100644 --- a/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java +++ b/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java @@ -7,6 +7,19 @@ * call {@link #visitChildren}. */ public abstract class AbstractVisitor implements Visitor { + private final int maxDepth; + private int currentDepth; + + public AbstractVisitor() { + this(Integer.MAX_VALUE); + } + + protected AbstractVisitor(int maxDepth) { + if (maxDepth < 0) { + throw new IllegalArgumentException("maxDepth must be >= 0"); + } + this.maxDepth = maxDepth; + } @Override public void visit(BlockQuote blockQuote) { @@ -129,12 +142,20 @@ public void visit(CustomNode customNode) { * @param parent the parent node whose children should be visited */ protected void visitChildren(Node parent) { + if (currentDepth >= maxDepth) { + return; + } Node node = parent.getFirstChild(); while (node != null) { // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no // node after visiting it. So get the next node before visiting. Node next = node.getNext(); - node.accept(this); + currentDepth++; + try { + node.accept(this); + } finally { + currentDepth--; + } node = next; } } diff --git a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java index edb6936f4..554f9c25e 100644 --- a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java +++ b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java @@ -3,9 +3,38 @@ import org.commonmark.node.*; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class AbstractVisitorTest { + @Test + public void maxDepthMustBeZeroOrGreater() { + assertThatThrownBy(() -> new RecordingVisitor(-1)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void maxDepthZeroVisitsOnlyRoot() { + var paragraph = paragraphTree(); + var visitor = new RecordingVisitor(0); + + paragraph.accept(visitor); + + assertThat(visitor.visited).containsExactly("paragraph"); + } + + @Test + public void maxDepthOneVisitsDirectChildrenButNotGrandchildren() { + var paragraph = paragraphTree(); + var visitor = new RecordingVisitor(1); + + paragraph.accept(visitor); + + assertThat(visitor.visited).containsExactly("paragraph", "emphasis", "text:tail"); + } @Test public void replacingNodeInVisitorShouldNotDestroyVisitOrder() { @@ -34,4 +63,39 @@ private static void assertCode(String expectedLiteral, Node node) { Code code = (Code) node; assertThat(code.getLiteral()).isEqualTo(expectedLiteral); } + + private static Paragraph paragraphTree() { + var paragraph = new Paragraph(); + var emphasis = new Emphasis(); + emphasis.appendChild(new Text("nested")); + paragraph.appendChild(emphasis); + paragraph.appendChild(new Text("tail")); + return paragraph; + } + + private static final class RecordingVisitor extends AbstractVisitor { + private final List visited = new ArrayList<>(); + + private RecordingVisitor(int maxDepth) { + super(maxDepth); + } + + @Override + public void visit(Paragraph paragraph) { + visited.add("paragraph"); + super.visit(paragraph); + } + + @Override + public void visit(Emphasis emphasis) { + visited.add("emphasis"); + super.visit(emphasis); + } + + @Override + public void visit(Text text) { + visited.add("text:" + text.getLiteral()); + super.visit(text); + } + } } From 50d2c368f8f4225da809697f371c7c4ea9ee05c2 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Mon, 30 Mar 2026 21:21:55 +1100 Subject: [PATCH 73/89] abort deep nodes early --- .../commonmark/internal/DocumentParser.java | 8 +- .../java/org/commonmark/parser/Parser.java | 26 ++++- .../java/org/commonmark/test/ParserTest.java | 102 ++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java index d935f8d27..2b9db45cc 100644 --- a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java +++ b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java @@ -76,6 +76,7 @@ public class DocumentParser implements ParserState { private final List linkProcessors; private final Set linkMarkers; private final IncludeSourceSpans includeSourceSpans; + private final int maxOpenBlockParsers; private final DocumentBlockParser documentBlockParser; private final Definitions definitions = new Definitions(); @@ -84,7 +85,8 @@ public class DocumentParser implements ParserState { public DocumentParser(List blockParserFactories, InlineParserFactory inlineParserFactory, List inlineContentParserFactories, List delimiterProcessors, - List linkProcessors, Set linkMarkers, IncludeSourceSpans includeSourceSpans) { + List linkProcessors, Set linkMarkers, + IncludeSourceSpans includeSourceSpans, int maxOpenBlockParsers) { this.blockParserFactories = blockParserFactories; this.inlineParserFactory = inlineParserFactory; this.inlineContentParserFactories = inlineContentParserFactories; @@ -92,6 +94,7 @@ public DocumentParser(List blockParserFactories, InlineParse this.linkProcessors = linkProcessors; this.linkMarkers = linkMarkers; this.includeSourceSpans = includeSourceSpans; + this.maxOpenBlockParsers = maxOpenBlockParsers; this.documentBlockParser = new DocumentBlockParser(); activateBlockParser(new OpenBlockParser(documentBlockParser, 0)); @@ -461,6 +464,9 @@ private void addSourceSpans() { } private BlockStartImpl findBlockStart(BlockParser blockParser) { + if (openBlockParsers.size() - 1 >= maxOpenBlockParsers) { + return null; + } MatchedBlockParser matchedBlockParser = new MatchedBlockParserImpl(blockParser); for (BlockParserFactory blockParserFactory : blockParserFactories) { BlockStart result = blockParserFactory.tryStart(this, matchedBlockParser); diff --git a/commonmark/src/main/java/org/commonmark/parser/Parser.java b/commonmark/src/main/java/org/commonmark/parser/Parser.java index b98d0581f..9e90e906b 100644 --- a/commonmark/src/main/java/org/commonmark/parser/Parser.java +++ b/commonmark/src/main/java/org/commonmark/parser/Parser.java @@ -37,6 +37,7 @@ public class Parser { private final InlineParserFactory inlineParserFactory; private final List postProcessors; private final IncludeSourceSpans includeSourceSpans; + private final int maxOpenBlockParsers; private Parser(Builder builder) { this.blockParserFactories = DocumentParser.calculateBlockParserFactories(builder.blockParserFactories, builder.enabledBlockTypes); @@ -47,6 +48,7 @@ private Parser(Builder builder) { this.linkProcessors = builder.linkProcessors; this.linkMarkers = builder.linkMarkers; this.includeSourceSpans = builder.includeSourceSpans; + this.maxOpenBlockParsers = builder.maxOpenBlockParsers; // Try to construct an inline parser. Invalid configuration might result in an exception, which we want to // detect as soon as possible. @@ -106,7 +108,7 @@ public Node parseReader(Reader input) throws IOException { private DocumentParser createDocumentParser() { return new DocumentParser(blockParserFactories, inlineParserFactory, inlineContentParserFactories, - delimiterProcessors, linkProcessors, linkMarkers, includeSourceSpans); + delimiterProcessors, linkProcessors, linkMarkers, includeSourceSpans, maxOpenBlockParsers); } private Node postProcess(Node document) { @@ -129,6 +131,7 @@ public static class Builder { private Set> enabledBlockTypes = DocumentParser.getDefaultBlockParserTypes(); private InlineParserFactory inlineParserFactory; private IncludeSourceSpans includeSourceSpans = IncludeSourceSpans.NONE; + private int maxOpenBlockParsers = Integer.MAX_VALUE; /** * @return the configured {@link Parser} @@ -200,6 +203,27 @@ public Builder includeSourceSpans(IncludeSourceSpans includeSourceSpans) { return this; } + /** + * Limit how many non-document block parsers may be open at once while parsing. + *

    + * Once the limit is reached, additional block starts are treated as plain text instead of + * creating deeper nested block structure. + *

    + * The document root parser is not counted. The default is unlimited, so callers that keep + * using {@code Parser.builder().build()} preserve current behavior. + * + * @param maxOpenBlockParsers maximum number of open non-document block parsers, must be + * zero or greater + * @return {@code this} + */ + public Builder maxOpenBlockParsers(int maxOpenBlockParsers) { + if (maxOpenBlockParsers < 0) { + throw new IllegalArgumentException("maxOpenBlockParsers must be >= 0"); + } + this.maxOpenBlockParsers = maxOpenBlockParsers; + return this; + } + /** * Add a custom block parser factory. *

    diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index c119b5e2d..6164759ec 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -4,6 +4,7 @@ import org.commonmark.parser.*; import org.commonmark.parser.block.*; import org.commonmark.renderer.html.HtmlRenderer; +import org.commonmark.renderer.text.TextContentRenderer; import org.commonmark.testutil.TestResources; import org.junit.jupiter.api.Test; @@ -135,6 +136,76 @@ public void threading() throws Exception { } } + @Test + public void maxOpenBlockParsersMustBeZeroOrGreater() { + assertThatThrownBy(() -> + Parser.builder().maxOpenBlockParsers(-1)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void maxOpenBlockParsersIsOptIn() { + var parser = Parser.builder().build(); + + var document = parser.parse(alternatingNestedList(9)); + + assertThat(renderText(deepestStructuredParagraph(document, 9))).isEqualTo("level9"); + } + + @Test + public void maxOpenBlockParsersPreservesSevenLogicalListLevelsAtSeventeenBlocks() { + var parser = Parser.builder().maxOpenBlockParsers(17).build(); + + var document = parser.parse(alternatingNestedList(7)); + + assertThat(renderText(deepestStructuredParagraph(document, 7))).isEqualTo("level7"); + } + + @Test + public void maxOpenBlockParsersPreservesEightLogicalListLevelsAtSeventeenBlocks() { + var parser = Parser.builder().maxOpenBlockParsers(17).build(); + + var document = parser.parse(alternatingNestedList(8)); + + assertThat(renderText(deepestStructuredParagraph(document, 8))).isEqualTo("level8"); + } + + @Test + public void maxOpenBlockParsersDegradesTheNinthLogicalListLevelToPlainText() { + var parser = Parser.builder().maxOpenBlockParsers(17).build(); + + var document = parser.parse(alternatingNestedList(9)); + var deepestParagraph = deepestStructuredParagraph(document, 8); + + assertThat(renderText(deepestParagraph)).isEqualTo("level8\n- level9"); + assertThat(deepestParagraph.getNext()).isNull(); + } + + @Test + public void maxOpenBlockParsersAlsoLimitsMixedListAndBlockQuoteNesting() { + var parser = Parser.builder().maxOpenBlockParsers(5).build(); + + var document = parser.parse(String.join("\n", + "- level1", + " > level2", + " > > level3", + " > > > level4")); + + var listBlock = document.getFirstChild(); + assertThat(listBlock).isInstanceOf(BulletList.class); + + var listItem = listBlock.getFirstChild(); + var blockQuote1 = listItem.getLastChild(); + assertThat(blockQuote1).isInstanceOf(BlockQuote.class); + + var blockQuote2 = blockQuote1.getLastChild(); + assertThat(blockQuote2).isInstanceOf(BlockQuote.class); + + var deepestParagraph = blockQuote2.getLastChild(); + assertThat(deepestParagraph).isInstanceOf(Paragraph.class); + assertThat(renderText(deepestParagraph)).isEqualTo("level3\n> level4"); + assertThat(deepestParagraph.getNext()).isNull(); + } + private String firstText(Node n) { while (!(n instanceof Text)) { assertThat(n).isNotNull(); @@ -142,4 +213,35 @@ private String firstText(Node n) { } return ((Text) n).getLiteral(); } + + private Paragraph deepestStructuredParagraph(Node document, int levels) { + Node node = document.getFirstChild(); + for (int level = 1; level <= levels; level++) { + assertThat(node).isInstanceOf(ListBlock.class); + var listItem = node.getFirstChild(); + assertThat(listItem).isNotNull(); + if (level == levels) { + assertThat(listItem.getFirstChild()).isInstanceOf(Paragraph.class); + return (Paragraph) listItem.getFirstChild(); + } + node = listItem.getLastChild(); + } + throw new AssertionError("unreachable"); + } + + private String renderText(Node node) { + return TextContentRenderer.builder().build().render(node).trim(); + } + + private String alternatingNestedList(int levels) { + int indent = 0; + var lines = new ArrayList(); + for (int level = 1; level <= levels; level++) { + var ordered = level % 2 == 0; + var marker = ordered ? "1. " : "- "; + lines.add(" ".repeat(indent) + marker + "level" + level); + indent += marker.length(); + } + return String.join("\n", lines); + } } From 7e39836541e97eec661dd2e331b19b45082cbc19 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Mon, 30 Mar 2026 21:23:25 +1100 Subject: [PATCH 74/89] Apply suggestion from @spirit-at-canva --- .../src/main/java/org/commonmark/internal/DocumentParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java index 2b9db45cc..07d97296b 100644 --- a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java +++ b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java @@ -464,7 +464,7 @@ private void addSourceSpans() { } private BlockStartImpl findBlockStart(BlockParser blockParser) { - if (openBlockParsers.size() - 1 >= maxOpenBlockParsers) { + if (openBlockParsers.size() > maxOpenBlockParsers) { return null; } MatchedBlockParser matchedBlockParser = new MatchedBlockParserImpl(blockParser); From 7a9558b095574202647fb2e7cb061dfbe082e396 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Mon, 30 Mar 2026 21:25:11 +1100 Subject: [PATCH 75/89] Apply suggestion from @spirit-at-canva --- commonmark/src/main/java/org/commonmark/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commonmark/src/main/java/org/commonmark/parser/Parser.java b/commonmark/src/main/java/org/commonmark/parser/Parser.java index 9e90e906b..dc4decf28 100644 --- a/commonmark/src/main/java/org/commonmark/parser/Parser.java +++ b/commonmark/src/main/java/org/commonmark/parser/Parser.java @@ -210,7 +210,7 @@ public Builder includeSourceSpans(IncludeSourceSpans includeSourceSpans) { * creating deeper nested block structure. *

    * The document root parser is not counted. The default is unlimited, so callers that keep - * using {@code Parser.builder().build()} preserve current behavior. + * using {@code Parser.builder().build()} preserve behavior. * * @param maxOpenBlockParsers maximum number of open non-document block parsers, must be * zero or greater From 0c026d5b0062c4edf71d592dce67796c89628ed1 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Tue, 31 Mar 2026 12:59:26 +1100 Subject: [PATCH 76/89] remove changes to visitor --- .../org/commonmark/node/AbstractVisitor.java | 23 +--- .../commonmark/test/AbstractVisitorTest.java | 64 ----------- .../java/org/commonmark/test/ParserTest.java | 105 ++++++++++++++++++ 3 files changed, 106 insertions(+), 86 deletions(-) diff --git a/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java b/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java index b7389ceb9..7edd635d7 100644 --- a/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java +++ b/commonmark/src/main/java/org/commonmark/node/AbstractVisitor.java @@ -7,19 +7,6 @@ * call {@link #visitChildren}. */ public abstract class AbstractVisitor implements Visitor { - private final int maxDepth; - private int currentDepth; - - public AbstractVisitor() { - this(Integer.MAX_VALUE); - } - - protected AbstractVisitor(int maxDepth) { - if (maxDepth < 0) { - throw new IllegalArgumentException("maxDepth must be >= 0"); - } - this.maxDepth = maxDepth; - } @Override public void visit(BlockQuote blockQuote) { @@ -142,20 +129,12 @@ public void visit(CustomNode customNode) { * @param parent the parent node whose children should be visited */ protected void visitChildren(Node parent) { - if (currentDepth >= maxDepth) { - return; - } Node node = parent.getFirstChild(); while (node != null) { // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no // node after visiting it. So get the next node before visiting. Node next = node.getNext(); - currentDepth++; - try { - node.accept(this); - } finally { - currentDepth--; - } + node.accept(this); node = next; } } diff --git a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java index 554f9c25e..edb6936f4 100644 --- a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java +++ b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java @@ -3,38 +3,9 @@ import org.commonmark.node.*; import org.junit.jupiter.api.Test; -import java.util.ArrayList; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; public class AbstractVisitorTest { - @Test - public void maxDepthMustBeZeroOrGreater() { - assertThatThrownBy(() -> new RecordingVisitor(-1)) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void maxDepthZeroVisitsOnlyRoot() { - var paragraph = paragraphTree(); - var visitor = new RecordingVisitor(0); - - paragraph.accept(visitor); - - assertThat(visitor.visited).containsExactly("paragraph"); - } - - @Test - public void maxDepthOneVisitsDirectChildrenButNotGrandchildren() { - var paragraph = paragraphTree(); - var visitor = new RecordingVisitor(1); - - paragraph.accept(visitor); - - assertThat(visitor.visited).containsExactly("paragraph", "emphasis", "text:tail"); - } @Test public void replacingNodeInVisitorShouldNotDestroyVisitOrder() { @@ -63,39 +34,4 @@ private static void assertCode(String expectedLiteral, Node node) { Code code = (Code) node; assertThat(code.getLiteral()).isEqualTo(expectedLiteral); } - - private static Paragraph paragraphTree() { - var paragraph = new Paragraph(); - var emphasis = new Emphasis(); - emphasis.appendChild(new Text("nested")); - paragraph.appendChild(emphasis); - paragraph.appendChild(new Text("tail")); - return paragraph; - } - - private static final class RecordingVisitor extends AbstractVisitor { - private final List visited = new ArrayList<>(); - - private RecordingVisitor(int maxDepth) { - super(maxDepth); - } - - @Override - public void visit(Paragraph paragraph) { - visited.add("paragraph"); - super.visit(paragraph); - } - - @Override - public void visit(Emphasis emphasis) { - visited.add("emphasis"); - super.visit(emphasis); - } - - @Override - public void visit(Text text) { - visited.add("text:" + text.getLiteral()); - super.visit(text); - } - } } diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index 6164759ec..5550df225 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -206,6 +206,55 @@ public void maxOpenBlockParsersAlsoLimitsMixedListAndBlockQuoteNesting() { assertThat(deepestParagraph.getNext()).isNull(); } + @Test + public void maxOpenBlockParsersAlignsWithVisitorDepthAtDocumentRoot() { + var parser = Parser.builder().maxOpenBlockParsers(5).build(); + + var document = parser.parse(String.join("\n", + "- level1", + " > level2", + " > > level3", + " > > > level4")); + var listBlock = document.getFirstChild(); + var listItem = listBlock.getFirstChild(); + var blockQuote1 = listItem.getLastChild(); + var blockQuote2 = blockQuote1.getLastChild(); + var deepestParagraph = blockQuote2.getLastChild(); + var depthFiveVisitor = new RecordingVisitor(5); + var unlimitedVisitor = new RecordingVisitor(); + + assertThat(depth(deepestParagraph.getFirstChild())).isEqualTo(6); + assertThat(depth(deepestParagraph.getLastChild())).isEqualTo(6); + + document.accept(depthFiveVisitor); + document.accept(unlimitedVisitor); + + assertThat(depthFiveVisitor.visited).containsExactly( + "document", + "bulletList", + "listItem", + "paragraph", + "text:level1", + "blockQuote", + "paragraph", + "text:level2", + "blockQuote", + "paragraph"); + assertThat(unlimitedVisitor.visited).containsExactly( + "document", + "bulletList", + "listItem", + "paragraph", + "text:level1", + "blockQuote", + "paragraph", + "text:level2", + "blockQuote", + "paragraph", + "text:level3", + "text:> level4"); + } + private String firstText(Node n) { while (!(n instanceof Text)) { assertThat(n).isNotNull(); @@ -244,4 +293,60 @@ private String alternatingNestedList(int levels) { } return String.join("\n", lines); } + + private int depth(Node node) { + int depth = 0; + while (node.getParent() != null) { + node = node.getParent(); + depth++; + } + return depth; + } + + private static final class RecordingVisitor extends AbstractVisitor { + private final List visited = new ArrayList<>(); + + private RecordingVisitor() { + } + + private RecordingVisitor(int maxDepth) { + super(maxDepth); + } + + @Override + public void visit(Document document) { + visited.add("document"); + super.visit(document); + } + + @Override + public void visit(BulletList bulletList) { + visited.add("bulletList"); + super.visit(bulletList); + } + + @Override + public void visit(ListItem listItem) { + visited.add("listItem"); + super.visit(listItem); + } + + @Override + public void visit(BlockQuote blockQuote) { + visited.add("blockQuote"); + super.visit(blockQuote); + } + + @Override + public void visit(Paragraph paragraph) { + visited.add("paragraph"); + super.visit(paragraph); + } + + @Override + public void visit(Text text) { + visited.add("text:" + text.getLiteral()); + super.visit(text); + } + } } From 2378c7cbe03e2801063e84eea37d6b873b154417 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Tue, 31 Mar 2026 13:00:29 +1100 Subject: [PATCH 77/89] Apply suggestion from @robinst Co-authored-by: Robin Stocker --- commonmark/src/main/java/org/commonmark/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commonmark/src/main/java/org/commonmark/parser/Parser.java b/commonmark/src/main/java/org/commonmark/parser/Parser.java index dc4decf28..8faac789b 100644 --- a/commonmark/src/main/java/org/commonmark/parser/Parser.java +++ b/commonmark/src/main/java/org/commonmark/parser/Parser.java @@ -204,7 +204,7 @@ public Builder includeSourceSpans(IncludeSourceSpans includeSourceSpans) { } /** - * Limit how many non-document block parsers may be open at once while parsing. + * Limit how many block parsers may be open at once while parsing. *

    * Once the limit is reached, additional block starts are treated as plain text instead of * creating deeper nested block structure. From bef429e670479a13af66b2ef74bb39094dd53014 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Tue, 31 Mar 2026 13:01:27 +1100 Subject: [PATCH 78/89] review --- .../src/test/java/org/commonmark/test/ParserTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index 5550df225..89df86407 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -2,9 +2,8 @@ import org.commonmark.node.*; import org.commonmark.parser.*; -import org.commonmark.parser.block.*; import org.commonmark.renderer.html.HtmlRenderer; -import org.commonmark.renderer.text.TextContentRenderer; +import org.commonmark.renderer.markdown.MarkdownRenderer; import org.commonmark.testutil.TestResources; import org.junit.jupiter.api.Test; @@ -16,8 +15,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -279,7 +276,7 @@ private Paragraph deepestStructuredParagraph(Node document, int levels) { } private String renderText(Node node) { - return TextContentRenderer.builder().build().render(node).trim(); + return MarkdownRenderer.builder().build().render(node).trim(); } private String alternatingNestedList(int levels) { From b9dd2d5747aa8d04070811b098e2032cf9556ecd Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Tue, 31 Mar 2026 13:08:56 +1100 Subject: [PATCH 79/89] remove stale test --- .../java/org/commonmark/test/ParserTest.java | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index 89df86407..2427347ea 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -203,55 +203,6 @@ public void maxOpenBlockParsersAlsoLimitsMixedListAndBlockQuoteNesting() { assertThat(deepestParagraph.getNext()).isNull(); } - @Test - public void maxOpenBlockParsersAlignsWithVisitorDepthAtDocumentRoot() { - var parser = Parser.builder().maxOpenBlockParsers(5).build(); - - var document = parser.parse(String.join("\n", - "- level1", - " > level2", - " > > level3", - " > > > level4")); - var listBlock = document.getFirstChild(); - var listItem = listBlock.getFirstChild(); - var blockQuote1 = listItem.getLastChild(); - var blockQuote2 = blockQuote1.getLastChild(); - var deepestParagraph = blockQuote2.getLastChild(); - var depthFiveVisitor = new RecordingVisitor(5); - var unlimitedVisitor = new RecordingVisitor(); - - assertThat(depth(deepestParagraph.getFirstChild())).isEqualTo(6); - assertThat(depth(deepestParagraph.getLastChild())).isEqualTo(6); - - document.accept(depthFiveVisitor); - document.accept(unlimitedVisitor); - - assertThat(depthFiveVisitor.visited).containsExactly( - "document", - "bulletList", - "listItem", - "paragraph", - "text:level1", - "blockQuote", - "paragraph", - "text:level2", - "blockQuote", - "paragraph"); - assertThat(unlimitedVisitor.visited).containsExactly( - "document", - "bulletList", - "listItem", - "paragraph", - "text:level1", - "blockQuote", - "paragraph", - "text:level2", - "blockQuote", - "paragraph", - "text:level3", - "text:> level4"); - } - private String firstText(Node n) { while (!(n instanceof Text)) { assertThat(n).isNotNull(); From 0308e3494a8945e370b6b0833ac72346d923ff32 Mon Sep 17 00:00:00 2001 From: Spirit Wolf Date: Tue, 31 Mar 2026 13:13:55 +1100 Subject: [PATCH 80/89] compile --- .../java/org/commonmark/test/ParserTest.java | 51 +------------------ 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java index 2427347ea..337196c56 100644 --- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java +++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java @@ -173,7 +173,7 @@ public void maxOpenBlockParsersDegradesTheNinthLogicalListLevelToPlainText() { var document = parser.parse(alternatingNestedList(9)); var deepestParagraph = deepestStructuredParagraph(document, 8); - assertThat(renderText(deepestParagraph)).isEqualTo("level8\n- level9"); + assertThat(renderText(deepestParagraph)).isEqualTo("level8\n\\- level9"); assertThat(deepestParagraph.getNext()).isNull(); } @@ -199,7 +199,7 @@ public void maxOpenBlockParsersAlsoLimitsMixedListAndBlockQuoteNesting() { var deepestParagraph = blockQuote2.getLastChild(); assertThat(deepestParagraph).isInstanceOf(Paragraph.class); - assertThat(renderText(deepestParagraph)).isEqualTo("level3\n> level4"); + assertThat(renderText(deepestParagraph)).isEqualTo("level3\n\\> level4"); assertThat(deepestParagraph.getNext()).isNull(); } @@ -250,51 +250,4 @@ private int depth(Node node) { } return depth; } - - private static final class RecordingVisitor extends AbstractVisitor { - private final List visited = new ArrayList<>(); - - private RecordingVisitor() { - } - - private RecordingVisitor(int maxDepth) { - super(maxDepth); - } - - @Override - public void visit(Document document) { - visited.add("document"); - super.visit(document); - } - - @Override - public void visit(BulletList bulletList) { - visited.add("bulletList"); - super.visit(bulletList); - } - - @Override - public void visit(ListItem listItem) { - visited.add("listItem"); - super.visit(listItem); - } - - @Override - public void visit(BlockQuote blockQuote) { - visited.add("blockQuote"); - super.visit(blockQuote); - } - - @Override - public void visit(Paragraph paragraph) { - visited.add("paragraph"); - super.visit(paragraph); - } - - @Override - public void visit(Text text) { - visited.add("text:" + text.getLiteral()); - super.visit(text); - } - } } From 61200f9c27cb7b8706b0754ca958a5822bdf801b Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 31 Mar 2026 14:04:40 +1100 Subject: [PATCH 81/89] Update commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java --- .../commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java | 1 + 1 file changed, 1 insertion(+) 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 index b524adc05..8008fc8dd 100644 --- 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 @@ -87,6 +87,7 @@ private void tryConvertToAlert(BlockQuote blockQuote) { // 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 From e2e27557e475588652902bb261ad0bb8f3be094e Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 31 Mar 2026 14:20:29 +1100 Subject: [PATCH 82/89] README: Add section about alerts --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index a917167f6..b9e09a1a9 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,19 @@ Enables tables using pipes as in [GitHub Flavored Markdown][gfm-tables]. Use class `TablesExtension` in artifact `commonmark-ext-gfm-tables`. +### Alerts + +Adds support for GitHub-style alerts (also known as callouts or admonitions) as described [here](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts), e.g.: + +``` +> [!NOTE] +> The text of the note. +``` + +As types you can use NOTE, TIP, IMPORTANT, WARNING, CAUTION; or configure the extension to add additional ones. + +Use class `AlertsExtension` in artifact `commonmark-ext-gfm-alerts`. + ### Footnotes Enables footnotes like in [GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes) From 66210650c3bee8c3129fb4239edf93d902206d20 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 31 Mar 2026 14:27:24 +1100 Subject: [PATCH 83/89] Add alerts extension to integration tests --- commonmark-integration-test/pom.xml | 4 ++++ .../src/test/java/org/commonmark/integration/Extensions.java | 2 ++ pom.xml | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 3433b42d4..8e52dd383 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -28,6 +28,10 @@ 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/Extensions.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java index 8df0408cb..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 @@ -4,6 +4,7 @@ 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; @@ -19,6 +20,7 @@ public class Extensions { FootnotesExtension.create(), ImageAttributesExtension.create(), InsExtension.create(), + AlertsExtension.create(), StrikethroughExtension.create(), TablesExtension.create(), TaskListItemsExtension.create(), diff --git a/pom.xml b/pom.xml index 58c3ead2c..1c9a53f28 100644 --- a/pom.xml +++ b/pom.xml @@ -155,6 +155,11 @@ commonmark-ext-ins 0.27.2-SNAPSHOT + + org.commonmark + commonmark-ext-gfm-alerts + 0.27.2-SNAPSHOT + org.commonmark commonmark-ext-gfm-strikethrough From 873a861938a28653002549a1c418a1ff4b519657 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 31 Mar 2026 14:20:37 +1100 Subject: [PATCH 84/89] Prepare CHANGELOG for version 0.28.0 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b973b700f..399071347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. +## [0.28.0] - 2026-03-31 +### Added +- New extension for alerts (aka callouts/admonitions) + - Syntax: + ``` + > [!NOTE] + > The text of the note. + ``` + - As types you can use NOTE, TIP, IMPORTANT, WARNING, CAUTION; or configure the + extension to add additional ones. + - Use class `AlertsExtension` in artifact `commonmark-ext-gfm-alerts` (#420) +- New option `maxOpenBlockParsers` for `Parser.Builder` to set an overall limit + for the depth of block parsing. If set, any nesting beyond the limit will be + parsed as paragraph text instead. The default remains unlimited. + ## [0.27.1] - 2026-01-14 ### Fixed - Line(s) after a hard line break would sometimes also get an unwanted hard @@ -518,6 +533,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. +[0.28.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0 [0.27.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.0...commonmark-parent-0.27.1 [0.27.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.26.0...commonmark-parent-0.27.0 [0.26.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.25.1...commonmark-parent-0.26.0 From d278947053b678c639bc888d5410cb507853cf59 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 31 Mar 2026 14:31:56 +1100 Subject: [PATCH 85/89] mvn versions:set -DnewVersion=0.28.0-SNAPSHOT --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-alerts/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 26 ++++++++++++------------ 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index a99fd0b8c..79d8db07a 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 8bb88d74a..14a4a9290 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-alerts/pom.xml b/commonmark-ext-gfm-alerts/pom.xml index 5235af6b2..7780e4e2d 100644 --- a/commonmark-ext-gfm-alerts/pom.xml +++ b/commonmark-ext-gfm-alerts/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-gfm-alerts diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index f6cedc69a..8c9a6b296 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 4e94f623c..37ead1ce6 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index 707948a10..c2be557e7 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 456ac04a2..19ea007a9 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index a06c27b29..7288b397b 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 0fc164672..bdd592d78 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.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 860cfbac7..85b3ad219 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.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 8e52dd383..6b1d76a65 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 699adb3de..063b8af46 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 76dad6c83..650a5562a 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index 1c9a53f28..ace89ff32 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -133,62 +133,62 @@ org.commonmark commonmark - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-ins - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-gfm-alerts - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT org.commonmark commonmark-test-util - 0.27.2-SNAPSHOT + 0.28.0-SNAPSHOT From 9e30657b678fd41b29ed3a957c3dd6a92fb445e1 Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Tue, 31 Mar 2026 03:40:37 +0000 Subject: [PATCH 86/89] [maven-release-plugin] prepare release commonmark-parent-0.28.0 --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-alerts/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 28 ++++++++++++------------ 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 79d8db07a..818ccf4fb 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index 14a4a9290..a81dcbfd1 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-alerts/pom.xml b/commonmark-ext-gfm-alerts/pom.xml index 7780e4e2d..4e5b4e1cc 100644 --- a/commonmark-ext-gfm-alerts/pom.xml +++ b/commonmark-ext-gfm-alerts/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-gfm-alerts diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 8c9a6b296..3b7afb3b1 100644 --- a/commonmark-ext-gfm-strikethrough/pom.xml +++ b/commonmark-ext-gfm-strikethrough/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index 37ead1ce6..ed4b5276b 100644 --- a/commonmark-ext-gfm-tables/pom.xml +++ b/commonmark-ext-gfm-tables/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index c2be557e7..a5cb099cf 100644 --- a/commonmark-ext-heading-anchor/pom.xml +++ b/commonmark-ext-heading-anchor/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 19ea007a9..07415ec8c 100644 --- a/commonmark-ext-image-attributes/pom.xml +++ b/commonmark-ext-image-attributes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 7288b397b..0a41c4dae 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index bdd592d78..7f96168ef 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.28.0-SNAPSHOT + 0.28.0 commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 85b3ad219..83185bad3 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.28.0-SNAPSHOT + 0.28.0 commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index 6b1d76a65..e50de5bc6 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 063b8af46..542eedc2e 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 650a5562a..673479fd4 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark diff --git a/pom.xml b/pom.xml index ace89ff32..c43a5aed5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.28.0-SNAPSHOT + 0.28.0 commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -133,62 +133,62 @@ org.commonmark commonmark - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-autolink - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-footnotes - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-image-attributes - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-ins - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-gfm-alerts - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-gfm-strikethrough - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-gfm-tables - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-heading-anchor - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-task-list-items - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-ext-yaml-front-matter - 0.28.0-SNAPSHOT + 0.28.0 org.commonmark commonmark-test-util - 0.28.0-SNAPSHOT + 0.28.0 @@ -315,7 +315,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - HEAD + commonmark-parent-0.28.0 From ba867e1e63fcabfb08511ce868707f0c1f20f453 Mon Sep 17 00:00:00 2001 From: "Robin Stocker (GitHub Actions)" <16778+robinst@users.noreply.github.com> Date: Tue, 31 Mar 2026 03:40:38 +0000 Subject: [PATCH 87/89] [maven-release-plugin] prepare for next development iteration --- commonmark-ext-autolink/pom.xml | 2 +- commonmark-ext-footnotes/pom.xml | 2 +- commonmark-ext-gfm-alerts/pom.xml | 2 +- commonmark-ext-gfm-strikethrough/pom.xml | 2 +- commonmark-ext-gfm-tables/pom.xml | 2 +- commonmark-ext-heading-anchor/pom.xml | 2 +- commonmark-ext-image-attributes/pom.xml | 2 +- commonmark-ext-ins/pom.xml | 2 +- commonmark-ext-task-list-items/pom.xml | 2 +- commonmark-ext-yaml-front-matter/pom.xml | 2 +- commonmark-integration-test/pom.xml | 2 +- commonmark-test-util/pom.xml | 2 +- commonmark/pom.xml | 2 +- pom.xml | 28 ++++++++++++------------ 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/commonmark-ext-autolink/pom.xml b/commonmark-ext-autolink/pom.xml index 818ccf4fb..2cc4d53ca 100644 --- a/commonmark-ext-autolink/pom.xml +++ b/commonmark-ext-autolink/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark-ext-autolink diff --git a/commonmark-ext-footnotes/pom.xml b/commonmark-ext-footnotes/pom.xml index a81dcbfd1..0d9e2f30c 100644 --- a/commonmark-ext-footnotes/pom.xml +++ b/commonmark-ext-footnotes/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark-ext-footnotes diff --git a/commonmark-ext-gfm-alerts/pom.xml b/commonmark-ext-gfm-alerts/pom.xml index 4e5b4e1cc..02ecbf802 100644 --- a/commonmark-ext-gfm-alerts/pom.xml +++ b/commonmark-ext-gfm-alerts/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark-ext-gfm-alerts diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml index 3b7afb3b1..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.28.0 + 0.28.1-SNAPSHOT commonmark-ext-gfm-strikethrough diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml index ed4b5276b..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.28.0 + 0.28.1-SNAPSHOT commonmark-ext-gfm-tables diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml index a5cb099cf..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.28.0 + 0.28.1-SNAPSHOT commonmark-ext-heading-anchor diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml index 07415ec8c..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.28.0 + 0.28.1-SNAPSHOT commonmark-ext-image-attributes diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml index 0a41c4dae..48481c073 100644 --- a/commonmark-ext-ins/pom.xml +++ b/commonmark-ext-ins/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark-ext-ins diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml index 7f96168ef..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.28.0 + 0.28.1-SNAPSHOT commonmark-ext-task-list-items diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml index 83185bad3..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.28.0 + 0.28.1-SNAPSHOT commonmark-ext-yaml-front-matter diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml index e50de5bc6..7e0048a73 100644 --- a/commonmark-integration-test/pom.xml +++ b/commonmark-integration-test/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark-integration-test diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml index 542eedc2e..6a9c342cc 100644 --- a/commonmark-test-util/pom.xml +++ b/commonmark-test-util/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark-test-util diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 673479fd4..4e060edaa 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -4,7 +4,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark diff --git a/pom.xml b/pom.xml index c43a5aed5..f12805316 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.commonmark commonmark-parent - 0.28.0 + 0.28.1-SNAPSHOT commonmark-java parent Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted @@ -133,62 +133,62 @@ org.commonmark commonmark - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-autolink - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-footnotes - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-image-attributes - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-ins - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-gfm-alerts - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-gfm-strikethrough - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-gfm-tables - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-heading-anchor - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-task-list-items - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-ext-yaml-front-matter - 0.28.0 + 0.28.1-SNAPSHOT org.commonmark commonmark-test-util - 0.28.0 + 0.28.1-SNAPSHOT @@ -315,7 +315,7 @@ scm:git:https://github.com/commonmark/commonmark-java scm:git:https://github.com/commonmark/commonmark-java https://github.com/commonmark/commonmark-java - commonmark-parent-0.28.0 + HEAD From 41586ddabdee9d455c1b8b7c816bd5f0f86647a5 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 31 Mar 2026 14:52:18 +1100 Subject: [PATCH 88/89] Bump versions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b9e09a1a9..845226729 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Coordinates for core library (see all on [Maven Central]): org.commonmark commonmark - 0.27.1 + 0.28.0 ``` @@ -291,7 +291,7 @@ First, add an additional dependency (see [Maven Central] for others): org.commonmark commonmark-ext-gfm-tables - 0.27.1 + 0.28.0 ``` From 8df50617946609975b21f3e791c7d0712cb0b34c Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Fri, 10 Apr 2026 15:51:59 +1000 Subject: [PATCH 89/89] Allow customizing HTML attributes for alert title --- CHANGELOG.md | 5 +++++ .../ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 399071347..9c5c67268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), with the exception that 0.x versions can break between minor versions. +## [Unreleased] +### Added +- Allow customizing HTML attributes for alert title `

    ` tag via `AttributeProvider` + ## [0.28.0] - 2026-03-31 ### Added - New extension for alerts (aka callouts/admonitions) @@ -533,6 +537,7 @@ API breaking changes (caused by changes in spec): Initial release of commonmark-java, a port of commonmark.js with extensions for autolinking URLs, GitHub flavored strikethrough and tables. +[Unreleased]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.28.0...main [0.28.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0 [0.27.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.0...commonmark-parent-0.27.1 [0.27.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.26.0...commonmark-parent-0.27.0 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 index 763b2e217..ca562ba33 100644 --- 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 @@ -34,7 +34,7 @@ protected void renderAlert(Alert alert) { htmlWriter.line(); // Render alert title - htmlWriter.tag("p", Map.of("class", "markdown-alert-title")); + htmlWriter.tag("p", context.extendAttributes(alert, "p", Map.of("class", "markdown-alert-title"))); htmlWriter.text(getAlertTitle(type)); htmlWriter.tag("/p"); htmlWriter.line();