diff --git a/README.md b/README.md index bcf552c5b0cb9bc1f7f7da1dfa994471b18f6b6a..25df264ef0497e5b2b58e79cc7118def41d6ae2d 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,14 @@ Java library for parsing and rendering [Markdown] text according to the Introduction ------------ -Provides classes for parsing input to an abstract syntax tree of nodes -(AST), visiting and manipulating nodes, and rendering to HTML. It -started out as a port of [commonmark.js], but has since evolved into a -full library with a nice API and the following features: +Provides classes for parsing input to an abstract syntax tree (AST), +visiting and manipulating nodes, and rendering to HTML or back to Markdown. +It started out as a port of [commonmark.js], but has since evolved into an +extensible library with the following features: * Small (core has no dependencies, extensions in separate artifacts) -* Fast (10-20 times faster than pegdown, see benchmarks in repo) +* Fast (10-20 times faster than [pegdown] which used to be a popular Markdown + library, see benchmarks in repo) * Flexible (manipulate the AST after parsing, customize HTML rendering) * Extensible (tables, strikethrough, autolinking and more, see below) @@ -63,9 +64,9 @@ import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; Parser parser = Parser.builder().build(); -Node document = parser.parse("This is *Sparta*"); +Node document = parser.parse("This is *Markdown*"); HtmlRenderer renderer = HtmlRenderer.builder().build(); -renderer.render(document); // "<p>This is <em>Sparta</em></p>\n" +renderer.render(document); // "<p>This is <em>Markdown</em></p>\n" ``` This uses the parser and renderer with default options. Both builders have @@ -81,8 +82,23 @@ to which tags are allowed, etc. That is the responsibility of the caller, and if you expose the resulting HTML, you probably want to run a sanitizer on it after this. -For rendering to plain text, there's also a `TextContentRenderer` with -a very similar API. +#### Render to Markdown + +```java +import org.commonmark.node.*; +import org.commonmark.renderer.markdown.MarkdownRenderer; + +MarkdownRenderer renderer = MarkdownRenderer.builder().build(); +Node document = new Document(); +Heading heading = new Heading(); +heading.setLevel(2); +heading.appendChild(new Text("My title")); +document.appendChild(heading); + +renderer.render(document); // "## My title\n" +``` + +For rendering to plain text with minimal markup, there's also `TextContentRenderer`. #### Use a visitor to process parsed nodes @@ -390,6 +406,7 @@ BSD (2-clause) licensed, see LICENSE.txt file. [CommonMark]: https://commonmark.org/ [Markdown]: https://daringfireball.net/projects/markdown/ [commonmark.js]: https://github.com/commonmark/commonmark.js +[pegdown]: https://github.com/sirthias/pegdown [CommonMark Dingus]: https://spec.commonmark.org/dingus/ [Maven Central]: https://search.maven.org/#search|ga|1|g%3A%22org.commonmark%22 [Semantic Versioning]: https://semver.org/ diff --git a/commonmark/pom.xml b/commonmark/pom.xml index 64c45619261cde44b797a98c86711ceb658140bc..9988370a8b0e214ed539486efc5accf37cc4edd1 100644 --- a/commonmark/pom.xml +++ b/commonmark/pom.xml @@ -9,7 +9,7 @@ <artifactId>commonmark</artifactId> <name>commonmark-java core</name> - <description>Core of commonmark-java (implementation of CommonMark for parsing markdown and rendering to HTML)</description> + <description>Core of commonmark-java (a library for parsing Markdown to an AST, modifying the AST and rendering it to HTML or Markdown)</description> <dependencies> <dependency> diff --git a/commonmark/src/main/java/org/commonmark/package-info.java b/commonmark/src/main/java/org/commonmark/package-info.java index e784703e9e2a8d90780a70d363024c952e5c828d..b683017f6e986b61817c905329031e933dcc5898 100644 --- a/commonmark/src/main/java/org/commonmark/package-info.java +++ b/commonmark/src/main/java/org/commonmark/package-info.java @@ -4,6 +4,7 @@ * <li>{@link org.commonmark.parser} for parsing input text to AST nodes</li> * <li>{@link org.commonmark.node} for AST node types and visitors</li> * <li>{@link org.commonmark.renderer.html} for HTML rendering</li> + * <li>{@link org.commonmark.renderer.markdown} for Markdown rendering</li> * </ul> */ package org.commonmark; diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java index 5805c458b0ee4b38a2aaf7386ef013050767fd32..40640d1b44024c69a465fafcc5f8c119295d87ec 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java +++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java @@ -4,6 +4,9 @@ import org.commonmark.node.Node; import java.util.Set; +/** + * Context that is passed to custom node renderers, see {@link MarkdownNodeRendererFactory#create}. + */ public interface MarkdownNodeRendererContext { /** diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java index adfe8a07b7cd7f2cb5f1676202eaf0400bea791e..14221ea568faa37fe932b45c18821caf0989e445 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java +++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java @@ -5,7 +5,7 @@ import org.commonmark.renderer.NodeRenderer; import java.util.Set; /** - * Factory for instantiating new node renderers ƒor rendering. + * Factory for instantiating new node renderers for rendering custom nodes. */ public interface MarkdownNodeRendererFactory { 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 926105202e8f20c3a36e5481c7ed62604ed66f05..25fa9a142c6b62e9c56ba80f8f6409cb854d929d 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java @@ -9,14 +9,17 @@ import org.commonmark.renderer.Renderer; import java.util.*; /** - * Renders nodes to CommonMark Markdown. + * Renders nodes to Markdown (CommonMark syntax); use {@link #builder()} to create a renderer. * <p> - * Note that it does not currently attempt to preserve the exact syntax of the original input Markdown (if any): + * Note that it doesn't currently preserve the exact syntax of the original input Markdown (if any): * <ul> * <li>Headings are output as ATX headings if possible (multi-line headings need Setext headings)</li> * <li>Escaping might be over-eager, e.g. a plain {@code *} might be escaped * even though it doesn't need to be in that particular context</li> + * <li>Leading whitespace in paragraphs is not preserved</li> * </ul> + * However, it should produce Markdown that is semantically equivalent to the input, i.e. if the Markdown was parsed + * again and compared against the original AST, it should be the same (minus bugs). */ public class MarkdownRenderer implements Renderer { @@ -66,7 +69,7 @@ public class MarkdownRenderer implements Renderer { */ public static class Builder { - private List<MarkdownNodeRendererFactory> nodeRendererFactories = new ArrayList<>(); + private final List<MarkdownNodeRendererFactory> nodeRendererFactories = new ArrayList<>(); /** * @return the configured {@link MarkdownRenderer} @@ -106,9 +109,15 @@ public class MarkdownRenderer implements Renderer { } /** - * Extension for {@link MarkdownRenderer}. + * Extension for {@link MarkdownRenderer} for rendering custom nodes. */ public interface MarkdownRendererExtension extends Extension { + + /** + * Extend Markdown rendering, usually by registering custom node renderers using {@link Builder#nodeRendererFactory}. + * + * @param rendererBuilder the renderer builder to extend + */ void extend(Builder rendererBuilder); } diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/package-info.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..f707671d502a69157db0ff7f97d7b2ded35fd699 --- /dev/null +++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/package-info.java @@ -0,0 +1,4 @@ +/** + * Markdown rendering (see {@link org.commonmark.renderer.markdown.MarkdownRenderer}) + */ +package org.commonmark.renderer.markdown; diff --git a/commonmark/src/main/java/org/commonmark/renderer/text/package-info.java b/commonmark/src/main/java/org/commonmark/renderer/text/package-info.java index 07a5580918e441e5115a98437d1e46d73624cdc9..8309f4bd618ff48eee5e8db47d095a8232627307 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/text/package-info.java +++ b/commonmark/src/main/java/org/commonmark/renderer/text/package-info.java @@ -1,4 +1,4 @@ /** - * Text content rendering (see {@link org.commonmark.renderer.text.TextContentRenderer}) + * Plain text rendering with minimal markup (see {@link org.commonmark.renderer.text.TextContentRenderer}) */ package org.commonmark.renderer.text; diff --git a/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java index 9ff646630645d4566107108f9f936bce2447eaf4..08235965a4c86345cb27641ac48d6d34d6469cb3 100644 --- a/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java +++ b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java @@ -4,6 +4,7 @@ import org.commonmark.node.*; import org.commonmark.parser.Parser; import org.commonmark.renderer.NodeRenderer; import org.commonmark.renderer.html.*; +import org.commonmark.renderer.markdown.MarkdownRenderer; import org.junit.Ignore; import org.junit.Test; @@ -22,9 +23,21 @@ public class UsageExampleTest { @Test public void parseAndRender() { Parser parser = Parser.builder().build(); - Node document = parser.parse("This is *Sparta*"); + Node document = parser.parse("This is *Markdown*"); HtmlRenderer renderer = HtmlRenderer.builder().escapeHtml(true).build(); - assertEquals("<p>This is <em>Sparta</em></p>\n", renderer.render(document)); + assertEquals("<p>This is <em>Markdown</em></p>\n", renderer.render(document)); + } + + @Test + public void renderToMarkdown() { + MarkdownRenderer renderer = MarkdownRenderer.builder().build(); + Node document = new Document(); + Heading heading = new Heading(); + heading.setLevel(2); + heading.appendChild(new Text("My title")); + document.appendChild(heading); + + assertEquals("## My title\n", renderer.render(document)); } @Test