Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,26 @@ Enables tables using pipes as in [GitHub Flavored Markdown][gfm-tables].

Use class `TablesExtension` in artifact `commonmark-ext-gfm-tables`.

### YAML front matter

Enables an YAML front matter block. This extension only supports a subset of YAML syntax. Here's an example of what's supported:

```
---
key: value
list:
- value 1
- value 2
literal: |
this is literal value.

literal values 2
---

document start here
```

Use class `YamlFrontMatterExtension` in artifact `commonmark-ext-yaml-front-matter`. To fetch metadata, use `YamlFrontMatterVisitor`.

Contributing
------------
Expand Down
29 changes: 29 additions & 0 deletions commonmark-ext-yaml-front-matter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>commonmark-parent</artifactId>
<groupId>com.atlassian.commonmark</groupId>
<version>0.4.2-SNAPSHOT</version>
</parent>

<artifactId>commonmark-ext-yaml-front-matter</artifactId>
<name>commonmark-java extension for YAML front matter</name>
<description>commonmark-java extension for YAML front matter</description>

<dependencies>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
</dependency>

<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-test-util</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.commonmark.ext.front.matter;

import org.commonmark.node.CustomBlock;

public class YamlFrontMatterBlock extends CustomBlock {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.commonmark.ext.front.matter;

import org.commonmark.Extension;
import org.commonmark.ext.front.matter.internal.YamlFrontMatterBlockParser;
import org.commonmark.ext.front.matter.internal.YamlFrontMatterBlockRenderer;
import org.commonmark.html.HtmlRenderer;
import org.commonmark.parser.Parser;

/**
* Extension for YAML-like metadata.
* <p>
* Create it with {@link #create()} and then configure it on the builders
* ({@link org.commonmark.parser.Parser.Builder#extensions(Iterable)},
* {@link org.commonmark.html.HtmlRenderer.Builder#extensions(Iterable)}).
* </p>
* <p>
* The parsed metadata is turned into {@link YamlFrontMatterNode}. You can access the metadata using {@link YamlFrontMatterVisitor}.
* </p>
*/
public class YamlFrontMatterExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {
private YamlFrontMatterExtension() {
}

@Override
public void extend(HtmlRenderer.Builder rendererBuilder) {
rendererBuilder.customHtmlRenderer(new YamlFrontMatterBlockRenderer());
}

@Override
public void extend(Parser.Builder parserBuilder) {
parserBuilder.customBlockParserFactory(new YamlFrontMatterBlockParser.Factory());
}

public static Extension create() {
return new YamlFrontMatterExtension();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.commonmark.ext.front.matter;

import org.commonmark.node.CustomNode;

import java.util.List;

public class YamlFrontMatterNode extends CustomNode {
private String key;
private List<String> values;

public YamlFrontMatterNode(String key, List<String> values) {
this.key = key;
this.values = values;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public List<String> getValues() {
return values;
}

public void setValues(List<String> values) {
this.values = values;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.commonmark.ext.front.matter;

import org.commonmark.node.AbstractVisitor;
import org.commonmark.node.CustomNode;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class YamlFrontMatterVisitor extends AbstractVisitor {
private Map<String, List<String>> data;

public YamlFrontMatterVisitor() {
data = new LinkedHashMap<>();
}

@Override
public void visit(CustomNode customNode) {
if (customNode instanceof YamlFrontMatterNode) {
data.put(((YamlFrontMatterNode) customNode).getKey(), ((YamlFrontMatterNode) customNode).getValues());
} else {
super.visit(customNode);
}
}

public Map<String, List<String>> getData() {
return data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.commonmark.ext.front.matter.internal;

import org.commonmark.ext.front.matter.YamlFrontMatterBlock;
import org.commonmark.ext.front.matter.YamlFrontMatterNode;
import org.commonmark.internal.DocumentBlockParser;
import org.commonmark.node.Block;
import org.commonmark.parser.InlineParser;
import org.commonmark.parser.block.*;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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 inYAMLBlock;
private boolean inLiteral;
private String currentKey;
private List<String> currentValues;
private YamlFrontMatterBlock block;

public YamlFrontMatterBlockParser() {
inYAMLBlock = true;
inLiteral = false;
currentKey = null;
currentValues = new ArrayList<>();
block = new YamlFrontMatterBlock();
}

@Override
public Block getBlock() {
return block;
}

@Override
public void addLine(CharSequence line) {
}

@Override
public BlockContinue tryContinue(ParserState parserState) {
final CharSequence line = parserState.getLine();

if (inYAMLBlock) {
if (REGEX_END.matcher(line).matches()) {
if (currentKey != null) {
block.appendChild(new YamlFrontMatterNode(currentKey, currentValues));
}
return BlockContinue.finished();
}

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<>();
if ("|".equals(matcher.group(2))) {
inLiteral = true;
} else if (!"".equals(matcher.group(2))) {
currentValues.add(matcher.group(2));
}

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()) {
currentValues.add(matcher.group(1));
}
}

return BlockContinue.atIndex(parserState.getIndex());
}
} else if (REGEX_BEGIN.matcher(line).matches()) {
inYAMLBlock = true;
return BlockContinue.atIndex(parserState.getIndex());
}

return BlockContinue.none();
}

@Override
public void parseInlines(InlineParser inlineParser) {
}

public static class Factory extends AbstractBlockParserFactory {
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
CharSequence line = state.getLine();
BlockParser parentParser = matchedBlockParser.getMatchedBlockParser();
// check whether this line is the first line of whole document or not
if (parentParser instanceof DocumentBlockParser && parentParser.getBlock().getFirstChild() == null &&
REGEX_BEGIN.matcher(line).matches()) {
return BlockStart.of(new YamlFrontMatterBlockParser()).atIndex(state.getNextNonSpaceIndex());
}

return BlockStart.none();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.commonmark.ext.front.matter.internal;

import org.commonmark.ext.front.matter.YamlFrontMatterBlock;
import org.commonmark.ext.front.matter.YamlFrontMatterNode;
import org.commonmark.html.CustomHtmlRenderer;
import org.commonmark.html.HtmlWriter;
import org.commonmark.node.Node;
import org.commonmark.node.Visitor;

public class YamlFrontMatterBlockRenderer implements CustomHtmlRenderer {
@Override
public boolean render(Node node, HtmlWriter htmlWriter, Visitor visitor) {
return node instanceof YamlFrontMatterBlock || node instanceof YamlFrontMatterNode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<html>
<body>
<b>Extension for YAML front matter</b>
<p>See {@link org.commonmark.ext.yaml.YamlFrontMatterExtension}</p>
</body>
</html>
Loading