From 7db535466a86298366d5657883f14413e7d2b15b Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Sun, 23 Mar 2025 22:23:43 +1100 Subject: [PATCH] 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 fb412397..62831d7b 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 95ac6ce4..c74f9075 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 0f8559f3..e4996fb0 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 c47a6dde..36a5b528 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);