Better handle YAML frontmatter in case the opening --- is never actually closed (#160)

This commit is contained in:
Alexandre Mutel
2017-11-18 10:55:16 +01:00
parent 964538ec79
commit fbdb8cf063
4 changed files with 125 additions and 22 deletions

View File

@@ -20617,7 +20617,7 @@ namespace Markdig.Tests
TestParser.TestSpec("---\nthis: is a frontmatter\n...\nThis is a text", "<p>This is a text</p>", "yaml");
}
}
// It expects exactly three dots `...`:
// If the end front matter marker (`...` or `---`) is not present, it will render the `---` has a `<hr>`:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
@@ -20630,17 +20630,18 @@ namespace Markdig.Tests
// The following CommonMark:
// ---
// this: is a frontmatter
// ....
// This is a text
//
// Should be rendered as:
//
// <hr />
// <p>this: is a frontmatter
// This is a text</p>
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");
TestParser.TestSpec("---\nthis: is a frontmatter\nThis is a text", "<hr />\n<p>this: is a frontmatter\nThis is a text</p>", "yaml");
}
}
// Front matter ends with the first line containing three dots `...` or three dashes `...`:
// It expects exactly three dots `...`:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
@@ -20654,6 +20655,32 @@ namespace Markdig.Tests
// ---
// this: is a frontmatter
// ....
// This is a text
//
// Should be rendered as:
// <hr />
// <p>this: is a frontmatter
// ....
// 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....\nThis is a text", "<hr />\n<p>this: is a frontmatter\n....\nThis is a text</p>", "yaml");
}
}
// Front matter ends with the first line containing three dots `...` or three dashes `...`:
[TestFixture]
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example007()
{
// Example 7
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
// ---
// this: is a frontmatter
// ....
//
// Hello
// ---
@@ -20662,7 +20689,7 @@ namespace Markdig.Tests
// 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");
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 7, "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");
}
}
@@ -20671,9 +20698,9 @@ namespace Markdig.Tests
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example007()
public void Example008()
{
// Example 7
// Example 8
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
@@ -20685,7 +20712,7 @@ namespace Markdig.Tests
// 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");
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");
}
}
@@ -20694,9 +20721,9 @@ namespace Markdig.Tests
public partial class TestExtensionsYAMLfrontmatterdiscard
{
[Test]
public void Example008()
public void Example009()
{
// Example 8
// Example 9
// Section: Extensions YAML frontmatter discard
//
// The following CommonMark:
@@ -20708,7 +20735,7 @@ namespace Markdig.Tests
// 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");
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 9, "Extensions YAML frontmatter discard");
TestParser.TestSpec("---\nthis: is a frontmatter\n... \nThis is a text", "<p>This is a text</p>", "yaml");
}
}

View File

@@ -53,6 +53,18 @@ This is a text
<p>This is a text</p>
````````````````````````````````
If the end front matter marker (`...` or `---`) is not present, it will render the `---` has a `<hr>`:
```````````````````````````````` example
---
this: is a frontmatter
This is a text
.
<hr />
<p>this: is a frontmatter
This is a text</p>
````````````````````````````````
It expects exactly three dots `...`:
```````````````````````````````` example
@@ -61,6 +73,10 @@ this: is a frontmatter
....
This is a text
.
<hr />
<p>this: is a frontmatter
....
This is a text</p>
````````````````````````````````
Front matter ends with the first line containing three dots `...` or three dashes `...`:

View File

@@ -67,17 +67,55 @@ namespace Markdig.Extensions.Yaml
// 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;
bool hasFullYamlFrontMatter = false;
// We make sure that there is a closing frontmatter somewhere in the document
// so here we work on the full document instead of just the line
var fullLine = new StringSlice(line.Text, line.Start, line.Text.Length - 1);
c = fullLine.CurrentChar;
while (c != '\0')
{
c = fullLine.NextChar();
if (c == '\n' || c == '\r')
{
var nc = fullLine.PeekChar();
if (c == '\r' && nc == '\n')
{
c = fullLine.NextChar();
}
nc = fullLine.NextChar(); // skip \n
if (nc == '-')
{
if (fullLine.NextChar() == '-' && fullLine.NextChar() == '-' && (fullLine.NextChar() == '\0' || fullLine.SkipSpacesToEndOfLineOrEndOfDocument()))
{
hasFullYamlFrontMatter = true;
break;
}
}
else if (nc == '.')
{
if (fullLine.NextChar() == '.' && fullLine.NextChar() == '.' && (fullLine.NextChar() == '\0' || fullLine.SkipSpacesToEndOfLineOrEndOfDocument()))
{
hasFullYamlFrontMatter = true;
break;
}
}
}
}
// Store the number of matched string into the context
processor.NewBlocks.Push(block);
if (hasFullYamlFrontMatter)
{
// Create a front matter block
var block = this.CreateFrontMatterBlock(processor);
block.Column = processor.Column;
block.Span.Start = 0;
block.Span.End = line.Start;
// Discard the current line as it is already parsed
return BlockState.ContinueDiscard;
// 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;

View File

@@ -106,7 +106,7 @@ namespace Markdig.Helpers
/// <param name="offset">The offset.</param>
/// <returns>The character at offset, returns `\0` if none.</returns>
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
public char PeekChar(int offset)
public char PeekChar(int offset = 1)
{
var index = Start + offset;
return index >= Start && index <= End ? Text[index] : (char) 0;
@@ -168,6 +168,28 @@ namespace Markdig.Helpers
return i == text.Length;
}
/// <summary>
/// Expect spaces until a end of line. Return <c>false</c> otherwise.
/// </summary>
/// <returns><c>true</c> if whitespaces where matched until a end of line</returns>
public bool SkipSpacesToEndOfLineOrEndOfDocument()
{
for (int i = Start; i <= End; i++)
{
var c = Text[i];
if (c.IsWhitespace())
{
if (c == '\0' || c == '\n' || (c == '\r' && i + 1 <= End && Text[i + 1] != '\n'))
{
return true;
}
continue;
}
return false;
}
return true;
}
/// <summary>
/// Matches the specified text using lowercase comparison.
/// </summary>