mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 05:44:50 +00:00
handle whitespace before and after paragraphs correctly
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
366
src/Markdig.Tests/TestCst.cs
Normal file
366
src/Markdig.Tests/TestCst.cs
Normal file
@@ -0,0 +1,366 @@
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// General notes
|
||||
/// - whitespace can occur before, between and after symbols
|
||||
/// </summary>
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCst
|
||||
{
|
||||
[Test]
|
||||
public void TestWhitespaceBefore()
|
||||
{
|
||||
var markdown = " This is a paragraph. ";
|
||||
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var paragraphBlock = markdownDocument[0] as ParagraphBlock;
|
||||
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewLinesBeforeAndAfter()
|
||||
{
|
||||
var markdown = " \n \nLine2\n\nLine1\n\n";
|
||||
//var markdown = "\r\nLine2\r\n\r\nLine1\r\n\r\n";
|
||||
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var paragraphBlock = markdownDocument[0] as ParagraphBlock;
|
||||
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
|
||||
private void RoundTrip(string markdown)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
|
||||
[TestCase("p")]
|
||||
[TestCase(" p")]
|
||||
[TestCase("p ")]
|
||||
[TestCase(" p ")]
|
||||
|
||||
[TestCase("p\np")]
|
||||
[TestCase(" p\np")]
|
||||
[TestCase("p \np")]
|
||||
[TestCase(" p \np")]
|
||||
|
||||
[TestCase("p\n p")]
|
||||
[TestCase(" p\n p")]
|
||||
[TestCase("p \n p")]
|
||||
[TestCase(" p \n p")]
|
||||
|
||||
[TestCase("p\np ")]
|
||||
[TestCase(" p\np ")]
|
||||
[TestCase("p \np ")]
|
||||
[TestCase(" p \np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
|
||||
[TestCase("p\n\np")]
|
||||
[TestCase(" p\n\np")]
|
||||
[TestCase("p \n\np")]
|
||||
[TestCase(" p \n\np")]
|
||||
|
||||
[TestCase("p\n\n p")]
|
||||
[TestCase(" p\n\n p")]
|
||||
[TestCase("p \n\n p")]
|
||||
[TestCase(" p \n\n p")]
|
||||
|
||||
[TestCase("p\n\np ")]
|
||||
[TestCase(" p\n\np ")]
|
||||
[TestCase("p \n\np ")]
|
||||
[TestCase(" p \n\np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewLinesBeforeAndAfter2()
|
||||
{
|
||||
RoundTrip("\n# H1\n\nLine1");
|
||||
RoundTrip("\n# H1\n\nLine1\n");
|
||||
RoundTrip("\n# H1\n\nLine1\n\n");
|
||||
RoundTrip("\n\n# H1\n\nLine1\n\n");
|
||||
RoundTrip("\n\n# H1\nLine1\n\n");
|
||||
RoundTrip("\n\n# H1\nLine1\n\n");
|
||||
}
|
||||
|
||||
// i = item
|
||||
[TestCase("- i1")]
|
||||
[TestCase("- i1 ")]
|
||||
[TestCase("- i1\n- i2")]
|
||||
[TestCase("- i1\n - i2")]
|
||||
[TestCase("- i1\n - i1.1\n - i1.2")]
|
||||
public void TestList(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestImage()
|
||||
{
|
||||
RoundTrip(" ");
|
||||
RoundTrip("paragraph ");
|
||||
}
|
||||
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q1\n - > q2")]
|
||||
public void TestListItem_BlockQuote(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">quote")]
|
||||
[TestCase("> quote")]
|
||||
[TestCase("> quote")]
|
||||
[TestCase(" > quote")]
|
||||
public void TestBlockQuote(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBlockQuote()
|
||||
{
|
||||
//RoundTrip("- >quote"); // par in qb in l in li
|
||||
// 3ws? - ws 3ws? q ws? p
|
||||
// 3ws? lb
|
||||
// - li
|
||||
// ws li
|
||||
// 3ws? qb
|
||||
// q qb
|
||||
// ws? qb
|
||||
// p p
|
||||
//RoundTrip(" - > quote"); // par in qb in l in li
|
||||
}
|
||||
|
||||
/// A codeblock is indented with 4 spaces. After the 4th space, whitespace is interpreted as content.
|
||||
[TestCase(" code")]
|
||||
[TestCase(" code")]
|
||||
public void TestImplicitCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[](b)")]
|
||||
[TestCase(" [](b)")]
|
||||
[TestCase("[](b) ")]
|
||||
[TestCase(" [](b) ")]
|
||||
|
||||
[TestCase("[a](b)")]
|
||||
[TestCase(" [a](b)")]
|
||||
[TestCase("[a](b) ")]
|
||||
[TestCase(" [a](b) ")]
|
||||
|
||||
[TestCase("[ a](b)")]
|
||||
[TestCase(" [ a](b)")]
|
||||
[TestCase("[ a](b) ")]
|
||||
[TestCase(" [ a](b) ")]
|
||||
|
||||
[TestCase("[a ](b)")]
|
||||
[TestCase(" [a ](b)")]
|
||||
[TestCase("[a ](b) ")]
|
||||
[TestCase(" [a ](b) ")]
|
||||
|
||||
[TestCase("[ a ](b)")]
|
||||
[TestCase(" [ a ](b)")]
|
||||
[TestCase("[ a ](b) ")]
|
||||
[TestCase(" [ a ](b) ")]
|
||||
|
||||
// below cases are required for a full CST but not have low prio for impl
|
||||
//[TestCase("[]( b)")]
|
||||
//[TestCase(" []( b)")]
|
||||
//[TestCase("[]( b) ")]
|
||||
//[TestCase(" []( b) ")]
|
||||
|
||||
//[TestCase("[a]( b)")]
|
||||
//[TestCase(" [a]( b)")]
|
||||
//[TestCase("[a]( b) ")]
|
||||
//[TestCase(" [a]( b) ")]
|
||||
|
||||
//[TestCase("[ a]( b)")]
|
||||
//[TestCase(" [ a]( b)")]
|
||||
//[TestCase("[ a]( b) ")]
|
||||
//[TestCase(" [ a]( b) ")]
|
||||
|
||||
//[TestCase("[a ]( b)")]
|
||||
//[TestCase(" [a ]( b)")]
|
||||
//[TestCase("[a ]( b) ")]
|
||||
//[TestCase(" [a ]( b) ")]
|
||||
|
||||
//[TestCase("[ a ]( b)")]
|
||||
//[TestCase(" [ a ]( b)")]
|
||||
//[TestCase("[ a ]( b) ")]
|
||||
//[TestCase(" [ a ]( b) ")]
|
||||
|
||||
//[TestCase("[](b )")]
|
||||
//[TestCase(" [](b )")]
|
||||
//[TestCase("[](b ) ")]
|
||||
//[TestCase(" [](b ) ")]
|
||||
|
||||
//[TestCase("[a](b )")]
|
||||
//[TestCase(" [a](b )")]
|
||||
//[TestCase("[a](b ) ")]
|
||||
//[TestCase(" [a](b ) ")]
|
||||
|
||||
//[TestCase("[ a](b )")]
|
||||
//[TestCase(" [ a](b )")]
|
||||
//[TestCase("[ a](b ) ")]
|
||||
//[TestCase(" [ a](b ) ")]
|
||||
|
||||
//[TestCase("[a ](b )")]
|
||||
//[TestCase(" [a ](b )")]
|
||||
//[TestCase("[a ](b ) ")]
|
||||
//[TestCase(" [a ](b ) ")]
|
||||
|
||||
//[TestCase("[ a ](b )")]
|
||||
//[TestCase(" [ a ](b )")]
|
||||
//[TestCase("[ a ](b ) ")]
|
||||
//[TestCase(" [ a ](b ) ")]
|
||||
|
||||
//[TestCase("[]( b )")]
|
||||
//[TestCase(" []( b )")]
|
||||
//[TestCase("[]( b ) ")]
|
||||
//[TestCase(" []( b ) ")]
|
||||
|
||||
//[TestCase("[a]( b )")]
|
||||
//[TestCase(" [a]( b )")]
|
||||
//[TestCase("[a]( b ) ")]
|
||||
//[TestCase(" [a]( b ) ")]
|
||||
|
||||
//[TestCase("[ a]( b )")]
|
||||
//[TestCase(" [ a]( b )")]
|
||||
//[TestCase("[ a]( b ) ")]
|
||||
//[TestCase(" [ a]( b ) ")]
|
||||
|
||||
//[TestCase("[a ]( b )")]
|
||||
//[TestCase(" [a ]( b )")]
|
||||
//[TestCase("[a ]( b ) ")]
|
||||
//[TestCase(" [a ]( b ) ")]
|
||||
|
||||
//[TestCase("[ a ]( b )")]
|
||||
//[TestCase(" [ a ]( b )")]
|
||||
//[TestCase("[ a ]( b ) ")]
|
||||
//[TestCase(" [ a ]( b ) ")]
|
||||
public void TestInlineLink(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
[TestCase("  ")]
|
||||
public void TestImage(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("``")]
|
||||
[TestCase(" ``")]
|
||||
[TestCase("`` ")]
|
||||
[TestCase(" `` ")]
|
||||
|
||||
[TestCase("`a`")]
|
||||
[TestCase(" `a`")]
|
||||
[TestCase("`a` ")]
|
||||
[TestCase(" `a` ")]
|
||||
|
||||
[TestCase("` a`")]
|
||||
[TestCase(" ` a`")]
|
||||
[TestCase("` a` ")]
|
||||
[TestCase(" ` a` ")]
|
||||
|
||||
[TestCase("`a `")]
|
||||
[TestCase(" `a `")]
|
||||
[TestCase("`a ` ")]
|
||||
[TestCase(" `a ` ")]
|
||||
|
||||
/// <see cref="CodeInlineParser"/>: intentionally trimmed. TODO: decide on how to handle
|
||||
//[TestCase("` a `")]
|
||||
//[TestCase(" ` a `")]
|
||||
//[TestCase("` a ` ")]
|
||||
//[TestCase(" ` a ` ")]
|
||||
public void TestCodeInline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("<http://a>")]
|
||||
[TestCase(" <http://a>")]
|
||||
[TestCase("<http://a> ")]
|
||||
[TestCase(" <http://a> ")]
|
||||
|
||||
[TestCase("< http://a>")]
|
||||
[TestCase(" < http://a>")]
|
||||
[TestCase("< http://a> ")]
|
||||
[TestCase(" < http://a> ")]
|
||||
|
||||
[TestCase("<http://a >")]
|
||||
[TestCase(" <http://a >")]
|
||||
[TestCase("<http://a > ")]
|
||||
[TestCase(" <http://a > ")]
|
||||
|
||||
[TestCase("< http://a >")]
|
||||
[TestCase(" < http://a >")]
|
||||
[TestCase("< http://a > ")]
|
||||
[TestCase(" < http://a > ")]
|
||||
public void TestAutolinkInline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestInlineLinkParser
|
||||
{
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
string markdown = " ";
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var paragraphBlock = markdownDocument[0] as ParagraphBlock;
|
||||
var containerInline = paragraphBlock.Inline as ContainerInline;
|
||||
var linkInline = containerInline.FirstChild as LinkInline;
|
||||
var description = linkInline.FirstChild as LiteralInline;
|
||||
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test1()
|
||||
{
|
||||
string markdown = " ";
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var paragraphBlock = markdownDocument[0] as ParagraphBlock;
|
||||
var containerInline = paragraphBlock.Inline as ContainerInline;
|
||||
var linkInline = containerInline.FirstChild as LinkInline;
|
||||
var description = linkInline.FirstChild as LiteralInline;
|
||||
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestParagraphParser
|
||||
{
|
||||
[Test]
|
||||
public void TestWhitespaceBefore()
|
||||
{
|
||||
var markdown = " This is a paragraph. ";
|
||||
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var paragraphBlock = markdownDocument[0] as ParagraphBlock;
|
||||
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewLinesBeforeAndAfter()
|
||||
{
|
||||
var markdown = " \n \nLine2\n\nLine1\n\n";
|
||||
//var markdown = "\r\nLine2\r\n\r\nLine1\r\n\r\n";
|
||||
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var paragraphBlock = markdownDocument[0] as ParagraphBlock;
|
||||
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
|
||||
private void RoundTrip(string markdown)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var sw = new StringWriter();
|
||||
var nr = new NormalizeRenderer(sw);
|
||||
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(markdown, sw.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewLinesBeforeAndAfter2()
|
||||
{
|
||||
RoundTrip("paragraph");
|
||||
RoundTrip("\n# H1\n\nLine1");
|
||||
RoundTrip("\n# H1\n\nLine1\n");
|
||||
RoundTrip("\n# H1\n\nLine1\n\n");
|
||||
RoundTrip("\n\n# H1\n\nLine1\n\n");
|
||||
RoundTrip("\n\n# H1\nLine1\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,7 +356,7 @@ namespace Markdig.Helpers
|
||||
if (c == '(')
|
||||
{
|
||||
text.NextChar();
|
||||
text.TrimStart();
|
||||
text.TrimStart(); // this breaks whitespace before an uri
|
||||
|
||||
var pos = text.Start;
|
||||
if (TryParseUrl(ref text, out link))
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
Text = text;
|
||||
Start = 0;
|
||||
ActualStart = 0;
|
||||
End = (Text?.Length ?? 0) - 1;
|
||||
}
|
||||
|
||||
@@ -43,6 +44,7 @@ namespace Markdig.Helpers
|
||||
|
||||
Text = text;
|
||||
Start = start;
|
||||
ActualStart = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
@@ -56,6 +58,11 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
public int Start { readonly get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start position within <see cref="Text"/>.
|
||||
/// </summary>
|
||||
public int ActualStart { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end position (inclusive) within <see cref="Text"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -168,13 +168,14 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
private bool ContinueProcessingLine { get; set; }
|
||||
|
||||
public StringSlice BeforeWhitespace
|
||||
public int WhitespaceStart { get; set; }
|
||||
|
||||
public StringSlice PopBeforeWhitespace(int column)
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: make lazy
|
||||
return new StringSlice(Line.Text, StartBeforeIndent, ColumnBeforeIndent - 1);
|
||||
}
|
||||
var stringSlice = new StringSlice(Line.Text, WhitespaceStart, column - 1);
|
||||
//var stringSlice = new StringSlice(Line.Text, Line.Start + WhitespaceStart, Line.Start + column - 1);
|
||||
WhitespaceStart = column;
|
||||
return stringSlice;
|
||||
}
|
||||
|
||||
public List<StringSlice> BeforeLines { get; set; }
|
||||
@@ -267,13 +268,13 @@ namespace Markdig.Parsers
|
||||
{
|
||||
Column = CharHelper.AddTab(Column);
|
||||
}
|
||||
else if (c == ' ')
|
||||
{
|
||||
Column++;
|
||||
}
|
||||
//else if (c == ' ')
|
||||
//{
|
||||
// Column++;
|
||||
//}
|
||||
else
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
c = Line.NextChar();
|
||||
}
|
||||
@@ -883,6 +884,7 @@ namespace Markdig.Parsers
|
||||
ColumnBeforeIndent = 0;
|
||||
StartBeforeIndent = Start;
|
||||
originalLineStart = newLine.Start;
|
||||
WhitespaceStart = 0;
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Markdig.Parsers
|
||||
Level = leadingCount,
|
||||
Column = column,
|
||||
Span = { Start = sourcePosition },
|
||||
BeforeWhitespace = processor.BeforeWhitespace.ToString(),
|
||||
BeforeWhitespace = processor.PopBeforeWhitespace(column),
|
||||
LinesBefore = processor.UseLinesBefore()
|
||||
};
|
||||
processor.NewBlocks.Push(headingBlock);
|
||||
|
||||
@@ -50,15 +50,15 @@ namespace Markdig.Parsers.Inlines
|
||||
{
|
||||
// Remove line endings if the next char is a new line
|
||||
length = nextStart - slice.Start;
|
||||
if (text[nextStart] == '\n')
|
||||
{
|
||||
int end = nextStart - 1;
|
||||
while (length > 0 && text[end].IsSpace())
|
||||
{
|
||||
length--;
|
||||
end--;
|
||||
}
|
||||
}
|
||||
//if (text[nextStart] == '\n')
|
||||
//{
|
||||
// int end = nextStart - 1;
|
||||
// while (length > 0 && text[end].IsSpace())
|
||||
// {
|
||||
// length--;
|
||||
// end--;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
// The LiteralInlineParser is always matching (at least an empty string)
|
||||
|
||||
@@ -211,6 +211,7 @@ namespace Markdig.Parsers
|
||||
|
||||
// Item starting with a blank line
|
||||
int columnWidth;
|
||||
var beforeWhitespace = state.PopBeforeWhitespace(initColumn);
|
||||
|
||||
// Do we have a blank line right after the bullet?
|
||||
if (c == '\0')
|
||||
@@ -257,12 +258,14 @@ namespace Markdig.Parsers
|
||||
}
|
||||
}
|
||||
|
||||
state.WhitespaceStart += 2;
|
||||
int.TryParse(listInfo.OrderedStart, out int order);
|
||||
var newListItem = new ListItemBlock(this)
|
||||
{
|
||||
Column = initColumn,
|
||||
ColumnWidth = columnWidth,
|
||||
Order = order,
|
||||
BeforeWhitespace = beforeWhitespace,
|
||||
Span = new SourceSpan(sourcePosition, sourceEndPosition)
|
||||
};
|
||||
state.NewBlocks.Push(newListItem);
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Markdig.Parsers
|
||||
{
|
||||
Column = processor.Column,
|
||||
Span = new SourceSpan(processor.Line.Start, processor.Line.End),
|
||||
BeforeWhitespace = processor.BeforeWhitespace.ToString(),
|
||||
BeforeWhitespace = processor.PopBeforeWhitespace(processor.Column),
|
||||
LinesBefore = processor.UseLinesBefore()
|
||||
});
|
||||
return BlockState.Continue;
|
||||
@@ -102,8 +102,10 @@ namespace Markdig.Parsers
|
||||
Span = new SourceSpan(paragraph.Span.Start, line.Start),
|
||||
Level = level,
|
||||
Lines = paragraph.Lines,
|
||||
BeforeWhitespace = state.PopBeforeWhitespace(state.Column),
|
||||
LinesBefore = state.UseLinesBefore()
|
||||
};
|
||||
heading.Lines.Trim();
|
||||
//heading.Lines.Trim();
|
||||
|
||||
// Remove the paragraph as a pending block
|
||||
state.NewBlocks.Push(heading);
|
||||
|
||||
@@ -35,16 +35,24 @@ namespace Markdig.Parsers
|
||||
// A block quote marker consists of 0-3 spaces of initial indent, plus (a) the character > together with a following space, or (b) a single character > not followed by a space.
|
||||
var quoteChar = processor.CurrentChar;
|
||||
var c = processor.NextChar();
|
||||
bool hasSpaceAfterQuoteChar = false;
|
||||
int whitespaceToAdd = 1;
|
||||
if (c.IsSpaceOrTab())
|
||||
{
|
||||
processor.NextColumn();
|
||||
hasSpaceAfterQuoteChar = true;
|
||||
whitespaceToAdd += 1;
|
||||
}
|
||||
//beforeWhitespace.End -= 1 + (hasSpaceAfterQuoteChar ? 1 : 0);
|
||||
processor.NewBlocks.Push(new QuoteBlock(this)
|
||||
{
|
||||
QuoteChar = quoteChar,
|
||||
Column = column,
|
||||
Span = new SourceSpan(sourcePosition, processor.Line.End),
|
||||
BeforeWhitespace = processor.PopBeforeWhitespace(column),
|
||||
HasSpaceAfterQuoteChar = hasSpaceAfterQuoteChar,
|
||||
});
|
||||
processor.WhitespaceStart += whitespaceToAdd;
|
||||
return BlockState.Continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Markdig.Parsers
|
||||
{
|
||||
result = new ListInfo(state.CurrentChar);
|
||||
state.NextChar();
|
||||
state.NextChar();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Markdig.Renderers.Normalize
|
||||
{
|
||||
protected override void Write(NormalizeRenderer renderer, ListBlock listBlock)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.RenderLinesBefore(listBlock);
|
||||
var compact = renderer.CompactParagraph;
|
||||
renderer.CompactParagraph = !listBlock.IsLoose;
|
||||
if (listBlock.IsOrdered)
|
||||
@@ -30,12 +30,17 @@ namespace Markdig.Renderers.Normalize
|
||||
break;
|
||||
}
|
||||
}
|
||||
var writeLine = false;
|
||||
for (var i = 0; i < listBlock.Count; i++)
|
||||
{
|
||||
var item = listBlock[i];
|
||||
var listItem = (ListItemBlock) item;
|
||||
renderer.EnsureLine();
|
||||
if (writeLine)
|
||||
{
|
||||
renderer.WriteLine();
|
||||
}
|
||||
|
||||
renderer.Write(listItem.BeforeWhitespace);
|
||||
renderer.Write(index.ToString(CultureInfo.InvariantCulture));
|
||||
renderer.Write(listBlock.OrderedDelimiter);
|
||||
renderer.Write(' ');
|
||||
@@ -50,18 +55,25 @@ namespace Markdig.Renderers.Normalize
|
||||
}
|
||||
if (i + 1 < listBlock.Count && listBlock.IsLoose)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
//renderer.EnsureLine();
|
||||
renderer.WriteLine();
|
||||
}
|
||||
writeLine = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var writeLine = false;
|
||||
for (var i = 0; i < listBlock.Count; i++)
|
||||
{
|
||||
if (writeLine)
|
||||
{
|
||||
renderer.WriteLine();
|
||||
}
|
||||
var item = listBlock[i];
|
||||
var listItem = (ListItemBlock) item;
|
||||
renderer.EnsureLine();
|
||||
//renderer.EnsureLine();
|
||||
renderer.Write(listItem.BeforeWhitespace);
|
||||
renderer.Write(renderer.Options.ListItemCharacter ?? listBlock.BulletType);
|
||||
renderer.Write(' ');
|
||||
renderer.PushIndent(" ");
|
||||
@@ -69,14 +81,15 @@ namespace Markdig.Renderers.Normalize
|
||||
renderer.PopIndent();
|
||||
if (i + 1 < listBlock.Count && listBlock.IsLoose)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
//renderer.EnsureLine();
|
||||
renderer.WriteLine();
|
||||
}
|
||||
writeLine = true;
|
||||
}
|
||||
}
|
||||
renderer.CompactParagraph = compact;
|
||||
|
||||
renderer.FinishBlock(true);
|
||||
renderer.RenderLinesAfter(listBlock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Syntax;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Renderers.Normalize
|
||||
{
|
||||
@@ -10,29 +11,16 @@ namespace Markdig.Renderers.Normalize
|
||||
/// A Normalize renderer for a <see cref="ParagraphBlock"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="NormalizeObjectRenderer{ParagraphBlock}" />
|
||||
[DebuggerDisplay("renderer.Writer.ToString()")]
|
||||
public class ParagraphRenderer : NormalizeObjectRenderer<ParagraphBlock>
|
||||
{
|
||||
protected override void Write(NormalizeRenderer renderer, ParagraphBlock obj)
|
||||
protected override void Write(NormalizeRenderer renderer, ParagraphBlock parapgraph)
|
||||
{
|
||||
if (obj.LinesBefore != null)
|
||||
{
|
||||
foreach (var line in obj.LinesBefore)
|
||||
{
|
||||
renderer.WriteLine(line.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
renderer.Write(obj.BeforeWhitespace);
|
||||
renderer.WriteLeafInline(obj);
|
||||
renderer.RenderLinesBefore(parapgraph);
|
||||
renderer.Write(parapgraph.BeforeWhitespace);
|
||||
renderer.WriteLeafInline(parapgraph);
|
||||
renderer.FinishBlock();
|
||||
|
||||
if (obj.LinesAfter != null)
|
||||
{
|
||||
foreach (var line in obj.LinesAfter)
|
||||
{
|
||||
renderer.WriteLine(line.ToString());
|
||||
}
|
||||
}
|
||||
renderer.RenderLinesAfter(parapgraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,15 @@ namespace Markdig.Renderers.Normalize
|
||||
{
|
||||
protected override void Write(NormalizeRenderer renderer, QuoteBlock obj)
|
||||
{
|
||||
var quoteIndent = renderer.Options.SpaceAfterQuoteBlock ? obj.QuoteChar + " " : obj.QuoteChar.ToString();
|
||||
renderer.PushIndent(quoteIndent);
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.PopIndent();
|
||||
renderer.Write(obj.BeforeWhitespace);
|
||||
|
||||
renderer.FinishBlock(true);
|
||||
var quoteIndent = obj.HasSpaceAfterQuoteChar ? obj.QuoteChar + " " : obj.QuoteChar.ToString();
|
||||
//renderer.PushIndent(quoteIndent);
|
||||
renderer.Write(quoteIndent);
|
||||
renderer.WriteChildren(obj);
|
||||
//renderer.PopIndent();
|
||||
|
||||
renderer.FinishBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,8 @@ namespace Markdig.Syntax
|
||||
/// </summary>
|
||||
public bool RemoveAfterProcessInlines { get; set; }
|
||||
|
||||
public string BeforeWhitespace { get; set; }
|
||||
public StringSlice BeforeWhitespace { get; set; }
|
||||
public StringSlice AfterWhitespace { get; set; }
|
||||
|
||||
public List<StringSlice> LinesBefore { get; set; }
|
||||
public List<StringSlice> LinesAfter { get; internal set; }
|
||||
|
||||
@@ -54,8 +54,13 @@ namespace Markdig.Syntax
|
||||
{
|
||||
Lines = new StringLineGroup(4, ProcessInlines);
|
||||
}
|
||||
|
||||
var stringLine = new StringLine(ref slice, line, column, sourceLinePosition);
|
||||
int c = column;
|
||||
if (Lines.Count != 0)
|
||||
{
|
||||
// if not first line, preserve whitespace
|
||||
c = 0;
|
||||
}
|
||||
var stringLine = new StringLine(ref slice, line, c, sourceLinePosition);
|
||||
// Regular case, we are not in the middle of a tab
|
||||
if (slice.CurrentChar != '\t' || !CharHelper.IsAcrossTab(column))
|
||||
{
|
||||
|
||||
@@ -24,5 +24,10 @@ namespace Markdig.Syntax
|
||||
/// Gets or sets the quote character (usually `>`)
|
||||
/// </summary>
|
||||
public char QuoteChar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if a space is parsed between the > character and the paragraph
|
||||
/// </summary>
|
||||
public bool HasSpaceAfterQuoteChar { get; internal set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user