diff --git a/flexmark-ext-docusaurus-admonition/pom.xml b/flexmark-ext-docusaurus-admonition/pom.xml new file mode 100644 index 000000000..9bf2130bd --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + com.vladsch.flexmark + flexmark-java + 0.64.8 + + + flexmark-ext-docusaurus-admonition + flexmark-java extension for Docusaurus admonition syntax + flexmark-java extension for docusaurus admonition syntax + + + + com.vladsch.flexmark + flexmark-util + + + com.vladsch.flexmark + flexmark + + + com.vladsch.flexmark + flexmark-test-util + test + + + com.vladsch.flexmark + flexmark-ext-tables + test + + + com.vladsch.flexmark + flexmark-core-test + test + + + + \ No newline at end of file diff --git a/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlock.java b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlock.java new file mode 100644 index 000000000..f8f9725a1 --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlock.java @@ -0,0 +1,48 @@ +package com.vladsch.flexmark.ext.docusaurus.admonition; + +import com.vladsch.flexmark.util.ast.Block; +import com.vladsch.flexmark.util.ast.BlockContent; +import com.vladsch.flexmark.util.sequence.BasedSequence; + +class DocusaurusAdmonitionBlock extends Block { + private BasedSequence openingMarker = BasedSequence.NULL; + private BasedSequence type = BasedSequence.NULL; + private BasedSequence closingMarker = BasedSequence.NULL; + private final BlockContent content = new BlockContent(); + + public DocusaurusAdmonitionBlock() { + } + + @Override + public BasedSequence[] getSegments() { + return new BasedSequence[]{openingMarker, type, closingMarker}; + } + + public BasedSequence getOpeningMarker() { + return this.openingMarker; + } + + public BasedSequence getType() { + return this.type; + } + + public BasedSequence getClosingMarker() { + return this.closingMarker; + } + + public BlockContent getContent() { + return this.content; + } + + public void setOpeningMarker(BasedSequence openingMarker) { + this.openingMarker = openingMarker; + } + + public void setType(BasedSequence type) { + this.type = type; + } + + public void setClosingMarker(BasedSequence closingMarker) { + this.closingMarker = closingMarker; + } +} diff --git a/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockParser.java b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockParser.java new file mode 100644 index 000000000..b71a6ef9d --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockParser.java @@ -0,0 +1,118 @@ +package com.vladsch.flexmark.ext.docusaurus.admonition; + +import com.vladsch.flexmark.parser.block.*; +import com.vladsch.flexmark.util.ast.Block; +import com.vladsch.flexmark.util.ast.BlockContent; +import com.vladsch.flexmark.util.data.DataHolder; +import com.vladsch.flexmark.util.sequence.BasedSequence; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +class DocusaurusAdmonitionBlockParser extends AbstractBlockParser { + private final DocusaurusAdmonitionBlock block = new DocusaurusAdmonitionBlock(); + private BlockContent content = new BlockContent(); + private boolean hadClose = false; + + private final String closingLine; + + public DocusaurusAdmonitionBlockParser(BasedSequence openingMarker, BasedSequence type, int nestingLevel) { + block.setOpeningMarker(openingMarker); + block.setType(type); + closingLine = ":".repeat(nestingLevel); + } + + @Override + public Block getBlock() { + return block; + } + + @Override + public BlockContinue tryContinue(ParserState state) { + + if (hadClose) { + return BlockContinue.none(); + } + + BasedSequence line = state.getLine(); + + boolean isClosingLine = line.matches(closingLine); + + if (isClosingLine) { + block.setClosingMarker(line); + hadClose = true; + return BlockContinue.atIndex(state.getLineEndIndex()); + } + + return BlockContinue.atIndex(state.getIndex()); + } + + @Override + public boolean isContainer() { + return true; // can contain other blocks + } + + @Override + public boolean canContain(ParserState state, BlockParser blockParser, Block block) { + return true; // can contain anything + } + + @Override + public void addLine(ParserState state, BasedSequence line) { + content.add(line, state.getIndent()); + + } + + @Override + public void closeBlock(ParserState state) { + block.setContent(content); + block.setCharsFromContent(); + + content = null; + } + + public static class Factory implements CustomBlockParserFactory { + @Override + public @NotNull BlockParserFactory apply(@NotNull DataHolder options) { + return new BlockFactory(options); + } + + @Override + public Set> getAfterDependents() { + return null; + } + + @Override + public Set> getBeforeDependents() { + return null; + } + + @Override + public boolean affectsGlobalScope() { + return false; + } + } + + private static class BlockFactory extends AbstractBlockParserFactory { + BlockFactory(DataHolder options) { + super(options); + } + + @Override + public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { + BasedSequence line = state.getLine(); + if (line.startsWith(":::")) { + int colons = 0; + while (colons < line.length() && line.charAt(colons) == ':') { + colons++; + } + BasedSequence type = line.subSequence(colons).trim(); + if (!type.isEmpty() && !type.toString().contains(" ")) { + return BlockStart.of(new DocusaurusAdmonitionBlockParser(line.subSequence(0, colons), type, colons)) + .atIndex(line.length()); + } + } + return BlockStart.none(); + } + } +} diff --git a/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionExtension.java b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionExtension.java new file mode 100644 index 000000000..fccab493d --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionExtension.java @@ -0,0 +1,33 @@ +package com.vladsch.flexmark.ext.docusaurus.admonition; + +import com.vladsch.flexmark.html.HtmlRenderer; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.data.MutableDataHolder; +import org.jetbrains.annotations.NotNull; + +public class DocusaurusAdmonitionExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension { + + public static DocusaurusAdmonitionExtension create() { + return new DocusaurusAdmonitionExtension(); + } + + @Override + public void extend(Parser.Builder parserBuilder) { + parserBuilder.customBlockParserFactory(new DocusaurusAdmonitionBlockParser.Factory()); + } + + @Override + public void extend(HtmlRenderer.@NotNull Builder rendererBuilder, String rendererType) { + if (rendererType.equals("HTML")) { + rendererBuilder.nodeRendererFactory(new DocusaurusAdmonitionNodeRenderer.Factory()); + } + } + + @Override + public void rendererOptions(@NotNull MutableDataHolder options) { + } + + @Override + public void parserOptions(MutableDataHolder options) { + } +} diff --git a/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionNodeRenderer.java b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionNodeRenderer.java new file mode 100644 index 000000000..3a767e5c7 --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/src/main/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionNodeRenderer.java @@ -0,0 +1,41 @@ +package com.vladsch.flexmark.ext.docusaurus.admonition; + +import com.vladsch.flexmark.html.HtmlWriter; +import com.vladsch.flexmark.html.renderer.NodeRenderer; +import com.vladsch.flexmark.html.renderer.NodeRendererContext; +import com.vladsch.flexmark.html.renderer.NodeRendererFactory; +import com.vladsch.flexmark.html.renderer.NodeRenderingHandler; +import com.vladsch.flexmark.util.data.DataHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +class DocusaurusAdmonitionNodeRenderer implements NodeRenderer { + + @Override + public Set> getNodeRenderingHandlers() { + Set> set = new HashSet<>(); + set.add(new NodeRenderingHandler<>(DocusaurusAdmonitionBlock.class, this::render)); + return set; + } + + private void render(DocusaurusAdmonitionBlock node, NodeRendererContext context, HtmlWriter html) { + + String type = node.getType().toString(); + + html.attr("class", "docusaurus-admonition docusaurus-admonition-" + type); + html.withAttr().tag("div"); + + context.renderChildren(node); + + html.tag("/div"); + } + + public static class Factory implements NodeRendererFactory { + @Override + public @NotNull NodeRenderer apply(@NotNull DataHolder options) { + return new DocusaurusAdmonitionNodeRenderer(); + } + } +} diff --git a/flexmark-ext-docusaurus-admonition/src/main/javadoc/overview.md b/flexmark-ext-docusaurus-admonition/src/main/javadoc/overview.md new file mode 100644 index 000000000..a9c509b22 --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/src/main/javadoc/overview.md @@ -0,0 +1,35 @@ +# flexmark-ext-docusaurus-admonition + +Extension for [Docusaurus Admonitions](https://docusaurus.io/docs/markdown-features/admonitions). + +This extension renders Docusaurus admonitions into following HTML: + +```html +
+ +
+``` + +Please note that `-type` in class name is replaced with actual type of the admonition eg. `note`, `tip` etc. + +Nested admonitions are supported. + +Custom admonitions are supported by replacing `-type` in class with custom adminition type. It means that the following Markdown: + +```md + +:::my-custom-admonition +Text here. +::: + +``` + +Will be rendered as: + +```html +
+

Text here.

+
+``` + +Admonitions' titles are not supported now. diff --git a/flexmark-ext-docusaurus-admonition/src/test/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockHtmlRendererTest.java b/flexmark-ext-docusaurus-admonition/src/test/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockHtmlRendererTest.java new file mode 100644 index 000000000..cefa3602b --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/src/test/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockHtmlRendererTest.java @@ -0,0 +1,19 @@ +package com.vladsch.flexmark.ext.docusaurus.admonition; + +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Document; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; + +public class DocusaurusAdmonitionBlockHtmlRendererTest { + + @Test + public void checkParsing() { + Parser parser = Parser.builder().extensions(Collections.singleton(DocusaurusAdmonitionExtension.create())).build(); + + Document document = parser.parse(":::info\nThis is info\n:::"); + Assert.assertTrue(document.getFirstChild() instanceof DocusaurusAdmonitionBlock); + } +} diff --git a/flexmark-ext-docusaurus-admonition/src/test/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockParserTest.java b/flexmark-ext-docusaurus-admonition/src/test/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockParserTest.java new file mode 100644 index 000000000..5e8e3d6c9 --- /dev/null +++ b/flexmark-ext-docusaurus-admonition/src/test/java/com/vladsch/flexmark/ext/docusaurus/admonition/DocusaurusAdmonitionBlockParserTest.java @@ -0,0 +1,32 @@ +package com.vladsch.flexmark.ext.docusaurus.admonition; + +import com.vladsch.flexmark.html.HtmlRenderer; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.data.MutableDataSet; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class DocusaurusAdmonitionBlockParserTest { + + @Test + public void checkParsing() { + MutableDataSet options = new MutableDataSet(); + options.set(Parser.EXTENSIONS, List.of( + DocusaurusAdmonitionExtension.create() + )); + + Parser parser = Parser.builder(options).build(); + HtmlRenderer renderer = HtmlRenderer.builder(options).build(); + + String markdownInput = ":::info\nThis is info\n:::"; + + // Act + String result = renderer.render(parser.parse(markdownInput)); + + // Assert + String expectedHtml = "
\n

This is info

\n
\n"; + Assert.assertEquals(expectedHtml, result); + } +} diff --git a/pom.xml b/pom.xml index 3a2a62832..8e318d91a 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ flexmark-core-test flexmark-ext-abbreviation flexmark-ext-admonition + flexmark-ext-docusaurus-admonition flexmark-ext-anchorlink flexmark-ext-aside flexmark-ext-attributes