Implement Pandoc compatible front matter parsing

This commit is contained in:
Ken Muse
2017-08-29 18:49:14 -04:00
parent ed2371beae
commit 4e4825cb3f
4 changed files with 284 additions and 31 deletions

View File

@@ -20492,6 +20492,124 @@ namespace Markdig.Tests
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");
}
}
// It can end with three dots `...`:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example004()
{
// Example 4
// 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, 4, "Extensions YAML frontmatter discard");
TestParser.TestSpec("---\nthis: is a frontmatter\n...\nThis is a text", "<p>This is a text</p>", "yaml");
}
}
// It expects exactly three dots `...`:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example005()
{
// Example 5
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
// ---
// this: is a frontmatter
// ....
// This is a text
//
// Should be rendered as:
//
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 5, "Extensions YAML frontmatter discard");
TestParser.TestSpec("---\nthis: is a frontmatter\n....\nThis is a text", "", "yaml");
}
}
// Front matter ends with the first line containing three dots `...` or three dashes `...`:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example006()
{
// Example 6
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
// ---
// this: is a frontmatter
// ....
//
// Hello
// ---
// This is a text
//
// Should be rendered as:
// <p>This is a text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 6, "Extensions YAML frontmatter discard");
TestParser.TestSpec("---\nthis: is a frontmatter\n....\n\nHello\n---\nThis is a text", "<p>This is a text</p>", "yaml");
}
}
// It expects whitespace can exist after the leading characters
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example007()
{
// Example 7
// 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, 7, "Extensions YAML frontmatter discard");
TestParser.TestSpec("--- \nthis: is a frontmatter\n...\nThis is a text", "<p>This is a text</p>", "yaml");
}
}
// It expects whitespace can exist after the trailing characters
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example008()
{
// Example 8
// 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, 8, "Extensions YAML frontmatter discard");
TestParser.TestSpec("---\nthis: is a frontmatter\n... \nThis is a text", "<p>This is a text</p>", "yaml");
}
}
// # Extensions
//

View File

@@ -42,3 +42,62 @@ This is a text
<p>This is a text</p>
````````````````````````````````
It can end with three dots `...`:
```````````````````````````````` example
---
this: is a frontmatter
...
This is a text
.
<p>This is a text</p>
````````````````````````````````
It expects exactly three dots `...`:
```````````````````````````````` example
---
this: is a frontmatter
....
This is a text
.
````````````````````````````````
Front matter ends with the first line containing three dots `...` or three dashes `...`:
```````````````````````````````` example
---
this: is a frontmatter
....
Hello
---
This is a text
.
<p>This is a text</p>
````````````````````````````````
It expects whitespace can exist after the leading characters
```````````````````````````````` example
---
this: is a frontmatter
...
This is a text
.
<p>This is a text</p>
````````````````````````````````
It expects whitespace can exist after the trailing characters
```````````````````````````````` example
---
this: is a frontmatter
...
This is a text
.
<p>This is a text</p>
````````````````````````````````

View File

@@ -10,7 +10,7 @@ namespace Markdig.Extensions.Yaml
/// A YAML frontmatter block.
/// </summary>
/// <seealso cref="Markdig.Syntax.CodeBlock" />
public class YamlFrontMatterBlock : CodeBlock, IFencedBlock
public class YamlFrontMatterBlock : CodeBlock
{
/// <summary>
/// Initializes a new instance of the <see cref="YamlFrontMatterBlock"/> class.
@@ -19,13 +19,5 @@ namespace Markdig.Extensions.Yaml
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

@@ -1,44 +1,128 @@
// 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.Helpers;
using Markdig.Parsers;
using Markdig.Syntax;
namespace Markdig.Extensions.Yaml
{
/// <summary>
/// Block parser for a YAML frontmatter.
/// </summary>
/// <seealso cref="Markdig.Parsers.FencedBlockParserBase{YamlFrontMatterBlock}" />
public class YamlFrontMatterParser : FencedBlockParserBase<YamlFrontMatterBlock>
/// <seealso cref="YamlFrontMatterBlock" />
public class YamlFrontMatterParser : BlockParser
{
// We reuse a FencedCodeBlock parser to grab a frontmatter, only active if it happens on the first line of the document.
// 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()
/// <summary>
/// Initializes a new instance of the <see cref="YamlFrontMatterParser"/> 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;
this.OpeningCharacters = new[] { '-' };
}
protected override YamlFrontMatterBlock CreateFencedBlock(BlockProcessor processor)
{
return new YamlFrontMatterBlock(this);
}
/// <summary>
/// Creates the front matter block.
/// </summary>
/// <param name="processor">The block processor</param>
/// <returns>The front matter block</returns>
protected virtual YamlFrontMatterBlock CreateFrontMatterBlock(BlockProcessor processor)
{
return new YamlFrontMatterBlock(this);
}
public override BlockState TryOpen(BlockProcessor processor)
/// <summary>
/// Tries to match a block opening.
/// </summary>
/// <param name="processor">The parser processor.</param>
/// <returns>The result of the match</returns>
public override BlockState TryOpen(BlockProcessor processor)
{
// Only accept a frontmatter at the beginning of the file
if (processor.LineIndex != 0)
// We expect no indentation for a fenced code block.
if (processor.IsCodeIndent)
{
return BlockState.None;
}
// Only accept a frontmatter at the beginning of the file
if (processor.Start != 0)
{
return BlockState.None;
}
int count = 0;
var line = processor.Line;
char c = line.CurrentChar;
return base.TryOpen(processor);
}
}
// Must consist of exactly three dashes
while (c == '-' && count < 4)
{
count++;
c = line.NextChar();
}
// If three dashes (optionally followed by whitespace)
// this is a YAML front matter blcok
if (count == 3 && (c == '\0' || c.IsWhitespace()) && line.TrimEnd())
{
// Create a front matter block
var block = this.CreateFrontMatterBlock(processor);
block.Column = processor.Column;
block.Span.Start = 0;
block.Span.End = line.Start;
// Store the number of matched string into the context
processor.NewBlocks.Push(block);
// Discard the current line as it is already parsed
return BlockState.ContinueDiscard;
}
return BlockState.None;
}
/// <summary>
/// Tries to continue matching a block already opened.
/// </summary>
/// <param name="processor">The parser processor.</param>
/// <param name="block">The block already opened.</param>
/// <returns>The result of the match. By default, don't expect any newline</returns>
public override BlockState TryContinue(BlockProcessor processor, Block block)
{
char matchChar;
int count = 0;
var c = processor.CurrentChar;
// Determine if we have a closing fence.
// It can start or end with either <c>---</c> or <c>...</c>
var line = processor.Line;
if (processor.Column == 0 && (c == '-' || c == '.'))
{
matchChar = c;
while (c == matchChar)
{
c = line.NextChar();
count++;
}
// If we have a closing fence, close it and discard the current line
// The line must contain only fence characters and optional following whitespace.
if (count == 3 && !processor.IsCodeIndent && (c == '\0' || c.IsWhitespace()) && line.TrimEnd())
{
block.UpdateSpanEnd(line.Start - 1);
// Don't keep the last line
return BlockState.BreakDiscard;
}
}
// Reset the indentation to the column before the indent
processor.GoToColumn(processor.ColumnBeforeIndent);
return BlockState.Continue;
}
}
}