diff --git a/src/Markdig.Tests/RoundtripSpecs/TestLinkReferenceDefinition.cs b/src/Markdig.Tests/RoundtripSpecs/TestLinkReferenceDefinition.cs index a272df0d..ccc2834a 100644 --- a/src/Markdig.Tests/RoundtripSpecs/TestLinkReferenceDefinition.cs +++ b/src/Markdig.Tests/RoundtripSpecs/TestLinkReferenceDefinition.cs @@ -209,4 +209,16 @@ public void TestSetextHeader(string value) { RoundTrip(value); } + + [TestCase("> [foo]: /url 'the title'\n")] + [TestCase("> [foo]: /url\n> 'the title'\n")] + [TestCase("> [foo]:\n> /url 'the title'\n")] + [TestCase("> [foo]:\n> /url\n> 'the title'\n")] + [TestCase("> [foo]:\n> /url\n>\n> [foo]\n")] + [TestCase("> [foo]:\n> /url 'the title'\n>\n> [foo]\n")] + [TestCase("> [foo]:\n> /url\n> 'the title'\n>\n> [foo]\n")] + public void TestMultilineInBlockquote(string value) + { + RoundTrip(value); + } } diff --git a/src/Markdig/Helpers/StringLineGroup.cs b/src/Markdig/Helpers/StringLineGroup.cs index 14b6680a..22072f45 100644 --- a/src/Markdig/Helpers/StringLineGroup.cs +++ b/src/Markdig/Helpers/StringLineGroup.cs @@ -2,6 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using Markdig.Syntax; using System.Collections; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -190,6 +191,33 @@ public void Trim() } } + internal SourceSpan ConvertToAbsoluteSpan(SourceSpan span) + { + if (span.IsEmpty || Count == 0) return span; + + var startPosition = GetAbsolutePosition(span.Start); + var endPosition = GetAbsolutePosition(span.End); + + return new SourceSpan(startPosition, endPosition); + } + + private int GetAbsolutePosition(int position) + { + int offset = 0; + for (int i = 0; i < Count; i++) + { + ref StringSlice slice = ref Lines[i].Slice; + var lineLength = slice.Length + slice.NewLine.Length(); + + if (i == Count - 1 || position < offset + lineLength) + { + return slice.Start + (position - offset); + } + offset += lineLength; + } + return Lines[Count - 1].Slice.End + 1; + } + /// /// Gets or sets the enumerator. /// diff --git a/src/Markdig/Parsers/ParagraphBlockParser.cs b/src/Markdig/Parsers/ParagraphBlockParser.cs index 3773b3c1..71412ee0 100644 --- a/src/Markdig/Parsers/ParagraphBlockParser.cs +++ b/src/Markdig/Parsers/ParagraphBlockParser.cs @@ -212,13 +212,11 @@ private static bool TryMatchLinkReferenceDefinition(ref StringLineGroup lines, B // Correct the locations of each field linkReferenceDefinition.Line = lines.Lines[0].Line; - int startPosition = lines.Lines[0].Slice.Start; - - linkReferenceDefinition.Span = linkReferenceDefinition.Span .MoveForward(startPosition); - linkReferenceDefinition.LabelSpan = linkReferenceDefinition.LabelSpan .MoveForward(startPosition); - linkReferenceDefinition.UrlSpan = linkReferenceDefinition.UrlSpan .MoveForward(startPosition); - linkReferenceDefinition.TitleSpan = linkReferenceDefinition.TitleSpan .MoveForward(startPosition); + linkReferenceDefinition.Span = lines.ConvertToAbsoluteSpan(linkReferenceDefinition.Span); + linkReferenceDefinition.LabelSpan = lines.ConvertToAbsoluteSpan(linkReferenceDefinition.LabelSpan); + linkReferenceDefinition.UrlSpan = lines.ConvertToAbsoluteSpan(linkReferenceDefinition.UrlSpan); + linkReferenceDefinition.TitleSpan = lines.ConvertToAbsoluteSpan(linkReferenceDefinition.TitleSpan); lines = iterator.Remaining(); } else @@ -256,24 +254,23 @@ private static bool TryMatchLinkReferenceDefinitionTrivia(ref StringLineGroup li // Correct the locations of each field lrd.Line = lines.Lines[0].Line; var text = lines.Lines[0].Slice.Text; - int startPosition = lines.Lines[0].Slice.Start; - - triviaBeforeLabel = triviaBeforeLabel.MoveForward(startPosition); - labelWithTrivia = labelWithTrivia.MoveForward(startPosition); - triviaBeforeUrl = triviaBeforeUrl.MoveForward(startPosition); - unescapedUrl = unescapedUrl.MoveForward(startPosition); - triviaBeforeTitle = triviaBeforeTitle.MoveForward(startPosition); - unescapedTitle = unescapedTitle.MoveForward(startPosition); - triviaAfterTitle = triviaAfterTitle.MoveForward(startPosition); - lrd.Span = lrd.Span.MoveForward(startPosition); + + triviaBeforeLabel = lines.ConvertToAbsoluteSpan(triviaBeforeLabel); + labelWithTrivia = lines.ConvertToAbsoluteSpan(labelWithTrivia); + triviaBeforeUrl = lines.ConvertToAbsoluteSpan(triviaBeforeUrl); + unescapedUrl = lines.ConvertToAbsoluteSpan(unescapedUrl); + triviaBeforeTitle = lines.ConvertToAbsoluteSpan(triviaBeforeTitle); + unescapedTitle = lines.ConvertToAbsoluteSpan(unescapedTitle); + triviaAfterTitle = lines.ConvertToAbsoluteSpan(triviaAfterTitle); + lrd.Span = lines.ConvertToAbsoluteSpan(lrd.Span); lrd.TriviaBefore = new StringSlice(text, triviaBeforeLabel.Start, triviaBeforeLabel.End); - lrd.LabelSpan = lrd.LabelSpan.MoveForward(startPosition); + lrd.LabelSpan = lines.ConvertToAbsoluteSpan(lrd.LabelSpan); lrd.LabelWithTrivia = new StringSlice(text, labelWithTrivia.Start, labelWithTrivia.End); lrd.TriviaBeforeUrl = new StringSlice(text, triviaBeforeUrl.Start, triviaBeforeUrl.End); - lrd.UrlSpan = lrd.UrlSpan.MoveForward(startPosition); + lrd.UrlSpan = lines.ConvertToAbsoluteSpan(lrd.UrlSpan); lrd.UnescapedUrl = new StringSlice(text, unescapedUrl.Start, unescapedUrl.End); lrd.TriviaBeforeTitle = new StringSlice(text, triviaBeforeTitle.Start, triviaBeforeTitle.End); - lrd.TitleSpan = lrd.TitleSpan.MoveForward(startPosition); + lrd.TitleSpan = lines.ConvertToAbsoluteSpan(lrd.TitleSpan); lrd.UnescapedTitle = new StringSlice(text, unescapedTitle.Start, unescapedTitle.End); lrd.TriviaAfter = new StringSlice(text, triviaAfterTitle.Start, triviaAfterTitle.End); lrd.LinesBefore = paragraph.LinesBefore; @@ -292,4 +289,5 @@ private static bool TryMatchLinkReferenceDefinitionTrivia(ref StringLineGroup li return atLeastOneFound; } + } diff --git a/src/Markdig/Renderers/TextRendererBase.cs b/src/Markdig/Renderers/TextRendererBase.cs index 199ecb19..983f9c95 100644 --- a/src/Markdig/Renderers/TextRendererBase.cs +++ b/src/Markdig/Renderers/TextRendererBase.cs @@ -320,6 +320,10 @@ public void Write(ReadOnlySpan content) { WriteIndent(); WriteRaw(content); + if (content[content.Length - 1] == '\n') + { + previousWasLine = true; + } } }