From 614abc159d31ae0b1ef46540f838c76d2aad2a9c Mon Sep 17 00:00:00 2001 From: Tomasz Jedrzejewski Date: Sat, 1 Aug 2026 14:19:51 +0200 Subject: [PATCH 1/2] Add support for different YAML front matter extractors in commonmark-ext-yaml-front-matter --- README.md | 34 ++++++- .../front/matter/YamlFrontMatterContent.java | 19 ++++ .../matter/YamlFrontMatterExtension.java | 31 +++++- .../matter/YamlFrontMatterExtractor.java | 13 +++ .../front/matter/YamlFrontMatterVisitor.java | 63 +++++++++++- .../extractor/YamlContentExtractor.java | 32 +++++++ .../matter/extractor/YamlDataExtractor.java | 96 +++++++++++++++++++ .../internal/YamlFrontMatterBlockParser.java | 86 +++-------------- .../YamlFrontMatterMarkdownNodeRenderer.java | 13 ++- .../matter/YamlFrontMatterContentTest.java | 95 ++++++++++++++++++ ...Test.java => YamlFrontMatterDataTest.java} | 77 +++++++++------ ...rontMatterMarkdownRendererContentTest.java | 36 +++++++ ...lFrontMatterMarkdownRendererDataTest.java} | 28 ++---- ...mlFrontMatterMarkdownRendererTestCase.java | 28 ++++++ .../front/matter/YamlFrontMatterTestCase.java | 36 +++++++ 15 files changed, 558 insertions(+), 129 deletions(-) create mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterContent.java create mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java create mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java create mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlDataExtractor.java create mode 100644 commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterContentTest.java rename commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/{YamlFrontMatterTest.java => YamlFrontMatterDataTest.java} (79%) create mode 100644 commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererContentTest.java rename commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/{YamlFrontMatterMarkdownRendererTest.java => YamlFrontMatterMarkdownRendererDataTest.java} (88%) create mode 100644 commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererTestCase.java create mode 100644 commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java diff --git a/README.md b/README.md index 8ee0b0119..ed5618864 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,8 @@ Use class `InsExtension` in artifact `commonmark-ext-ins`. ### YAML front matter -Adds support for metadata through a YAML front matter block. This extension only supports a subset of YAML syntax. Here's an example of what's supported: +Adds support for metadata through a YAML front matter block. The extension uses a built-in parser that supports a subset +of YAML syntax, suitable for simple use cases. Here's an example of what's supported: ```markdown --- @@ -407,7 +408,36 @@ literal: | document start here ``` -Use class `YamlFrontMatterExtension` in artifact `commonmark-ext-yaml-front-matter`. To fetch metadata, use `YamlFrontMatterVisitor`. +Use class `YamlFrontMatterExtension` in artifact `commonmark-ext-yaml-front-matter`. To fetch metadata, use `YamlFrontMatterVisitor`: + +```java +import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; + +List extensions = List.of(YamlFrontMatterExtension.create()); +Parser parser = Parser.builder() + .extensions(extensions) + .build(); + +Node document = parser.parse(markdownDocument); +Map> frontMatter = YamlFrontMatterVisitor.readData(document); +``` + +Alternatively, you can use initialize the extension with `YamlContentExtractor` that saves the YAML front matter content +as a string for further processing with other tools: + +```java +import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; + +List extensions = List.of(YamlFrontMatterExtension.create(new YamlContentExtractor.Factory())); +Parser parser = Parser.builder() + .extensions(extensions) + .build(); + +Node document = parser.parse(markdownDocument); +String frontMatter = YamlFrontMatterVisitor.readContent(document); +``` + +You can also write a custom extractor by implementing `YamlFrontMatterExtractor` interface. ### Image Attributes diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterContent.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterContent.java new file mode 100644 index 000000000..92ef418bf --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterContent.java @@ -0,0 +1,19 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.node.CustomNode; + +public class YamlFrontMatterContent extends CustomNode { + private String content; + + public YamlFrontMatterContent(String content) { + this.content = content; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java index 9bb1b4528..6f8f86d29 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java @@ -1,9 +1,13 @@ package org.commonmark.ext.front.matter; +import java.util.Objects; import java.util.Set; import org.commonmark.Extension; +import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.ext.front.matter.extractor.YamlDataExtractor; import org.commonmark.ext.front.matter.internal.YamlFrontMatterBlockParser; import org.commonmark.ext.front.matter.internal.YamlFrontMatterMarkdownNodeRenderer; +import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.commonmark.renderer.NodeRenderer; import org.commonmark.renderer.html.HtmlRenderer; @@ -18,21 +22,38 @@ * org.commonmark.parser.Parser.Builder#extensions(Iterable)}, {@link * HtmlRenderer.Builder#extensions(Iterable)}). * - *

The parsed metadata is turned into {@link YamlFrontMatterNode}. You can access the metadata - * using {@link YamlFrontMatterVisitor}. + *

By default, the extension parses the subset of YAML with a built-int parser. + * The parsed metadata is turned into {@link YamlFrontMatterNode}. You can access + * the metadata using {@link YamlFrontMatterVisitor#readData(Node)}. + * + *

Alternatively, you can create the extension with {@link YamlContentExtractor.Factory}. + * It turns the YAML front matter into {@link YamlFrontMatterContent} node, which stores + * the front matter content as a simple string. You can access the content with + * {@link YamlFrontMatterVisitor#readContent(Node)} to process it with other tools. + * + *

To create a custom YAML front matter extractor, implement {@link YamlFrontMatterExtractor} + * interface and the corresponding factory. */ public class YamlFrontMatterExtension implements Parser.ParserExtension, MarkdownRenderer.MarkdownRendererExtension { - private YamlFrontMatterExtension() {} + private final YamlFrontMatterExtractor.Factory yamlExtractorFactory; + + private YamlFrontMatterExtension(YamlFrontMatterExtractor.Factory yamlExtractorFactory) { + this.yamlExtractorFactory = Objects.requireNonNull(yamlExtractorFactory); + } @Override public void extend(Parser.Builder parserBuilder) { - parserBuilder.customBlockParserFactory(new YamlFrontMatterBlockParser.Factory()); + parserBuilder.customBlockParserFactory(new YamlFrontMatterBlockParser.Factory(yamlExtractorFactory)); } public static Extension create() { - return new YamlFrontMatterExtension(); + return create(new YamlDataExtractor.Factory()); + } + + public static Extension create(YamlFrontMatterExtractor.Factory extractor) { + return new YamlFrontMatterExtension(extractor); } @Override diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java new file mode 100644 index 000000000..7213f61f9 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java @@ -0,0 +1,13 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.parser.block.BlockContinue; + +public interface YamlFrontMatterExtractor { + void onNextLine(YamlFrontMatterBlock block, CharSequence line); + + BlockContinue onBlockEnd(YamlFrontMatterBlock block); + + interface Factory { + YamlFrontMatterExtractor create(); + } +} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java index e49e22c0d..a58022edd 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java @@ -3,14 +3,48 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + +import org.commonmark.ext.front.matter.extractor.YamlDataExtractor; +import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; import org.commonmark.node.AbstractVisitor; import org.commonmark.node.CustomNode; +import org.commonmark.node.Node; public class YamlFrontMatterVisitor extends AbstractVisitor { + private boolean present; private Map> data; + private String content; public YamlFrontMatterVisitor() { data = new LinkedHashMap<>(); + content = ""; + present = false; + } + + /** + * Reads the YAML front matter metadata, if the Markdown + * document has the YAML front matter and the extension + * uses {@link YamlDataExtractor} (default). + * + * @return The data stored in YAML front matter or empty map + */ + public static Map> readData(Node document) { + YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); + document.accept(visitor); + return visitor.getData(); + } + + /** + * Reads the YAML Front Matter metadata as a string, if the Markdown + * document has the YAML Front Matter and the extension uses + * {@link YamlContentExtractor} (default). + * + * @return The content of YAML front matter as string or empty string. + */ + public static String readContent(Node document) { + YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); + document.accept(visitor); + return visitor.getContent(); } @Override @@ -18,13 +52,40 @@ public void visit(CustomNode customNode) { if (customNode instanceof YamlFrontMatterNode) { data.put( ((YamlFrontMatterNode) customNode).getKey(), - ((YamlFrontMatterNode) customNode).getValues()); + ((YamlFrontMatterNode) customNode).getValues() + ); + present = true; + } else if (customNode instanceof YamlFrontMatterContent) { + content = ((YamlFrontMatterContent) customNode).getContent(); + present = true; } else { super.visit(customNode); } } + /** + * Returns the YAML front matter metadata, if the Markdown document has + * the YAML front matter and the extension uses {@link YamlDataExtractor} + * (default). + * + * @return The data stored in YAML front matter or empty map + */ public Map> getData() { return data; } + + /** + * Returns the YAML Front Matter metadata as a string, if the Markdown + * document has the YAML Front Matter and the extension uses + * {@link YamlContentExtractor} (default). + * + * @return The content of YAML front matter as string or empty string. + */ + public String getContent() { + return content; + } + + public boolean isFrontMatterPresent() { + return present; + } } diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java new file mode 100644 index 000000000..ab23f4981 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java @@ -0,0 +1,32 @@ +package org.commonmark.ext.front.matter.extractor; + +import org.commonmark.ext.front.matter.YamlFrontMatterExtractor; +import org.commonmark.ext.front.matter.YamlFrontMatterBlock; +import org.commonmark.ext.front.matter.YamlFrontMatterContent; +import org.commonmark.parser.block.BlockContinue; + +public class YamlContentExtractor implements YamlFrontMatterExtractor { + private StringBuilder content; + + public YamlContentExtractor() { + content = new StringBuilder(); + } + + @Override + public void onNextLine(YamlFrontMatterBlock block, CharSequence line) { + content.append(line).append('\n'); + } + + @Override + public BlockContinue onBlockEnd(YamlFrontMatterBlock block) { + block.appendChild(new YamlFrontMatterContent(content.toString())); + return BlockContinue.finished(); + } + + public static class Factory implements YamlFrontMatterExtractor.Factory { + @Override + public YamlFrontMatterExtractor create() { + return new YamlContentExtractor(); + } + } +} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlDataExtractor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlDataExtractor.java new file mode 100644 index 000000000..940940474 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlDataExtractor.java @@ -0,0 +1,96 @@ +package org.commonmark.ext.front.matter.extractor; + +import org.commonmark.ext.front.matter.YamlFrontMatterExtractor; +import org.commonmark.ext.front.matter.YamlFrontMatterBlock; +import org.commonmark.ext.front.matter.YamlFrontMatterNode; +import org.commonmark.parser.block.BlockContinue; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class YamlDataExtractor implements YamlFrontMatterExtractor { + private static final Pattern REGEX_METADATA = + Pattern.compile("^[ ]{0,3}([A-Za-z0-9._-]+):\\s*(.*)"); + private static final Pattern REGEX_METADATA_LIST = Pattern.compile("^[ ]+-\\s*(.*)"); + private static final Pattern REGEX_METADATA_LITERAL = Pattern.compile("^\\s*(.*)"); + + private boolean inLiteral; + private String currentKey; + private List currentValues; + + public YamlDataExtractor() { + inLiteral = false; + currentKey = null; + currentValues = new ArrayList<>(); + } + + @Override + public void onNextLine(YamlFrontMatterBlock block, CharSequence line) { + Matcher matcher = REGEX_METADATA.matcher(line); + if (matcher.matches()) { + if (currentKey != null) { + block.appendChild(new YamlFrontMatterNode(currentKey, currentValues)); + } + + inLiteral = false; + currentKey = matcher.group(1); + currentValues = new ArrayList<>(); + String value = matcher.group(2); + if ("|".equals(value)) { + inLiteral = true; + } else if (!"".equals(value)) { + currentValues.add(parseString(value)); + } + } else { + if (inLiteral) { + matcher = REGEX_METADATA_LITERAL.matcher(line); + if (matcher.matches()) { + if (currentValues.size() == 1) { + currentValues.set(0, currentValues.get(0) + "\n" + matcher.group(1).trim()); + } else { + currentValues.add(matcher.group(1).trim()); + } + } + } else { + matcher = REGEX_METADATA_LIST.matcher(line); + if (matcher.matches()) { + String value = matcher.group(1); + currentValues.add(parseString(value)); + } + } + } + } + + @Override + public BlockContinue onBlockEnd(YamlFrontMatterBlock block) { + if (currentKey != null) { + block.appendChild(new YamlFrontMatterNode(currentKey, currentValues)); + } + return BlockContinue.finished(); + } + + private static String parseString(String s) { + // Limited parsing of https://yaml.org/spec/1.2.2/#73-flow-scalar-styles + // We assume input is well-formed and otherwise treat it as a plain string. In a real + // parser, e.g. `'foo` would be invalid because it's missing a trailing `'`. + if (s.startsWith("'") && s.endsWith("'")) { + String inner = s.substring(1, s.length() - 1); + return inner.replace("''", "'"); + } else if (s.startsWith("\"") && s.endsWith("\"")) { + String inner = s.substring(1, s.length() - 1); + // Only support escaped `\` and `"`, nothing else. + return inner.replace("\\\"", "\"").replace("\\\\", "\\"); + } else { + return s; + } + } + + public static class Factory implements YamlFrontMatterExtractor.Factory { + @Override + public YamlFrontMatterExtractor create() { + return new YamlDataExtractor(); + } + } +} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java index 7d14b403b..da4d7c033 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java @@ -1,32 +1,22 @@ package org.commonmark.ext.front.matter.internal; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; import java.util.regex.Pattern; + +import org.commonmark.ext.front.matter.YamlFrontMatterExtractor; import org.commonmark.ext.front.matter.YamlFrontMatterBlock; -import org.commonmark.ext.front.matter.YamlFrontMatterNode; import org.commonmark.node.Block; import org.commonmark.node.Document; import org.commonmark.parser.block.*; public class YamlFrontMatterBlockParser extends AbstractBlockParser { - private static final Pattern REGEX_METADATA = - Pattern.compile("^[ ]{0,3}([A-Za-z0-9._-]+):\\s*(.*)"); - private static final Pattern REGEX_METADATA_LIST = Pattern.compile("^[ ]+-\\s*(.*)"); - private static final Pattern REGEX_METADATA_LITERAL = Pattern.compile("^\\s*(.*)"); private static final Pattern REGEX_BEGIN = Pattern.compile("^-{3}(\\s.*)?"); private static final Pattern REGEX_END = Pattern.compile("^(-{3}|\\.{3})(\\s.*)?"); - private boolean inLiteral; - private String currentKey; - private List currentValues; private YamlFrontMatterBlock block; + private YamlFrontMatterExtractor extractor; - public YamlFrontMatterBlockParser() { - inLiteral = false; - currentKey = null; - currentValues = new ArrayList<>(); + public YamlFrontMatterBlockParser(YamlFrontMatterExtractor extractor) { + this.extractor = extractor; block = new YamlFrontMatterBlock(); } @@ -40,68 +30,20 @@ public BlockContinue tryContinue(ParserState parserState) { final CharSequence line = parserState.getLine().getContent(); if (REGEX_END.matcher(line).matches()) { - if (currentKey != null) { - block.appendChild(new YamlFrontMatterNode(currentKey, currentValues)); - } - return BlockContinue.finished(); + return extractor.onBlockEnd(block); } - Matcher matcher = REGEX_METADATA.matcher(line); - if (matcher.matches()) { - if (currentKey != null) { - block.appendChild(new YamlFrontMatterNode(currentKey, currentValues)); - } - - inLiteral = false; - currentKey = matcher.group(1); - currentValues = new ArrayList<>(); - String value = matcher.group(2); - if ("|".equals(value)) { - inLiteral = true; - } else if (!"".equals(value)) { - currentValues.add(parseString(value)); - } - - return BlockContinue.atIndex(parserState.getIndex()); - } else { - if (inLiteral) { - matcher = REGEX_METADATA_LITERAL.matcher(line); - if (matcher.matches()) { - if (currentValues.size() == 1) { - currentValues.set(0, currentValues.get(0) + "\n" + matcher.group(1).trim()); - } else { - currentValues.add(matcher.group(1).trim()); - } - } - } else { - matcher = REGEX_METADATA_LIST.matcher(line); - if (matcher.matches()) { - String value = matcher.group(1); - currentValues.add(parseString(value)); - } - } - - return BlockContinue.atIndex(parserState.getIndex()); - } + extractor.onNextLine(block, line); + return BlockContinue.atIndex(parserState.getIndex()); } - private static String parseString(String s) { - // Limited parsing of https://yaml.org/spec/1.2.2/#73-flow-scalar-styles - // We assume input is well-formed and otherwise treat it as a plain string. In a real - // parser, e.g. `'foo` would be invalid because it's missing a trailing `'`. - if (s.startsWith("'") && s.endsWith("'")) { - String inner = s.substring(1, s.length() - 1); - return inner.replace("''", "'"); - } else if (s.startsWith("\"") && s.endsWith("\"")) { - String inner = s.substring(1, s.length() - 1); - // Only support escaped `\` and `"`, nothing else. - return inner.replace("\\\"", "\"").replace("\\\\", "\\"); - } else { - return s; + public static class Factory extends AbstractBlockParserFactory { + private YamlFrontMatterExtractor.Factory yamlExtractorFactory; + + public Factory(YamlFrontMatterExtractor.Factory factory) { + this.yamlExtractorFactory = factory; } - } - public static class Factory extends AbstractBlockParserFactory { @Override public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { CharSequence line = state.getLine().getContent(); @@ -110,7 +52,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar if (parentParser.getBlock() instanceof Document && parentParser.getBlock().getFirstChild() == null && REGEX_BEGIN.matcher(line).matches()) { - return BlockStart.of(new YamlFrontMatterBlockParser()) + return BlockStart.of(new YamlFrontMatterBlockParser(yamlExtractorFactory.create())) .atIndex(state.getNextNonSpaceIndex()); } diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java index 355596c8e..4ae184897 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java @@ -1,13 +1,14 @@ package org.commonmark.ext.front.matter.internal; import java.util.List; + +import org.commonmark.ext.front.matter.YamlFrontMatterContent; import org.commonmark.ext.front.matter.YamlFrontMatterNode; import org.commonmark.node.Node; import org.commonmark.renderer.markdown.MarkdownNodeRendererContext; import org.commonmark.renderer.markdown.MarkdownWriter; public class YamlFrontMatterMarkdownNodeRenderer extends YamlFrontMatterNodeRenderer { - private final MarkdownWriter writer; public YamlFrontMatterMarkdownNodeRenderer(MarkdownNodeRendererContext context) { @@ -21,6 +22,8 @@ public void render(Node node) { while (child != null) { if (child instanceof YamlFrontMatterNode) { renderNode((YamlFrontMatterNode) child); + } else if (child instanceof YamlFrontMatterContent) { + renderContent((YamlFrontMatterContent) child); } child = child.getNext(); } @@ -113,4 +116,12 @@ private boolean isFlowCollection(String value) { return (value.startsWith("[") && value.endsWith("]")) || (value.startsWith("{") && value.endsWith("}")); } + + private void renderContent(YamlFrontMatterContent content) { + String body = content.getContent(); + writer.raw(body); + if (!body.endsWith("\n")) { + writer.raw('\n'); + } + } } diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterContentTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterContentTest.java new file mode 100644 index 000000000..5b7d735f9 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterContentTest.java @@ -0,0 +1,95 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.Extension; +import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.node.Node; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +public class YamlFrontMatterContentTest extends YamlFrontMatterTestCase { + private static final Set EXTENSIONS = Set.of( + YamlFrontMatterExtension.create(new YamlContentExtractor.Factory()) + ); + + @Override + Set getExtensions() { + return EXTENSIONS; + } + + @Test + public void frontMatterAsStringContent() { + final String input = "---" + "\n first: foo" + "\n second: bar" + "\n..." + "\n" + "\ngreat"; + final String rendered = "

great

\n"; + + String content = getFrontMatterContent(input); + + assertThat(content).isEqualTo(" first: foo\n" + " second: bar\n"); + assertRendering(input, rendered); + } + + @Test + public void indentedDashesDoNotTerminateFrontMatter() { + final String input = "---" + "\n first: foo" + "\n second: |" + "\n ---" + "\n third: bar" + "\n..." + "\n" + "\ngreat"; + final String rendered = "

great

\n"; + + String content = getFrontMatterContent(input); + + assertThat(content).isEqualTo(" first: foo\n" + " second: |\n" + " ---\n" + " third: bar\n"); + assertRendering(input, rendered); + } + + @Test + public void contentNodeCanBeModified() { + final String input = "---" + "\nhello: world" + "\n---" + "\n"; + + Node document = parser.parse(input); + YamlFrontMatterContent contentNode = (YamlFrontMatterContent) document.getFirstChild().getFirstChild(); + + contentNode.setContent("see: you\n"); + + YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); + document.accept(visitor); + + String content = visitor.getContent(); + assertThat(visitor.isFrontMatterPresent()).isTrue(); + assertThat(content).isEqualTo("see: you\n"); + } + + @Test + public void dataNodesNotPresent() { + final String input = "---" + "\nhello: world" + "\n..." + "\n" + "\ngreat"; + + Map> data = getFrontMatterData(input); + + assertThat(data).isEmpty(); + } + + @Test + public void frontMatterPresent() { + final String input = "---" + "\nhello: world" + "\n..." + "\n" + "\ngreat"; + + Node document = parser.parse(input); + YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); + document.accept(visitor); + + assertThat(visitor.getContent()).isNotEmpty(); + assertThat(visitor.isFrontMatterPresent()).isTrue(); + } + + @Test + public void frontMatterNotPresent() { + final String input = "great!"; + + Node document = parser.parse(input); + YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); + document.accept(visitor); + + assertThat(visitor.getContent()).isEmpty(); + assertThat(visitor.isFrontMatterPresent()).isFalse(); + } +} 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/YamlFrontMatterDataTest.java similarity index 79% rename from commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java rename to commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterDataTest.java index 71da8a936..6ead2c48b 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/YamlFrontMatterDataTest.java @@ -8,23 +8,22 @@ import org.commonmark.Extension; import org.commonmark.node.CustomNode; import org.commonmark.node.Node; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; -import org.commonmark.testutil.RenderingTestCase; import org.junit.jupiter.api.Test; -public class YamlFrontMatterTest extends RenderingTestCase { +public class YamlFrontMatterDataTest extends YamlFrontMatterTestCase { private static final Set EXTENSIONS = Set.of(YamlFrontMatterExtension.create()); - private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build(); - private static final HtmlRenderer RENDERER = - HtmlRenderer.builder().extensions(EXTENSIONS).build(); + + @Override + Set getExtensions() { + return EXTENSIONS; + } @Test public void simpleValue() { final String input = "---" + "\nhello: world" + "\n..." + "\n" + "\ngreat"; final String rendered = "

great

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(1); assertThat(data.keySet().iterator().next()).isEqualTo("hello"); @@ -39,7 +38,7 @@ public void emptyValue() { final String input = "---" + "\nkey:" + "\n---" + "\n" + "\ngreat"; final String rendered = "

great

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(1); assertThat(data.keySet().iterator().next()).isEqualTo("key"); @@ -54,7 +53,7 @@ public void listValues() { "---" + "\nlist:" + "\n - value1" + "\n - value2" + "\n..." + "\n" + "\ngreat"; final String rendered = "

great

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(1); assertThat(data).containsKey("list"); @@ -77,7 +76,7 @@ public void literalValue1() { + "\ngreat"; final String rendered = "

great

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(1); assertThat(data).containsKey("literal"); @@ -93,7 +92,7 @@ public void literalValue2() { "---" + "\nliteral: |" + "\n - hello markdown!" + "\n---" + "\n" + "\ngreat"; final String rendered = "

great

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(1); assertThat(data).containsKey("literal"); @@ -119,7 +118,7 @@ public void complexValues() { + "\ngreat"; final String rendered = "

great

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(3); @@ -144,7 +143,7 @@ public void empty() { final String input = "---\n" + "---\n" + "test"; final String rendered = "

test

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).isEmpty(); @@ -158,7 +157,7 @@ public void yamlInParagraph() { final String rendered = "

hello

\n

hello markdown world!

\n

hello: world

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).isEmpty(); @@ -170,7 +169,7 @@ public void yamlOnSecondLine() { final String input = "hello\n" + "\n---" + "\nhello: world" + "\n---"; final String rendered = "

hello

\n
\n

hello: world

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).isEmpty(); @@ -182,7 +181,7 @@ public void nonMatchedStartTag() { final String input = "----\n" + "test"; final String rendered = "
\n

test

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).isEmpty(); @@ -194,7 +193,7 @@ public void inList() { final String input = "* ---\n" + " ---\n" + "test"; final String rendered = "
    \n
  • \n
    \n
    \n
  • \n
\n

test

\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).isEmpty(); @@ -206,7 +205,7 @@ public void visitorIgnoresOtherCustomNodes() { final String input = "---" + "\nhello: world" + "\n---" + "\n"; YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); - Node document = PARSER.parse(input); + Node document = parser.parse(input); document.appendChild(new TestNode()); document.accept(visitor); @@ -220,7 +219,7 @@ public void visitorIgnoresOtherCustomNodes() { public void nodesCanBeModified() { final String input = "---" + "\nhello: world" + "\n---" + "\n"; - Node document = PARSER.parse(input); + Node document = parser.parse(input); YamlFrontMatterNode node = (YamlFrontMatterNode) document.getFirstChild().getFirstChild(); node.setKey("see"); node.setValues(List.of("you")); @@ -238,7 +237,7 @@ public void nodesCanBeModified() { public void dotInKeys() { final String input = "---" + "\nms.author: author" + "\n---" + "\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(1); assertThat(data.keySet().iterator().next()).isEqualTo("ms.author"); @@ -251,7 +250,7 @@ public void singleQuotedLiterals() { final String input = "---" + "\nstring: 'It''s me'" + "\nlist:" + "\n - 'I''m here'" + "\n---" + "\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(2); assertThat(data.get("string").get(0)).isEqualTo("It's me"); @@ -268,24 +267,44 @@ public void doubleQuotedLiteral() { + "\n---" + "\n"; - Map> data = getFrontMatter(input); + Map> data = getFrontMatterData(input); assertThat(data).hasSize(2); assertThat(data.get("string").get(0)).isEqualTo("backslash: \\ quote: \""); assertThat(data.get("list").get(0)).isEqualTo("hey"); } - @Override - protected String render(String source) { - return RENDERER.render(PARSER.parse(source)); + @Test + public void contentNodesNotPresent() { + final String input = "---" + "\nhello: world" + "\n..." + "\n" + "\ngreat"; + + String content = getFrontMatterContent(input); + + assertThat(content).isEmpty(); + } + + @Test + public void frontMatterPresent() { + final String input = "---" + "\nhello: world" + "\n..." + "\n" + "\ngreat"; + + Node document = parser.parse(input); + YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); + document.accept(visitor); + + assertThat(visitor.getData()).isNotEmpty(); + assertThat(visitor.isFrontMatterPresent()).isTrue(); } - private Map> getFrontMatter(String input) { + @Test + public void frontMatterNotPresent() { + final String input = "great!"; + + Node document = parser.parse(input); YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); - Node document = PARSER.parse(input); document.accept(visitor); - return visitor.getData(); + assertThat(visitor.getData()).isEmpty(); + assertThat(visitor.isFrontMatterPresent()).isFalse(); } // Custom node for tests diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererContentTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererContentTest.java new file mode 100644 index 000000000..0c375e801 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererContentTest.java @@ -0,0 +1,36 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.Extension; +import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.node.Node; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +public class YamlFrontMatterMarkdownRendererContentTest extends YamlFrontMatterMarkdownRendererTestCase { + private static final Set EXTENSIONS = Set.of( + YamlFrontMatterExtension.create(new YamlContentExtractor.Factory()) + ); + + @Override + protected Set getExtensions() { + return EXTENSIONS; + } + + @Test + public void testRoundTripSimple() { + assertRoundTrip("---\ntitle: My Document\n---\n\nMarkdown content\n"); + } + + @Test + public void testAppendMissingTrailingNewline() { + final String input = "---" + "\nhello: world" + "\n---" + "\n\ngreat"; + + Node document = parser.parse(input); + YamlFrontMatterContent contentNode = (YamlFrontMatterContent) document.getFirstChild().getFirstChild(); + + contentNode.setContent("see: you "); + + assertRenderedEquals(document, "---" + "\nsee: you " + "\n---" + "\n\ngreat\n"); + } +} diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererDataTest.java similarity index 88% rename from commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererTest.java rename to commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererDataTest.java index b5bd06058..c97c9a9d1 100644 --- a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererTest.java +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererDataTest.java @@ -3,21 +3,21 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.List; +import java.util.Set; + import org.commonmark.Extension; import org.commonmark.node.Document; -import org.commonmark.node.Node; import org.commonmark.node.Paragraph; import org.commonmark.node.Text; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.markdown.MarkdownRenderer; import org.junit.jupiter.api.Test; -public class YamlFrontMatterMarkdownRendererTest { +public class YamlFrontMatterMarkdownRendererDataTest extends YamlFrontMatterMarkdownRendererTestCase { + private static final Set EXTENSIONS = Set.of(YamlFrontMatterExtension.create()); - private static final List EXTENSIONS = List.of(YamlFrontMatterExtension.create()); - private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build(); - private static final MarkdownRenderer RENDERER = - MarkdownRenderer.builder().extensions(EXTENSIONS).build(); + @Override + protected Set getExtensions() { + return EXTENSIONS; + } // ===== Round-trip tests (parse string -> render -> compare to input) ===== @@ -59,7 +59,7 @@ public void testRoundTripDoubleQuotedValue() { * quote, hence why this technically doesn't round-trip. */ var input = "---\nkey: \"value with \\\"double quotes\\\"\"\n---\n\nMarkdown content\n"; - var rendered = RENDERER.render(PARSER.parse(input)); + var rendered = renderer.render(parser.parse(input)); var expected = "---\nkey: 'value with \"double quotes\"'\n---\n\nMarkdown content\n"; assertThat(rendered).isEqualTo(expected); } @@ -201,16 +201,6 @@ public void testValueStartingWithDoubleQuote() { assertRenderedEquals(doc, "---\nkey: '\"quotes within value\"'\n---\n\nMarkdown content\n"); } - private void assertRoundTrip(String input) { - String rendered = RENDERER.render(PARSER.parse(input)); - assertThat(rendered).isEqualTo(input); - } - - private void assertRenderedEquals(Node inputNode, String expectedOutput) { - var renderedOutput = RENDERER.render(inputNode); - assertThat(renderedOutput).isEqualTo(expectedOutput); - } - private Document buildDocumentWithFrontMatter(List frontMatterNodes) { var doc = new Document(); diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererTestCase.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererTestCase.java new file mode 100644 index 000000000..670159f49 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererTestCase.java @@ -0,0 +1,28 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.Extension; +import org.commonmark.node.Node; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.markdown.MarkdownRenderer; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +abstract class YamlFrontMatterMarkdownRendererTestCase { + protected final Parser parser = Parser.builder().extensions(getExtensions()).build(); + protected final MarkdownRenderer renderer = + MarkdownRenderer.builder().extensions(getExtensions()).build(); + + protected abstract Set getExtensions(); + + protected void assertRoundTrip(String input) { + String rendered = renderer.render(parser.parse(input)); + assertThat(rendered).isEqualTo(input); + } + + protected void assertRenderedEquals(Node inputNode, String expectedOutput) { + var renderedOutput = renderer.render(inputNode); + assertThat(renderedOutput).isEqualTo(expectedOutput); + } +} diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java new file mode 100644 index 000000000..6f8e50e08 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java @@ -0,0 +1,36 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.Extension; +import org.commonmark.node.Node; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; +import org.commonmark.testutil.RenderingTestCase; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +abstract class YamlFrontMatterTestCase extends RenderingTestCase { + protected final Parser parser = Parser.builder().extensions(getExtensions()).build(); + protected final HtmlRenderer renderer = + HtmlRenderer.builder().extensions(getExtensions()).build(); + + abstract Set getExtensions(); + + @Override + protected String render(String source) { + return renderer.render(parser.parse(source)); + } + + protected String getFrontMatterContent(String input) { + Node document = parser.parse(input); + + return YamlFrontMatterVisitor.readContent(document); + } + + protected Map> getFrontMatterData(String input) { + Node document = parser.parse(input); + + return YamlFrontMatterVisitor.readData(document); + } +} From 2b264befeb501d1561c1200ccb2333cad8924dd0 Mon Sep 17 00:00:00 2001 From: Tomasz Jedrzejewski Date: Sun, 2 Aug 2026 21:17:00 +0200 Subject: [PATCH 2/2] fix: rework naming and improve FrontMatterParser interface --- README.md | 14 ++-- .../ext/front/matter/FrontMatterParser.java | 33 ++++++++++ .../matter/YamlFrontMatterExtension.java | 26 ++++---- .../matter/YamlFrontMatterExtractor.java | 13 ---- ...nt.java => YamlFrontMatterRawContent.java} | 4 +- .../front/matter/YamlFrontMatterVisitor.java | 37 ++++++----- .../extractor/YamlContentExtractor.java | 32 ---------- .../internal/YamlFrontMatterBlockParser.java | 30 +++++---- .../YamlFrontMatterMarkdownNodeRenderer.java | 8 +-- .../front/matter/parser/RawContentParser.java | 32 ++++++++++ .../YamlSubsetParser.java} | 28 ++++---- .../YamlFrontMatterCustomParserTest.java | 64 +++++++++++++++++++ ...MatterMarkdownRendererRawContentTest.java} | 8 +-- ...ava => YamlFrontMatterRawContentTest.java} | 16 ++--- .../front/matter/YamlFrontMatterTestCase.java | 2 +- 15 files changed, 218 insertions(+), 129 deletions(-) create mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/FrontMatterParser.java delete mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java rename commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/{YamlFrontMatterContent.java => YamlFrontMatterRawContent.java} (72%) delete mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java create mode 100644 commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/parser/RawContentParser.java rename commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/{extractor/YamlDataExtractor.java => parser/YamlSubsetParser.java} (77%) create mode 100644 commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterCustomParserTest.java rename commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/{YamlFrontMatterMarkdownRendererContentTest.java => YamlFrontMatterMarkdownRendererRawContentTest.java} (68%) rename commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/{YamlFrontMatterContentTest.java => YamlFrontMatterRawContentTest.java} (82%) diff --git a/README.md b/README.md index ed5618864..f9a03fb0a 100644 --- a/README.md +++ b/README.md @@ -411,7 +411,7 @@ document start here Use class `YamlFrontMatterExtension` in artifact `commonmark-ext-yaml-front-matter`. To fetch metadata, use `YamlFrontMatterVisitor`: ```java -import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.ext.front.matter.parser.RawContentParser; List extensions = List.of(YamlFrontMatterExtension.create()); Parser parser = Parser.builder() @@ -422,22 +422,22 @@ Node document = parser.parse(markdownDocument); Map> frontMatter = YamlFrontMatterVisitor.readData(document); ``` -Alternatively, you can use initialize the extension with `YamlContentExtractor` that saves the YAML front matter content -as a string for further processing with other tools: +Alternatively, you can use initialize the extension with `RawContentParser` to capture the entire front matter as a +string for further processing with other tools: ```java -import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.ext.front.matter.parser.RawContentParser; -List extensions = List.of(YamlFrontMatterExtension.create(new YamlContentExtractor.Factory())); +List extensions = List.of(YamlFrontMatterExtension.create(new RawContentParser.Factory())); Parser parser = Parser.builder() .extensions(extensions) .build(); Node document = parser.parse(markdownDocument); -String frontMatter = YamlFrontMatterVisitor.readContent(document); +String frontMatter = YamlFrontMatterVisitor.readRawContent(document); ``` -You can also write a custom extractor by implementing `YamlFrontMatterExtractor` interface. +You can also write a custom front matter parser by implementing `FrontMatterParser` interface. ### Image Attributes diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/FrontMatterParser.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/FrontMatterParser.java new file mode 100644 index 000000000..11db91f03 --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/FrontMatterParser.java @@ -0,0 +1,33 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.parser.SourceLine; + +/** + * Parses the content of the front matter block between `---` separators. + * The implementations should add at least 1 child node to + * {@link YamlFrontMatterBlock} to store the result of the parsing. + */ +public interface FrontMatterParser { + void onNextLine(YamlFrontMatterBlock block, SourceLine line); + + /** + * Notifies about finding the line with the ending separator (`---` or `...`, without + * initial whitespace). Advanced parsers may be able to determine that the separator + * is a part of the single-/double-quoted multiline string and return {@link SeparatorRole#CONTENT} + * to include it in the front matter content. In this case, front matter parsing continues until + * finding another separator. To end the parsing, return {@link SeparatorRole#BLOCK_END}. + * + * @param block Main block node + * @param separator `---` or `...` + * @return Separator role: block end or the part of the front matter content + */ + SeparatorRole onEndingSeparator(YamlFrontMatterBlock block, SourceLine separator); + + interface Factory { + FrontMatterParser create(); + } + + enum SeparatorRole { + BLOCK_END, CONTENT + } +} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java index 6f8f86d29..3ffea5bd9 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtension.java @@ -3,8 +3,8 @@ import java.util.Objects; import java.util.Set; import org.commonmark.Extension; -import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; -import org.commonmark.ext.front.matter.extractor.YamlDataExtractor; +import org.commonmark.ext.front.matter.parser.RawContentParser; +import org.commonmark.ext.front.matter.parser.YamlSubsetParser; import org.commonmark.ext.front.matter.internal.YamlFrontMatterBlockParser; import org.commonmark.ext.front.matter.internal.YamlFrontMatterMarkdownNodeRenderer; import org.commonmark.node.Node; @@ -22,24 +22,24 @@ * org.commonmark.parser.Parser.Builder#extensions(Iterable)}, {@link * HtmlRenderer.Builder#extensions(Iterable)}). * - *

By default, the extension parses the subset of YAML with a built-int parser. + *

By default, the extension parses the subset of YAML with a built-int {@link YamlSubsetParser}. * The parsed metadata is turned into {@link YamlFrontMatterNode}. You can access * the metadata using {@link YamlFrontMatterVisitor#readData(Node)}. * - *

Alternatively, you can create the extension with {@link YamlContentExtractor.Factory}. - * It turns the YAML front matter into {@link YamlFrontMatterContent} node, which stores - * the front matter content as a simple string. You can access the content with - * {@link YamlFrontMatterVisitor#readContent(Node)} to process it with other tools. + *

Alternatively, you can create the extension with {@link RawContentParser.Factory}. + * It turns the YAML front matter into {@link YamlFrontMatterRawContent} node, which stores + * the entire front matter as a string. You can access the content with + * {@link YamlFrontMatterVisitor#readRawContent(Node)} to process it with other tools. * - *

To create a custom YAML front matter extractor, implement {@link YamlFrontMatterExtractor} - * interface and the corresponding factory. + *

Implement {@link FrontMatterParser} interface and the corresponding factory to + * parse the front matter with a custom parser. */ public class YamlFrontMatterExtension implements Parser.ParserExtension, MarkdownRenderer.MarkdownRendererExtension { - private final YamlFrontMatterExtractor.Factory yamlExtractorFactory; + private final FrontMatterParser.Factory yamlExtractorFactory; - private YamlFrontMatterExtension(YamlFrontMatterExtractor.Factory yamlExtractorFactory) { + private YamlFrontMatterExtension(FrontMatterParser.Factory yamlExtractorFactory) { this.yamlExtractorFactory = Objects.requireNonNull(yamlExtractorFactory); } @@ -49,10 +49,10 @@ public void extend(Parser.Builder parserBuilder) { } public static Extension create() { - return create(new YamlDataExtractor.Factory()); + return create(new YamlSubsetParser.Factory()); } - public static Extension create(YamlFrontMatterExtractor.Factory extractor) { + public static Extension create(FrontMatterParser.Factory extractor) { return new YamlFrontMatterExtension(extractor); } diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java deleted file mode 100644 index 7213f61f9..000000000 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterExtractor.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.commonmark.ext.front.matter; - -import org.commonmark.parser.block.BlockContinue; - -public interface YamlFrontMatterExtractor { - void onNextLine(YamlFrontMatterBlock block, CharSequence line); - - BlockContinue onBlockEnd(YamlFrontMatterBlock block); - - interface Factory { - YamlFrontMatterExtractor create(); - } -} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterContent.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterRawContent.java similarity index 72% rename from commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterContent.java rename to commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterRawContent.java index 92ef418bf..85b1d1bbd 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterContent.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterRawContent.java @@ -2,10 +2,10 @@ import org.commonmark.node.CustomNode; -public class YamlFrontMatterContent extends CustomNode { +public class YamlFrontMatterRawContent extends CustomNode { private String content; - public YamlFrontMatterContent(String content) { + public YamlFrontMatterRawContent(String content) { this.content = content; } diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java index a58022edd..8864e66ae 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/YamlFrontMatterVisitor.java @@ -4,8 +4,8 @@ import java.util.List; import java.util.Map; -import org.commonmark.ext.front.matter.extractor.YamlDataExtractor; -import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.ext.front.matter.parser.YamlSubsetParser; +import org.commonmark.ext.front.matter.parser.RawContentParser; import org.commonmark.node.AbstractVisitor; import org.commonmark.node.CustomNode; import org.commonmark.node.Node; @@ -23,10 +23,10 @@ public YamlFrontMatterVisitor() { /** * Reads the YAML front matter metadata, if the Markdown - * document has the YAML front matter and the extension - * uses {@link YamlDataExtractor} (default). + * document has the front matter and the extension + * uses {@link YamlSubsetParser} (default). * - * @return The data stored in YAML front matter or empty map + * @return The data stored in YAML front matter or empty map. */ public static Map> readData(Node document) { YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); @@ -35,16 +35,15 @@ public static Map> readData(Node document) { } /** - * Reads the YAML Front Matter metadata as a string, if the Markdown - * document has the YAML Front Matter and the extension uses - * {@link YamlContentExtractor} (default). + * Reads the raw content of the front matter, if the Markdown document has + * the front matter and the extension uses {@link RawContentParser}. * - * @return The content of YAML front matter as string or empty string. + * @return Raw content of the front matter as string or empty string. */ - public static String readContent(Node document) { + public static String readRawContent(Node document) { YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); document.accept(visitor); - return visitor.getContent(); + return visitor.getRawContent(); } @Override @@ -55,8 +54,8 @@ public void visit(CustomNode customNode) { ((YamlFrontMatterNode) customNode).getValues() ); present = true; - } else if (customNode instanceof YamlFrontMatterContent) { - content = ((YamlFrontMatterContent) customNode).getContent(); + } else if (customNode instanceof YamlFrontMatterRawContent) { + content = ((YamlFrontMatterRawContent) customNode).getContent(); present = true; } else { super.visit(customNode); @@ -65,7 +64,7 @@ public void visit(CustomNode customNode) { /** * Returns the YAML front matter metadata, if the Markdown document has - * the YAML front matter and the extension uses {@link YamlDataExtractor} + * the front matter and the extension uses {@link YamlSubsetParser} * (default). * * @return The data stored in YAML front matter or empty map @@ -75,13 +74,13 @@ public Map> getData() { } /** - * Returns the YAML Front Matter metadata as a string, if the Markdown - * document has the YAML Front Matter and the extension uses - * {@link YamlContentExtractor} (default). + * Returns the raw content of the front matter, if the Markdown + * document has the front matter and the extension uses + * {@link RawContentParser}. * - * @return The content of YAML front matter as string or empty string. + * @return Raw content of the front matter as string or empty string. */ - public String getContent() { + public String getRawContent() { return content; } diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java deleted file mode 100644 index ab23f4981..000000000 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlContentExtractor.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.commonmark.ext.front.matter.extractor; - -import org.commonmark.ext.front.matter.YamlFrontMatterExtractor; -import org.commonmark.ext.front.matter.YamlFrontMatterBlock; -import org.commonmark.ext.front.matter.YamlFrontMatterContent; -import org.commonmark.parser.block.BlockContinue; - -public class YamlContentExtractor implements YamlFrontMatterExtractor { - private StringBuilder content; - - public YamlContentExtractor() { - content = new StringBuilder(); - } - - @Override - public void onNextLine(YamlFrontMatterBlock block, CharSequence line) { - content.append(line).append('\n'); - } - - @Override - public BlockContinue onBlockEnd(YamlFrontMatterBlock block) { - block.appendChild(new YamlFrontMatterContent(content.toString())); - return BlockContinue.finished(); - } - - public static class Factory implements YamlFrontMatterExtractor.Factory { - @Override - public YamlFrontMatterExtractor create() { - return new YamlContentExtractor(); - } - } -} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java index da4d7c033..baf33b1b7 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterBlockParser.java @@ -2,10 +2,11 @@ import java.util.regex.Pattern; -import org.commonmark.ext.front.matter.YamlFrontMatterExtractor; +import org.commonmark.ext.front.matter.FrontMatterParser; import org.commonmark.ext.front.matter.YamlFrontMatterBlock; import org.commonmark.node.Block; import org.commonmark.node.Document; +import org.commonmark.parser.SourceLine; import org.commonmark.parser.block.*; public class YamlFrontMatterBlockParser extends AbstractBlockParser { @@ -13,10 +14,10 @@ public class YamlFrontMatterBlockParser extends AbstractBlockParser { private static final Pattern REGEX_END = Pattern.compile("^(-{3}|\\.{3})(\\s.*)?"); private YamlFrontMatterBlock block; - private YamlFrontMatterExtractor extractor; + private FrontMatterParser frontMatterParser; - public YamlFrontMatterBlockParser(YamlFrontMatterExtractor extractor) { - this.extractor = extractor; + public YamlFrontMatterBlockParser(FrontMatterParser frontMatterParser) { + this.frontMatterParser = frontMatterParser; block = new YamlFrontMatterBlock(); } @@ -27,21 +28,26 @@ public Block getBlock() { @Override public BlockContinue tryContinue(ParserState parserState) { - final CharSequence line = parserState.getLine().getContent(); + final SourceLine line = parserState.getLine(); - if (REGEX_END.matcher(line).matches()) { - return extractor.onBlockEnd(block); + if (REGEX_END.matcher(line.getContent()).matches()) { + switch (frontMatterParser.onEndingSeparator(block, line)) { + case BLOCK_END: + return BlockContinue.finished(); + case CONTENT: + return BlockContinue.atIndex(parserState.getIndex()); + } } - extractor.onNextLine(block, line); + frontMatterParser.onNextLine(block, line); return BlockContinue.atIndex(parserState.getIndex()); } public static class Factory extends AbstractBlockParserFactory { - private YamlFrontMatterExtractor.Factory yamlExtractorFactory; + private FrontMatterParser.Factory frontMatterParserFactory; - public Factory(YamlFrontMatterExtractor.Factory factory) { - this.yamlExtractorFactory = factory; + public Factory(FrontMatterParser.Factory factory) { + this.frontMatterParserFactory = factory; } @Override @@ -52,7 +58,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar if (parentParser.getBlock() instanceof Document && parentParser.getBlock().getFirstChild() == null && REGEX_BEGIN.matcher(line).matches()) { - return BlockStart.of(new YamlFrontMatterBlockParser(yamlExtractorFactory.create())) + return BlockStart.of(new YamlFrontMatterBlockParser(frontMatterParserFactory.create())) .atIndex(state.getNextNonSpaceIndex()); } diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java index 4ae184897..618acc969 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/internal/YamlFrontMatterMarkdownNodeRenderer.java @@ -2,7 +2,7 @@ import java.util.List; -import org.commonmark.ext.front.matter.YamlFrontMatterContent; +import org.commonmark.ext.front.matter.YamlFrontMatterRawContent; import org.commonmark.ext.front.matter.YamlFrontMatterNode; import org.commonmark.node.Node; import org.commonmark.renderer.markdown.MarkdownNodeRendererContext; @@ -22,8 +22,8 @@ public void render(Node node) { while (child != null) { if (child instanceof YamlFrontMatterNode) { renderNode((YamlFrontMatterNode) child); - } else if (child instanceof YamlFrontMatterContent) { - renderContent((YamlFrontMatterContent) child); + } else if (child instanceof YamlFrontMatterRawContent) { + renderContent((YamlFrontMatterRawContent) child); } child = child.getNext(); } @@ -117,7 +117,7 @@ private boolean isFlowCollection(String value) { || (value.startsWith("{") && value.endsWith("}")); } - private void renderContent(YamlFrontMatterContent content) { + private void renderContent(YamlFrontMatterRawContent content) { String body = content.getContent(); writer.raw(body); if (!body.endsWith("\n")) { diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/parser/RawContentParser.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/parser/RawContentParser.java new file mode 100644 index 000000000..2d0853b5a --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/parser/RawContentParser.java @@ -0,0 +1,32 @@ +package org.commonmark.ext.front.matter.parser; + +import org.commonmark.ext.front.matter.FrontMatterParser; +import org.commonmark.ext.front.matter.YamlFrontMatterBlock; +import org.commonmark.ext.front.matter.YamlFrontMatterRawContent; +import org.commonmark.parser.SourceLine; + +public class RawContentParser implements FrontMatterParser { + private StringBuilder content; + + public RawContentParser() { + content = new StringBuilder(); + } + + @Override + public void onNextLine(YamlFrontMatterBlock block, SourceLine line) { + content.append(line.getContent()).append('\n'); + } + + @Override + public SeparatorRole onEndingSeparator(YamlFrontMatterBlock block, SourceLine separator) { + block.appendChild(new YamlFrontMatterRawContent(content.toString())); + return SeparatorRole.BLOCK_END; + } + + public static class Factory implements FrontMatterParser.Factory { + @Override + public FrontMatterParser create() { + return new RawContentParser(); + } + } +} diff --git a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlDataExtractor.java b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/parser/YamlSubsetParser.java similarity index 77% rename from commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlDataExtractor.java rename to commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/parser/YamlSubsetParser.java index 940940474..37c07b893 100644 --- a/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/extractor/YamlDataExtractor.java +++ b/commonmark-ext-yaml-front-matter/src/main/java/org/commonmark/ext/front/matter/parser/YamlSubsetParser.java @@ -1,16 +1,16 @@ -package org.commonmark.ext.front.matter.extractor; +package org.commonmark.ext.front.matter.parser; -import org.commonmark.ext.front.matter.YamlFrontMatterExtractor; +import org.commonmark.ext.front.matter.FrontMatterParser; import org.commonmark.ext.front.matter.YamlFrontMatterBlock; import org.commonmark.ext.front.matter.YamlFrontMatterNode; -import org.commonmark.parser.block.BlockContinue; +import org.commonmark.parser.SourceLine; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class YamlDataExtractor implements YamlFrontMatterExtractor { +public class YamlSubsetParser implements FrontMatterParser { private static final Pattern REGEX_METADATA = Pattern.compile("^[ ]{0,3}([A-Za-z0-9._-]+):\\s*(.*)"); private static final Pattern REGEX_METADATA_LIST = Pattern.compile("^[ ]+-\\s*(.*)"); @@ -20,15 +20,15 @@ public class YamlDataExtractor implements YamlFrontMatterExtractor { private String currentKey; private List currentValues; - public YamlDataExtractor() { + public YamlSubsetParser() { inLiteral = false; currentKey = null; currentValues = new ArrayList<>(); } @Override - public void onNextLine(YamlFrontMatterBlock block, CharSequence line) { - Matcher matcher = REGEX_METADATA.matcher(line); + public void onNextLine(YamlFrontMatterBlock block, SourceLine line) { + Matcher matcher = REGEX_METADATA.matcher(line.getContent()); if (matcher.matches()) { if (currentKey != null) { block.appendChild(new YamlFrontMatterNode(currentKey, currentValues)); @@ -45,7 +45,7 @@ public void onNextLine(YamlFrontMatterBlock block, CharSequence line) { } } else { if (inLiteral) { - matcher = REGEX_METADATA_LITERAL.matcher(line); + matcher = REGEX_METADATA_LITERAL.matcher(line.getContent()); if (matcher.matches()) { if (currentValues.size() == 1) { currentValues.set(0, currentValues.get(0) + "\n" + matcher.group(1).trim()); @@ -54,7 +54,7 @@ public void onNextLine(YamlFrontMatterBlock block, CharSequence line) { } } } else { - matcher = REGEX_METADATA_LIST.matcher(line); + matcher = REGEX_METADATA_LIST.matcher(line.getContent()); if (matcher.matches()) { String value = matcher.group(1); currentValues.add(parseString(value)); @@ -64,11 +64,11 @@ public void onNextLine(YamlFrontMatterBlock block, CharSequence line) { } @Override - public BlockContinue onBlockEnd(YamlFrontMatterBlock block) { + public SeparatorRole onEndingSeparator(YamlFrontMatterBlock block, SourceLine separator) { if (currentKey != null) { block.appendChild(new YamlFrontMatterNode(currentKey, currentValues)); } - return BlockContinue.finished(); + return SeparatorRole.BLOCK_END; } private static String parseString(String s) { @@ -87,10 +87,10 @@ private static String parseString(String s) { } } - public static class Factory implements YamlFrontMatterExtractor.Factory { + public static class Factory implements FrontMatterParser.Factory { @Override - public YamlFrontMatterExtractor create() { - return new YamlDataExtractor(); + public FrontMatterParser create() { + return new YamlSubsetParser(); } } } diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterCustomParserTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterCustomParserTest.java new file mode 100644 index 000000000..cac749fef --- /dev/null +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterCustomParserTest.java @@ -0,0 +1,64 @@ +package org.commonmark.ext.front.matter; + +import org.commonmark.Extension; +import org.commonmark.ext.front.matter.parser.RawContentParser; +import org.commonmark.parser.SourceLine; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +public class YamlFrontMatterCustomParserTest extends YamlFrontMatterTestCase { + @Override + Set getExtensions() { + return Set.of(YamlFrontMatterExtension.create(new Factory())); + } + + @Test + public void separatorCanBePartOfContent() { + final String input = "---" + "\nstring: '" + "\n---" + "\n'" + "\n..." + "\n" + "\ngreat"; + final String rendered = "

great

\n"; + + String content = getFrontMatterContent(input); + + assertThat(content).isEqualTo("string: '\n" + "---\n" + "'\n"); + assertRendering(input, rendered); + } + + /** + * Allows testing {@link org.commonmark.ext.front.matter.FrontMatterParser.SeparatorRole#CONTENT} + */ + class CustomParser implements FrontMatterParser { + private StringBuilder content; + private boolean separatorAsContent = false; + + public CustomParser() { + content = new StringBuilder(); + } + + @Override + public void onNextLine(YamlFrontMatterBlock block, SourceLine line) { + content.append(line.getContent()).append('\n'); + } + + @Override + public SeparatorRole onEndingSeparator(YamlFrontMatterBlock block, SourceLine separator) { + if (!separatorAsContent) { + content.append(separator.getContent()).append('\n'); + separatorAsContent = true; + return SeparatorRole.CONTENT; + } else { + block.appendChild(new YamlFrontMatterRawContent(content.toString())); + return SeparatorRole.BLOCK_END; + } + } + } + + public class Factory implements FrontMatterParser.Factory { + @Override + public FrontMatterParser create() { + return new CustomParser(); + } + } +} diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererContentTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererRawContentTest.java similarity index 68% rename from commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererContentTest.java rename to commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererRawContentTest.java index 0c375e801..bc3976fb0 100644 --- a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererContentTest.java +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterMarkdownRendererRawContentTest.java @@ -1,15 +1,15 @@ package org.commonmark.ext.front.matter; import org.commonmark.Extension; -import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.ext.front.matter.parser.RawContentParser; import org.commonmark.node.Node; import org.junit.jupiter.api.Test; import java.util.Set; -public class YamlFrontMatterMarkdownRendererContentTest extends YamlFrontMatterMarkdownRendererTestCase { +public class YamlFrontMatterMarkdownRendererRawContentTest extends YamlFrontMatterMarkdownRendererTestCase { private static final Set EXTENSIONS = Set.of( - YamlFrontMatterExtension.create(new YamlContentExtractor.Factory()) + YamlFrontMatterExtension.create(new RawContentParser.Factory()) ); @Override @@ -27,7 +27,7 @@ public void testAppendMissingTrailingNewline() { final String input = "---" + "\nhello: world" + "\n---" + "\n\ngreat"; Node document = parser.parse(input); - YamlFrontMatterContent contentNode = (YamlFrontMatterContent) document.getFirstChild().getFirstChild(); + YamlFrontMatterRawContent contentNode = (YamlFrontMatterRawContent) document.getFirstChild().getFirstChild(); contentNode.setContent("see: you "); diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterContentTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterRawContentTest.java similarity index 82% rename from commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterContentTest.java rename to commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterRawContentTest.java index 5b7d735f9..c944a8917 100644 --- a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterContentTest.java +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterRawContentTest.java @@ -1,7 +1,7 @@ package org.commonmark.ext.front.matter; import org.commonmark.Extension; -import org.commonmark.ext.front.matter.extractor.YamlContentExtractor; +import org.commonmark.ext.front.matter.parser.RawContentParser; import org.commonmark.node.Node; import org.junit.jupiter.api.Test; @@ -11,9 +11,9 @@ import static org.assertj.core.api.Assertions.assertThat; -public class YamlFrontMatterContentTest extends YamlFrontMatterTestCase { +public class YamlFrontMatterRawContentTest extends YamlFrontMatterTestCase { private static final Set EXTENSIONS = Set.of( - YamlFrontMatterExtension.create(new YamlContentExtractor.Factory()) + YamlFrontMatterExtension.create(new RawContentParser.Factory()) ); @Override @@ -22,7 +22,7 @@ Set getExtensions() { } @Test - public void frontMatterAsStringContent() { + public void frontMatterAsRawContent() { final String input = "---" + "\n first: foo" + "\n second: bar" + "\n..." + "\n" + "\ngreat"; final String rendered = "

great

\n"; @@ -48,14 +48,14 @@ public void contentNodeCanBeModified() { final String input = "---" + "\nhello: world" + "\n---" + "\n"; Node document = parser.parse(input); - YamlFrontMatterContent contentNode = (YamlFrontMatterContent) document.getFirstChild().getFirstChild(); + YamlFrontMatterRawContent contentNode = (YamlFrontMatterRawContent) document.getFirstChild().getFirstChild(); contentNode.setContent("see: you\n"); YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); document.accept(visitor); - String content = visitor.getContent(); + String content = visitor.getRawContent(); assertThat(visitor.isFrontMatterPresent()).isTrue(); assertThat(content).isEqualTo("see: you\n"); } @@ -77,7 +77,7 @@ public void frontMatterPresent() { YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); document.accept(visitor); - assertThat(visitor.getContent()).isNotEmpty(); + assertThat(visitor.getRawContent()).isNotEmpty(); assertThat(visitor.isFrontMatterPresent()).isTrue(); } @@ -89,7 +89,7 @@ public void frontMatterNotPresent() { YamlFrontMatterVisitor visitor = new YamlFrontMatterVisitor(); document.accept(visitor); - assertThat(visitor.getContent()).isEmpty(); + assertThat(visitor.getRawContent()).isEmpty(); assertThat(visitor.isFrontMatterPresent()).isFalse(); } } diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java index 6f8e50e08..d8110d349 100644 --- a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java +++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTestCase.java @@ -25,7 +25,7 @@ protected String render(String source) { protected String getFrontMatterContent(String input) { Node document = parser.parse(input); - return YamlFrontMatterVisitor.readContent(document); + return YamlFrontMatterVisitor.readRawContent(document); } protected Map> getFrontMatterData(String input) {