113 lines
3 KiB
C#
113 lines
3 KiB
C#
using System.Linq;
|
|
|
|
using Markdig.Helpers;
|
|
using Markdig.Parsers;
|
|
using Markdig.Syntax.Inlines;
|
|
|
|
namespace MfGames.Markdown.Extensions;
|
|
|
|
public class WikiLinkInlineParser : InlineParser
|
|
{
|
|
private readonly WikiLinkOptions options;
|
|
|
|
public WikiLinkInlineParser(WikiLinkOptions options)
|
|
{
|
|
this.options = options;
|
|
this.OpeningCharacters = new[] { '[' };
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool Match(
|
|
InlineProcessor processor,
|
|
ref StringSlice slice)
|
|
{
|
|
// We are looking for the `[[` opening for the tag and that the first
|
|
// one isn't escaped.
|
|
if (IsNotDelimiter(slice, '['))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// We need to loop over the entire link, including the `[[` and `]]`
|
|
// while keeping track since we'll swallow additional characters beyond
|
|
// the link.
|
|
int linkStart = slice.Start;
|
|
int linkEnd = slice.Start;
|
|
|
|
slice.Start += 2;
|
|
|
|
// Our content starts after the double '[['.
|
|
int contentStart = slice.Start;
|
|
|
|
// We need to find the end of the link (the `]]`).
|
|
while (IsNotDelimiter(slice, ']'))
|
|
{
|
|
slice.NextChar();
|
|
linkEnd = slice.Start;
|
|
}
|
|
|
|
// Pull out the components before we adjust for the ']]' for the end.
|
|
int contentEnd = linkEnd;
|
|
|
|
// Finish skipping over the `]]`.
|
|
slice.NextChar();
|
|
slice.NextChar();
|
|
|
|
// Format the label and the URL.
|
|
string content = slice.Text.Substring(
|
|
contentStart,
|
|
contentEnd - contentStart);
|
|
string[] contentParts = content.Split('|', 2);
|
|
string label = contentParts.Last();
|
|
string url = this.options.GetUrl(contentParts.First());
|
|
|
|
// Add in any trailing components. This merges the `'s` from
|
|
// `[[Dale]]'s` into the label.
|
|
while (this.options.IsTrailingLink(slice.CurrentChar))
|
|
{
|
|
label += slice.CurrentChar;
|
|
slice.NextChar();
|
|
linkEnd++;
|
|
}
|
|
|
|
// Create the link that we're replacing.
|
|
WikiLink link = new()
|
|
{
|
|
Span =
|
|
{
|
|
Start = processor.GetSourcePosition(
|
|
linkStart,
|
|
out int line,
|
|
out int column),
|
|
},
|
|
Line = line,
|
|
Column = column,
|
|
Url = url,
|
|
IsClosed = true,
|
|
};
|
|
|
|
link.AppendChild(
|
|
new LiteralInline()
|
|
{
|
|
Line = line,
|
|
Column = column,
|
|
Content = new StringSlice(label),
|
|
IsClosed = true,
|
|
});
|
|
|
|
// Replace the inline and then indicate we have a match.
|
|
processor.Inline = link;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool IsNotDelimiter(
|
|
StringSlice slice,
|
|
char delimiter)
|
|
{
|
|
return slice.CurrentChar != delimiter
|
|
|| slice.PeekChar() != delimiter
|
|
|| slice.PeekCharExtra(-1) == '\\';
|
|
}
|
|
}
|