Add support for YAML frontmatter parsing/discard (issue #37)

This commit is contained in:
Alexandre Mutel
2016-09-19 10:11:38 +02:00
parent c43d5ccd63
commit f0ea008c46
9 changed files with 270 additions and 0 deletions

View File

@@ -89,6 +89,7 @@
<None Include="Specs\BootstrapSpecs.md" />
<None Include="Specs\DiagramsSpecs.md" />
<None Include="Specs\NoHtmlSpecs.md" />
<None Include="Specs\YamlSpecs.md" />
<None Include="Specs\TaskListSpecs.md" />
<None Include="Specs\SmartyPantsSpecs.md" />
<None Include="Specs\MediaSpecs.md" />

View File

@@ -19990,4 +19990,84 @@ namespace Markdig.Tests
TestParser.TestSpec("<div>\nthis is some text\n</div>", "<p>&lt;div&gt;\nthis is some text\n&lt;/div&gt;</p>", "nohtml");
}
}
// # Extensions
//
// Adds support for YAML frontmatter parsing:
//
// ## YAML frontmatter discard
//
// If a frontmatter is present, it will not be rendered:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example001()
{
// Example 1
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
// ---
// this: is a frontmatter
// ---
// This is a text
//
// Should be rendered as:
// <p>This is a text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 1, "Extensions YAML frontmatter discard");
TestParser.TestSpec("---\nthis: is a frontmatter\n---\nThis is a text", "<p>This is a text</p>", "yaml");
}
}
// But if a frontmatter doesn't happen on the first line, it will be parse as regular Markdown content
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example002()
{
// Example 2
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
// This is a text1
// ---
// this: is a frontmatter
// ---
// This is a text2
//
// Should be rendered as:
// <h2>This is a text1</h2>
// <h2>this: is a frontmatter</h2>
// <p>This is a text2</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 2, "Extensions YAML frontmatter discard");
TestParser.TestSpec("This is a text1\n---\nthis: is a frontmatter\n---\nThis is a text2", "<h2>This is a text1</h2>\n<h2>this: is a frontmatter</h2>\n<p>This is a text2</p>", "yaml");
}
}
// It expects an exact 3 dashes `---`:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example003()
{
// Example 3
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
// ----
// this: is a frontmatter
// ----
// This is a text
//
// Should be rendered as:
// <hr />
// <h2>this: is a frontmatter</h2>
// <p>This is a text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 3, "Extensions YAML frontmatter discard");
TestParser.TestSpec("----\nthis: is a frontmatter\n----\nThis is a text", "<hr />\n<h2>this: is a frontmatter</h2>\n<p>This is a text</p>", "yaml");
}
}
}

View File

@@ -60,6 +60,7 @@ SOFTWARE.
new KeyValuePair<string, string>(Host.ResolvePath("TaskListSpecs.md"), "tasklists|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("DiagramsSpecs.md"), "diagrams|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("NoHtmlSpecs.md"), "nohtml"),
new KeyValuePair<string, string>(Host.ResolvePath("YamlSpecs.md"), "yaml"),
};
var emptyLines = false;
var displayEmptyLines = false;

View File

@@ -0,0 +1,44 @@
# Extensions
Adds support for YAML frontmatter parsing:
## YAML frontmatter discard
If a frontmatter is present, it will not be rendered:
```````````````````````````````` example
---
this: is a frontmatter
---
This is a text
.
<p>This is a text</p>
````````````````````````````````
But if a frontmatter doesn't happen on the first line, it will be parse as regular Markdown content
```````````````````````````````` example
This is a text1
---
this: is a frontmatter
---
This is a text2
.
<h2>This is a text1</h2>
<h2>this: is a frontmatter</h2>
<p>This is a text2</p>
````````````````````````````````
It expects an exact 3 dashes `---`:
```````````````````````````````` example
----
this: is a frontmatter
----
This is a text
.
<hr />
<h2>this: is a frontmatter</h2>
<p>This is a text</p>
````````````````````````````````

View File

@@ -0,0 +1,31 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
using Markdig.Syntax;
namespace Markdig.Extensions.Yaml
{
/// <summary>
/// A YAML frontmatter block.
/// </summary>
/// <seealso cref="Markdig.Syntax.CodeBlock" />
public class YamlFrontMatterBlock : CodeBlock, IFencedBlock
{
/// <summary>
/// Initializes a new instance of the <see cref="YamlFrontMatterBlock"/> class.
/// </summary>
/// <param name="parser">The parser.</param>
public YamlFrontMatterBlock(BlockParser parser) : base(parser)
{
}
public string Info { get; set; }
public string Arguments { get; set; }
public int FencedCharCount { get; set; }
public char FencedChar { get; set; }
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
namespace Markdig.Extensions.Yaml
{
/// <summary>
/// Extension to discard a YAML frontmatter at the beginning of a Markdown document.
/// </summary>
public class YamlFrontMatterExtension : IMarkdownExtension
{
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<YamlFrontMatterParser>())
{
// Insert the YAML parser before the thematic break parser, as it is also triggered on a --- dash
pipeline.BlockParsers.InsertBefore<ThematicBreakParser>(new YamlFrontMatterParser());
}
}
public void Setup(IMarkdownRenderer renderer)
{
if (!renderer.ObjectRenderers.Contains<YamlFrontMatterRenderer>())
{
renderer.ObjectRenderers.InsertBefore<CodeBlockRenderer>(new YamlFrontMatterRenderer());
}
}
}
}

View File

@@ -0,0 +1,44 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
namespace Markdig.Extensions.Yaml
{
/// <summary>
/// Block parser for a YAML frontmatter.
/// </summary>
/// <seealso cref="Markdig.Parsers.FencedBlockParserBase{YamlFrontMatterBlock}" />
public class YamlFrontMatterParser : FencedBlockParserBase<YamlFrontMatterBlock>
{
// We reuse a FencedCodeBlock parser to grab a frontmatter, only active if it happens on the first line of the document.
/// <summary>
/// Initializes a new instance of the <see cref="FencedCodeBlockParser"/> class.
/// </summary>
public YamlFrontMatterParser()
{
OpeningCharacters = new[] { '-' };
InfoPrefix = null;
// We expect only 3 --- at the beginning of the file no more, no less
MinimumMatchCount = 3;
MaximumMatchCount = 3;
}
protected override YamlFrontMatterBlock CreateFencedBlock(BlockProcessor processor)
{
return new YamlFrontMatterBlock(this);
}
public override BlockState TryOpen(BlockProcessor processor)
{
// Only accept a frontmatter at the beginning of the file
if (processor.LineIndex != 0)
{
return BlockState.None;
}
return base.TryOpen(processor);
}
}
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Renderers;
using Markdig.Renderers.Html;
namespace Markdig.Extensions.Yaml
{
/// <summary>
/// Empty renderer for a <see cref="YamlFrontMatterBlock"/>
/// </summary>
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{YamlFrontMatterBlock}" />
public class YamlFrontMatterRenderer : HtmlObjectRenderer<YamlFrontMatterBlock>
{
protected override void Write(HtmlRenderer renderer, YamlFrontMatterBlock obj)
{
}
}
}

View File

@@ -26,6 +26,7 @@ using Markdig.Extensions.SelfPipeline;
using Markdig.Extensions.SmartyPants;
using Markdig.Extensions.Tables;
using Markdig.Extensions.TaskLists;
using Markdig.Extensions.Yaml;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
@@ -63,6 +64,17 @@ namespace Markdig
.UseGenericAttributes(); // Must be last as it is one parser that is modifying other parsers
}
/// <summary>
/// Uses YAML frontmatter extension that will parse a YAML frontmatter into the MarkdownDocument. Note that they are not rendered by any default HTML renderer.
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipelineBuilder UseYamlFrontMatter(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<YamlFrontMatterExtension>();
return pipeline;
}
/// <summary>
/// Uses the self pipeline extension that will detect the pipeline to use from the markdown input that contains a special tag. See <see cref="SelfPipelineExtension"/>
/// </summary>
@@ -412,6 +424,8 @@ namespace Markdig
return pipeline;
}
// TODO: the extension string should come from the extension itself instead of this hardcoded switch case.
foreach (var extension in extensions.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries))
{
switch (extension.ToLowerInvariant())
@@ -490,6 +504,9 @@ namespace Markdig
case "nohtml":
pipeline.DisableHtml();
break;
case "yaml":
pipeline.UseYamlFrontMatter();
break;
default:
throw new ArgumentException($"Invalid extension `{extension}` from `{extensions}`", nameof(extensions));
}