handle whitespace before and after paragraphs correctly

This commit is contained in:
Ruud Poutsma
2020-09-25 17:07:10 +02:00
parent 0234d60d74
commit f78b5c83cd
19 changed files with 462 additions and 184 deletions

View File

@@ -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>

View 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(" ![description](http://example.com)");
RoundTrip("paragraph ![description](http://example.com)");
}
[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("![](a)")]
[TestCase(" ![](a)")]
[TestCase("![](a) ")]
[TestCase(" ![](a) ")]
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);
}
}
}

View File

@@ -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 = " ![ description ]( http://example.com )";
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 = " ![description](http://example.com)";
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());
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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))

View File

@@ -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>

View File

@@ -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()

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -22,6 +22,7 @@ namespace Markdig.Parsers
{
result = new ListInfo(state.CurrentChar);
state.NextChar();
state.NextChar();
return true;
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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; }

View File

@@ -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))
{

View File

@@ -24,5 +24,10 @@ namespace Markdig.Syntax
/// Gets or sets the quote character (usually `&gt;`)
/// </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; }
}
}