diff --git a/src/Markdig.Tests/Specs/Specs.cs b/src/Markdig.Tests/Specs/Specs.cs index 46ae247a..45bd5979 100644 --- a/src/Markdig.Tests/Specs/Specs.cs +++ b/src/Markdig.Tests/Specs/Specs.cs @@ -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", "
\n

this: is a frontmatter

\n

This is a text

", "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: + //

This is a text

+ + 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", "

This is a text

", "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: + //

This is a text

+ + 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", "

This is a text

", "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: + //

This is a text

+ + 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", "

This is a text

", "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: + //

This is a text

+ + 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", "

This is a text

", "yaml"); + } } // # Extensions // diff --git a/src/Markdig.Tests/Specs/YamlSpecs.md b/src/Markdig.Tests/Specs/YamlSpecs.md index 10bb678f..b6ff478b 100644 --- a/src/Markdig.Tests/Specs/YamlSpecs.md +++ b/src/Markdig.Tests/Specs/YamlSpecs.md @@ -42,3 +42,62 @@ This is a text

This is a text

```````````````````````````````` +It can end with three dots `...`: + +```````````````````````````````` example +--- +this: is a frontmatter +... +This is a text +. +

This is a text

+```````````````````````````````` + +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 +. +

This is a text

+```````````````````````````````` + +It expects whitespace can exist after the leading characters + +```````````````````````````````` example +--- +this: is a frontmatter +... +This is a text +. +

This is a text

+```````````````````````````````` + +It expects whitespace can exist after the trailing characters + +```````````````````````````````` example +--- +this: is a frontmatter +... +This is a text +. +

This is a text

+```````````````````````````````` + + + diff --git a/src/Markdig/Extensions/Yaml/YamlFrontMatterBlock.cs b/src/Markdig/Extensions/Yaml/YamlFrontMatterBlock.cs index 45a37fd5..e6018740 100644 --- a/src/Markdig/Extensions/Yaml/YamlFrontMatterBlock.cs +++ b/src/Markdig/Extensions/Yaml/YamlFrontMatterBlock.cs @@ -10,7 +10,7 @@ namespace Markdig.Extensions.Yaml /// A YAML frontmatter block. /// /// - public class YamlFrontMatterBlock : CodeBlock, IFencedBlock + public class YamlFrontMatterBlock : CodeBlock { /// /// Initializes a new instance of the 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; } } } \ No newline at end of file diff --git a/src/Markdig/Extensions/Yaml/YamlFrontMatterParser.cs b/src/Markdig/Extensions/Yaml/YamlFrontMatterParser.cs index b76e6954..a8fc36c7 100644 --- a/src/Markdig/Extensions/Yaml/YamlFrontMatterParser.cs +++ b/src/Markdig/Extensions/Yaml/YamlFrontMatterParser.cs @@ -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 { /// /// Block parser for a YAML frontmatter. /// - /// - public class YamlFrontMatterParser : FencedBlockParserBase + /// + 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. - /// - /// Initializes a new instance of the class. - /// - public YamlFrontMatterParser() + /// + /// Initializes a new instance of the class. + /// + 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); - } + /// + /// Creates the front matter block. + /// + /// The block processor + /// The front matter block + protected virtual YamlFrontMatterBlock CreateFrontMatterBlock(BlockProcessor processor) + { + return new YamlFrontMatterBlock(this); + } - public override BlockState TryOpen(BlockProcessor processor) + /// + /// Tries to match a block opening. + /// + /// The parser processor. + /// The result of the match + 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; + } + + /// + /// Tries to continue matching a block already opened. + /// + /// The parser processor. + /// The block already opened. + /// The result of the match. By default, don't expect any newline + 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 --- or ... + 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; + } + } } \ No newline at end of file