mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 05:44:50 +00:00
Merge pull request #837 from snnz/fix-links
Fix errors in LinkHelper and LinkInlineParser.
This commit is contained in:
@@ -89,6 +89,14 @@ public class TestLinkHelper
|
||||
Assert.AreEqual("this\ris\r\na\ntitle", title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTitleMultilineWithSpaceAndBackslash()
|
||||
{
|
||||
var text = new StringSlice("'a\n\\ \\\ntitle'");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual("a\n\\ \\\ntitle", title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAndTitle()
|
||||
{
|
||||
@@ -238,6 +246,13 @@ public class TestLinkHelper
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestlLinkReferenceDefinitionInvalid()
|
||||
{
|
||||
var text = new StringSlice("[foo]: /url (title) x\n");
|
||||
Assert.False(LinkHelper.TryParseLinkReferenceDefinition(ref text, out _, out _, out _, out _, out _, out _));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoLinkUrlSimple()
|
||||
{
|
||||
|
||||
@@ -46,6 +46,14 @@ public class TestPlayParser
|
||||
Assert.AreEqual("/yoyo", link?.Url);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinkWithMultipleBackslashesInTitle()
|
||||
{
|
||||
var doc = Markdown.Parse(@"[link](/uri '\\\\127.0.0.1')");
|
||||
var link = doc.Descendants<LinkInline>().FirstOrDefault();
|
||||
Assert.AreEqual(@"\\127.0.0.1", link?.Title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestListBug2()
|
||||
{
|
||||
|
||||
@@ -545,88 +545,70 @@ public static class LinkHelper
|
||||
enclosingCharacter = c;
|
||||
var closingQuote = c == '(' ? ')' : c;
|
||||
bool hasEscape = false;
|
||||
// -1: undefined
|
||||
// 0: has only spaces
|
||||
// 1: has other characters
|
||||
int hasOnlyWhiteSpacesSinceLastLine = -1;
|
||||
while (true)
|
||||
bool isLineBlank = false; // the first line is never blank
|
||||
while ((c = text.NextChar()) != '\0')
|
||||
{
|
||||
c = text.NextChar();
|
||||
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine >= 0)
|
||||
if (isLineBlank)
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
hasOnlyWhiteSpacesSinceLastLine = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasEscape)
|
||||
{
|
||||
hasEscape = false;
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
|
||||
if (c == '\r' && text.PeekChar() == '\n')
|
||||
{
|
||||
buffer.Append('\n');
|
||||
text.SkipChar();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
break;
|
||||
isLineBlank = true;
|
||||
}
|
||||
|
||||
if (c == closingQuote)
|
||||
else if (hasEscape)
|
||||
{
|
||||
if (hasEscape)
|
||||
hasEscape = false;
|
||||
|
||||
if (!c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append(closingQuote);
|
||||
hasEscape = false;
|
||||
continue;
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
}
|
||||
else if (c == closingQuote)
|
||||
{
|
||||
// Skip last quote
|
||||
text.SkipChar();
|
||||
goto ReturnValid;
|
||||
title = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
else if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
continue;
|
||||
isLineBlank = false;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
|
||||
if (c.IsSpaceOrTab())
|
||||
else
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine < 0)
|
||||
if (isLineBlank && !c.IsSpaceOrTab())
|
||||
{
|
||||
hasOnlyWhiteSpacesSinceLastLine = 1;
|
||||
isLineBlank = false;
|
||||
}
|
||||
}
|
||||
else if (c != '\n' && c != '\r' && text.PeekChar() != '\n')
|
||||
{
|
||||
hasOnlyWhiteSpacesSinceLastLine = 0;
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
buffer.Append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Dispose();
|
||||
title = null;
|
||||
return false;
|
||||
|
||||
ReturnValid:
|
||||
title = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseTitleTrivia<T>(ref T text, out string? title, out char enclosingCharacter) where T : ICharIterator
|
||||
@@ -642,88 +624,70 @@ public static class LinkHelper
|
||||
enclosingCharacter = c;
|
||||
var closingQuote = c == '(' ? ')' : c;
|
||||
bool hasEscape = false;
|
||||
// -1: undefined
|
||||
// 0: has only spaces
|
||||
// 1: has other characters
|
||||
int hasOnlyWhiteSpacesSinceLastLine = -1;
|
||||
while (true)
|
||||
bool isLineBlank = false; // the first line is never blank
|
||||
while ((c = text.NextChar()) != '\0')
|
||||
{
|
||||
c = text.NextChar();
|
||||
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine >= 0)
|
||||
if (isLineBlank)
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
hasOnlyWhiteSpacesSinceLastLine = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasEscape)
|
||||
{
|
||||
hasEscape = false;
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
|
||||
if (c == '\r' && text.PeekChar() == '\n')
|
||||
{
|
||||
buffer.Append('\n');
|
||||
text.SkipChar();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
break;
|
||||
isLineBlank = true;
|
||||
}
|
||||
|
||||
if (c == closingQuote)
|
||||
else if (hasEscape)
|
||||
{
|
||||
if (hasEscape)
|
||||
hasEscape = false;
|
||||
|
||||
if (!c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append(closingQuote);
|
||||
hasEscape = false;
|
||||
continue;
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
}
|
||||
else if (c == closingQuote)
|
||||
{
|
||||
// Skip last quote
|
||||
text.SkipChar();
|
||||
goto ReturnValid;
|
||||
title = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
else if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
continue;
|
||||
isLineBlank = false;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
|
||||
if (c.IsSpaceOrTab())
|
||||
else
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine < 0)
|
||||
if (isLineBlank && !c.IsSpaceOrTab())
|
||||
{
|
||||
hasOnlyWhiteSpacesSinceLastLine = 1;
|
||||
isLineBlank = false;
|
||||
}
|
||||
}
|
||||
else if (c != '\n' && c != '\r' && text.PeekChar() != '\n')
|
||||
{
|
||||
hasOnlyWhiteSpacesSinceLastLine = 0;
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
buffer.Append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Dispose();
|
||||
title = null;
|
||||
return false;
|
||||
|
||||
ReturnValid:
|
||||
title = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseUrl<T>(T text, [NotNullWhen(true)] out string? link) where T : ICharIterator
|
||||
@@ -760,12 +724,15 @@ public static class LinkHelper
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
if (hasEscape)
|
||||
{
|
||||
buffer.Append('\\');
|
||||
hasEscape = false;
|
||||
if (!c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
else if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
continue;
|
||||
@@ -776,8 +743,6 @@ public static class LinkHelper
|
||||
break;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
|
||||
buffer.Append(c);
|
||||
|
||||
} while (c != '\0');
|
||||
@@ -816,20 +781,21 @@ public static class LinkHelper
|
||||
|
||||
if (!isAutoLink)
|
||||
{
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
if (hasEscape)
|
||||
{
|
||||
buffer.Append('\\');
|
||||
hasEscape = false;
|
||||
if (!c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
}
|
||||
|
||||
// If we have an escape
|
||||
if (c == '\\')
|
||||
else if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
c = text.NextChar();
|
||||
continue;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
}
|
||||
|
||||
if (IsEndOfUri(c, isAutoLink))
|
||||
@@ -907,12 +873,15 @@ public static class LinkHelper
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
if (hasEscape)
|
||||
{
|
||||
buffer.Append('\\');
|
||||
hasEscape = false;
|
||||
if (!c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
else if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
continue;
|
||||
@@ -923,8 +892,6 @@ public static class LinkHelper
|
||||
break;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
|
||||
buffer.Append(c);
|
||||
|
||||
} while (c != '\0');
|
||||
@@ -963,20 +930,21 @@ public static class LinkHelper
|
||||
|
||||
if (!isAutoLink)
|
||||
{
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
if (hasEscape)
|
||||
{
|
||||
buffer.Append('\\');
|
||||
hasEscape = false;
|
||||
if (!c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
}
|
||||
|
||||
// If we have an escape
|
||||
if (c == '\\')
|
||||
else if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
c = text.NextChar();
|
||||
continue;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
}
|
||||
|
||||
if (IsEndOfUri(c, isAutoLink))
|
||||
@@ -1161,7 +1129,7 @@ public static class LinkHelper
|
||||
c = text.NextChar();
|
||||
}
|
||||
|
||||
if (c != '\0' && c != '\n' && c != '\r' && text.PeekChar() != '\n')
|
||||
if (c != '\0' && c != '\n' && c != '\r')
|
||||
{
|
||||
// If we were able to parse the url but the title doesn't end with space,
|
||||
// we are still returning a valid definition
|
||||
@@ -1301,7 +1269,7 @@ public static class LinkHelper
|
||||
c = text.NextChar();
|
||||
}
|
||||
|
||||
if (c != '\0' && c != '\n' && c != '\r' && text.PeekChar() != '\n')
|
||||
if (c != '\0' && c != '\n' && c != '\r')
|
||||
{
|
||||
// If we were able to parse the url but the title doesn't end with space,
|
||||
// we are still returning a valid definition
|
||||
|
||||
@@ -137,6 +137,9 @@ public class LinkInlineParser : InlineParser
|
||||
if (linkRef.CreateLinkInline != null)
|
||||
{
|
||||
link = linkRef.CreateLinkInline(state, linkRef, parent.FirstChild);
|
||||
link.Span = new SourceSpan(parent.Span.Start, endPosition);
|
||||
link.Line = parent.Line;
|
||||
link.Column = parent.Column;
|
||||
}
|
||||
|
||||
// Create a default link if the callback was not found
|
||||
@@ -145,8 +148,8 @@ public class LinkInlineParser : InlineParser
|
||||
// Inline Link
|
||||
var linkInline = new LinkInline()
|
||||
{
|
||||
Url = HtmlHelper.Unescape(linkRef.Url),
|
||||
Title = HtmlHelper.Unescape(linkRef.Title),
|
||||
Url = HtmlHelper.Unescape(linkRef.Url, removeBackSlash: false),
|
||||
Title = HtmlHelper.Unescape(linkRef.Title, removeBackSlash: false),
|
||||
Label = label,
|
||||
LabelSpan = labelSpan,
|
||||
UrlSpan = linkRef.UrlSpan,
|
||||
@@ -256,8 +259,8 @@ public class LinkInlineParser : InlineParser
|
||||
// Inline Link
|
||||
link = new LinkInline()
|
||||
{
|
||||
Url = HtmlHelper.Unescape(url),
|
||||
Title = HtmlHelper.Unescape(title),
|
||||
Url = HtmlHelper.Unescape(url, removeBackSlash: false),
|
||||
Title = HtmlHelper.Unescape(title, removeBackSlash: false),
|
||||
IsImage = openParent.IsImage,
|
||||
LabelSpan = openParent.LabelSpan,
|
||||
UrlSpan = inlineState.GetSourcePositionFromLocalSpan(linkSpan),
|
||||
@@ -382,11 +385,11 @@ public class LinkInlineParser : InlineParser
|
||||
return new LinkInline()
|
||||
{
|
||||
TriviaBeforeUrl = wsBeforeLink,
|
||||
Url = HtmlHelper.Unescape(url),
|
||||
Url = HtmlHelper.Unescape(url, removeBackSlash: false),
|
||||
UnescapedUrl = unescapedUrl,
|
||||
UrlHasPointyBrackets = urlHasPointyBrackets,
|
||||
TriviaAfterUrl = wsAfterLink,
|
||||
Title = HtmlHelper.Unescape(title),
|
||||
Title = HtmlHelper.Unescape(title, removeBackSlash: false),
|
||||
UnescapedTitle = unescapedTitle,
|
||||
TitleEnclosingCharacter = titleEnclosingCharacter,
|
||||
TriviaAfterTitle = wsAfterTitle,
|
||||
|
||||
Reference in New Issue
Block a user