Skip to content

Commit

Permalink
Merge pull request xoofx#139 from kenmuse/enhance/yaml-frontmatter
Browse files Browse the repository at this point in the history
Implement Pandoc compatible front matter parsing
  • Loading branch information
xoofx authored Aug 30, 2017
2 parents ed2371b + 2df2ff1 commit 82987fa
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 20 deletions.
118 changes: 118 additions & 0 deletions src/Markdig.Tests/Specs/Specs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20492,6 +20492,124 @@ public void Example003()
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
//
Expand Down
59 changes: 59 additions & 0 deletions src/Markdig.Tests/Specs/YamlSpecs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>
````````````````````````````````



10 changes: 1 addition & 9 deletions src/Markdig/Extensions/Yaml/YamlFrontMatterBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -19,13 +19,5 @@ public class YamlFrontMatterBlock : CodeBlock, IFencedBlock
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; }
}
}
106 changes: 95 additions & 11 deletions src/Markdig/Extensions/Yaml/YamlFrontMatterParser.cs
Original file line number Diff line number Diff line change
@@ -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.

/// <summary>
/// Initializes a new instance of the <see cref="FencedCodeBlockParser"/> class.
/// 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)
/// <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);
}

/// <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)
{
// 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.LineIndex != 0)
if (processor.Start != 0)
{
return BlockState.None;
}

int count = 0;
var line = processor.Line;
char c = line.CurrentChar;

// 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 base.TryOpen(processor);
return BlockState.Continue;
}
}
}

0 comments on commit 82987fa

Please sign in to comment.