diff --git a/src/Markdig.Tests/TestAutoLinks.cs b/src/Markdig.Tests/TestAutoLinks.cs
new file mode 100644
index 0000000..005c3d1
--- /dev/null
+++ b/src/Markdig.Tests/TestAutoLinks.cs
@@ -0,0 +1,24 @@
+using Markdig.Extensions.AutoLinks;
+
+namespace Markdig.Tests;
+
+[TestFixture]
+public class TestAutoLinks
+{
+ [Test]
+ [TestCase("https://localhost", "
https://localhost
")]
+ [TestCase("http://localhost", "http://localhost
")]
+ [TestCase("https://l", "https://l
")]
+ [TestCase("www.l", "www.l
")]
+ [TestCase("https://localhost:5000", "https://localhost:5000
")]
+ [TestCase("www.l:5000", "www.l:5000
")]
+ public void TestLinksWithAllowDomainWithoutPeriod(string markdown, string expected)
+ {
+ var pipeline = new MarkdownPipelineBuilder()
+ .UseAutoLinks(new AutoLinkOptions { AllowDomainWithoutPeriod = true })
+ .Build();
+ var html = Markdown.ToHtml(markdown, pipeline);
+
+ Assert.That(html, Is.EqualTo(expected).IgnoreWhiteSpace);
+ }
+}
diff --git a/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs b/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs
new file mode 100644
index 0000000..32562cc
--- /dev/null
+++ b/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Alexandre Mutel. All rights reserved.
+// 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;
+
+namespace Markdig.Extensions.AutoIdentifiers;
+
+///
+/// A link reference definition to a stored at the level.
+///
+///
+public class HeadingLinkReferenceDefinition : LinkReferenceDefinition
+{
+ ///
+ /// Initializes a new instance of the HeadingLinkReferenceDefinition class.
+ ///
+ public HeadingLinkReferenceDefinition(HeadingBlock headling)
+ {
+ Heading = headling;
+ }
+
+ ///
+ /// Gets or sets the heading related to this link reference definition.
+ ///
+ public HeadingBlock Heading { get; set; }
+}
diff --git a/src/Markdig/Parsers/Inlines/LinkInlineParser.cs b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs
new file mode 100644
index 0000000..83ac5db
--- /dev/null
+++ b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs
@@ -0,0 +1,450 @@
+// Copyright (c) Alexandre Mutel. All rights reserved.
+// This file is licensed under the BSD-Clause 2 license.
+// See the license.txt file in the project root for more information.
+
+using Markdig.Helpers;
+using Markdig.Renderers.Html;
+using Markdig.Syntax;
+using Markdig.Syntax.Inlines;
+
+namespace Markdig.Parsers.Inlines;
+
+///
+/// An inline parser for .
+///
+///
+public class LinkInlineParser : InlineParser
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LinkInlineParser() : this(new LinkOptions())
+ {
+
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LinkInlineParser(LinkOptions options)
+ {
+ Options = options ?? throw new ArgumentNullException(nameof(options));
+ OpeningCharacters = ['[', ']', '!'];
+ }
+
+ ///
+ /// Gets or sets the options.
+ ///
+ public readonly LinkOptions Options;
+
+ ///
+ /// Attempts to match the parser at the current position.
+ ///
+ public override bool Match(InlineProcessor processor, ref StringSlice slice)
+ {
+ // The following methods are inspired by the "An algorithm for parsing nested emphasis and links"
+ // at the end of the CommonMark specs.
+
+ var c = slice.CurrentChar;
+
+ var startPosition = processor.GetSourcePosition(slice.Start, out int line, out int column);
+
+ bool isImage = false;
+ if (c == '!')
+ {
+ isImage = true;
+ c = slice.NextChar();
+ if (c != '[')
+ {
+ return false;
+ }
+ }
+ string? label;
+ SourceSpan labelWithTriviaSpan = SourceSpan.Empty;
+ switch (c)
+ {
+ case '[':
+ // If this is not an image, we may have a reference link shortcut
+ // so we try to resolve it here
+ var saved = slice;
+
+ SourceSpan labelSpan;
+ // If the label is followed by either a ( or a [, this is not a shortcut
+ if (processor.TrackTrivia)
+ {
+ if (LinkHelper.TryParseLabelTrivia(ref slice, out label, out labelSpan))
+ {
+ labelWithTriviaSpan.Start = labelSpan.Start; // skip opening [
+ labelWithTriviaSpan.End = labelSpan.End; // skip closing ]
+ if (!processor.Document.ContainsLinkReferenceDefinition(label))
+ {
+ label = null;
+ }
+ }
+ }
+ else
+ {
+ if (LinkHelper.TryParseLabel(ref slice, out label, out labelSpan))
+ {
+ if (!processor.Document.ContainsLinkReferenceDefinition(label))
+ {
+ label = null;
+ }
+ }
+ }
+ slice = saved;
+
+ // Else we insert a LinkDelimiter
+ slice.SkipChar();
+ var linkDelimiter = new LinkDelimiterInline(this)
+ {
+ Type = DelimiterType.Open,
+ Label = label,
+ LabelSpan = processor.GetSourcePositionFromLocalSpan(labelSpan),
+ IsImage = isImage,
+ Span = new SourceSpan(startPosition, processor.GetSourcePosition(slice.Start - 1)),
+ Line = line,
+ Column = column
+ };
+
+ if (processor.TrackTrivia)
+ {
+ linkDelimiter.LabelWithTrivia = new StringSlice(slice.Text, labelWithTriviaSpan.Start, labelWithTriviaSpan.End);
+ }
+
+ processor.Inline = linkDelimiter;
+ return true;
+
+ case ']':
+ slice.SkipChar();
+ if (processor.Inline != null)
+ {
+ if (TryProcessLinkOrImage(processor, ref slice))
+ {
+ return true;
+ }
+ }
+
+ // If we don’t find one, we return a literal slice node ].
+ // (Done after by the LiteralInline parser)
+ return false;
+ }
+
+ // We don't have an emphasis
+ return false;
+ }
+
+ private bool ProcessLinkReference(
+ InlineProcessor state,
+ StringSlice text,
+ string label,
+ SourceSpan labelWithriviaSpan,
+ bool isShortcut,
+ SourceSpan labelSpan,
+ LinkDelimiterInline parent,
+ int endPosition,
+ LocalLabel localLabel)
+ {
+ if (!state.Document.TryGetLinkReferenceDefinition(label, out LinkReferenceDefinition? linkRef))
+ {
+ return false;
+ }
+
+ Inline? link = null;
+ // Try to use a callback directly defined on the LinkReferenceDefinition
+ 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
+ if (link is null)
+ {
+ // Inline Link
+ var linkInline = new LinkInline()
+ {
+ Url = HtmlHelper.Unescape(linkRef.Url, removeBackSlash: false),
+ Title = HtmlHelper.Unescape(linkRef.Title, removeBackSlash: false),
+ Label = label,
+ LabelSpan = labelSpan,
+ UrlSpan = linkRef.UrlSpan,
+ IsImage = parent.IsImage,
+ IsShortcut = isShortcut,
+ Reference = linkRef,
+ Span = new SourceSpan(parent.Span.Start, endPosition),
+ Line = parent.Line,
+ Column = parent.Column,
+ };
+
+ if (state.TrackTrivia)
+ {
+ linkInline.LabelWithTrivia = new StringSlice(text.Text, labelWithriviaSpan.Start, labelWithriviaSpan.End);
+ linkInline.LinkRefDefLabel = linkRef.Label;
+ linkInline.LinkRefDefLabelWithTrivia = linkRef.LabelWithTrivia;
+ linkInline.LocalLabel = localLabel;
+ }
+
+ if (Options.OpenInNewWindow)
+ {
+ linkInline.GetAttributes().AddPropertyIfNotExist("target", "_blank");
+ }
+
+ link = linkInline;
+ }
+
+ if (link is ContainerInline containerLink)
+ {
+ var child = parent.FirstChild;
+ if (child is null)
+ {
+ child = new LiteralInline()
+ {
+ Content = StringSlice.Empty,
+ IsClosed = true,
+ // Not exact but we leave it like this
+ Span = parent.Span,
+ Line = parent.Line,
+ Column = parent.Column,
+ };
+ containerLink.AppendChild(child);
+ }
+ else
+ {
+ // Insert all child into the link
+ while (child != null)
+ {
+ var next = child.NextSibling;
+ child.Remove();
+ containerLink.AppendChild(child);
+ child = next;
+ }
+ }
+ }
+
+ link.IsClosed = true;
+
+ // Process emphasis delimiters
+ state.PostProcessInlines(0, link, null, false);
+
+ state.Inline = link;
+
+ return true;
+ }
+
+ private bool TryProcessLinkOrImage(InlineProcessor inlineState, ref StringSlice text)
+ {
+ LinkDelimiterInline? openParent = inlineState.Inline!.FirstParentOfType();
+
+ if (openParent is null)
+ {
+ return false;
+ }
+
+ // If we do find one, but it’s not active,
+ // we remove the inactive delimiter from the stack,
+ // and return a literal text node ].
+ if (!openParent.IsActive)
+ {
+ inlineState.Inline = new LiteralInline()
+ {
+ Content = new StringSlice("["),
+ Span = openParent.Span,
+ Line = openParent.Line,
+ Column = openParent.Column,
+ };
+ openParent.ReplaceBy(inlineState.Inline);
+ return false;
+ }
+
+ // If we find one and it’s active,
+ // then we parse ahead to see if we have
+ // an inline link/image, reference link/image,
+ // compact reference link/image,
+ // or shortcut reference link/image
+ var parentDelimiter = openParent.Parent;
+ var savedText = text;
+
+ if (text.CurrentChar == '(')
+ {
+ LinkInline? link = null;
+
+ if (inlineState.TrackTrivia)
+ {
+ link = TryParseInlineLinkTrivia(ref text, inlineState, openParent);
+ }
+ else
+ {
+ if (LinkHelper.TryParseInlineLink(ref text, out string? url, out string? title, out SourceSpan linkSpan, out SourceSpan titleSpan))
+ {
+ // Inline Link
+ link = new LinkInline()
+ {
+ Url = HtmlHelper.Unescape(url, removeBackSlash: false),
+ Title = title is null ? null : HtmlHelper.Unescape(title, removeBackSlash: false),
+ IsImage = openParent.IsImage,
+ LabelSpan = openParent.LabelSpan,
+ UrlSpan = inlineState.GetSourcePositionFromLocalSpan(linkSpan),
+ TitleSpan = inlineState.GetSourcePositionFromLocalSpan(titleSpan),
+ Span = new SourceSpan(openParent.Span.Start, inlineState.GetSourcePosition(text.Start - 1)),
+ Line = openParent.Line,
+ Column = openParent.Column,
+ };
+ }
+ }
+
+ if (link is not null)
+ {
+ openParent.ReplaceBy(link);
+ // Notifies processor as we are creating an inline locally
+ inlineState.Inline = link;
+
+ // Process emphasis delimiters
+ inlineState.PostProcessInlines(0, link, null, false);
+
+ // If we have a link (and not an image),
+ // we also set all [ delimiters before the opening delimiter to inactive.
+ // (This will prevent us from getting links within links.)
+ if (!openParent.IsImage)
+ {
+ MarkParentAsInactive(parentDelimiter);
+ }
+
+ link.IsClosed = true;
+
+ return true;
+ }
+
+ text = savedText;
+ }
+
+ var labelSpan = SourceSpan.Empty;
+ string? label = null;
+ bool isLabelSpanLocal = true;
+
+ bool isShortcut = false;
+ LocalLabel localLabel = LocalLabel.Local;
+ // Handle Collapsed links
+ if (text.CurrentChar == '[')
+ {
+ if (text.PeekChar() == ']')
+ {
+ label = openParent.Label;
+ labelSpan = openParent.LabelSpan;
+ isLabelSpanLocal = false;
+ localLabel = LocalLabel.Empty;
+ text.SkipChar(); // Skip [
+ text.SkipChar(); // Skip ]
+ }
+ }
+ else
+ {
+ localLabel = LocalLabel.None;
+ label = openParent.Label;
+ isShortcut = true;
+ }
+
+ if (label != null || LinkHelper.TryParseLabelTrivia(ref text, true, out label, out labelSpan))
+ {
+ var labelWithTrivia = new SourceSpan(labelSpan.Start, labelSpan.End);
+ if (isLabelSpanLocal)
+ {
+ labelSpan = inlineState.GetSourcePositionFromLocalSpan(labelSpan);
+ }
+
+ if (ProcessLinkReference(inlineState, text, label!, labelWithTrivia, isShortcut, labelSpan, openParent, inlineState.GetSourcePosition(text.Start - 1), localLabel))
+ {
+ // Remove the open parent
+ openParent.Remove();
+ if (!openParent.IsImage)
+ {
+ MarkParentAsInactive(parentDelimiter);
+ }
+ return true;
+ }
+ else if (text.CurrentChar != ']' && text.CurrentChar != '[')
+ {
+ return false;
+ }
+ }
+
+ // We have a nested [ ]
+ // firstParent.Remove();
+ // The opening [ will be transformed to a literal followed by all the children of the [
+
+ var literal = new LiteralInline()
+ {
+ Span = openParent.Span,
+ Content = new StringSlice(openParent.IsImage ? "![" : "[")
+ };
+
+ inlineState.Inline = openParent.ReplaceBy(literal);
+ return false;
+
+ static LinkInline? TryParseInlineLinkTrivia(ref StringSlice text, InlineProcessor inlineState, LinkDelimiterInline openParent)
+ {
+ if (LinkHelper.TryParseInlineLinkTrivia(
+ ref text,
+ out string? url,
+ out SourceSpan unescapedUrlSpan,
+ out string? title,
+ out SourceSpan unescapedTitleSpan,
+ out char titleEnclosingCharacter,
+ out SourceSpan linkSpan,
+ out SourceSpan titleSpan,
+ out SourceSpan triviaBeforeLink,
+ out SourceSpan triviaAfterLink,
+ out SourceSpan triviaAfterTitle,
+ out bool urlHasPointyBrackets))
+ {
+ var wsBeforeLink = new StringSlice(text.Text, triviaBeforeLink.Start, triviaBeforeLink.End);
+ var wsAfterLink = new StringSlice(text.Text, triviaAfterLink.Start, triviaAfterLink.End);
+ var wsAfterTitle = new StringSlice(text.Text, triviaAfterTitle.Start, triviaAfterTitle.End);
+ var unescapedUrl = new StringSlice(text.Text, unescapedUrlSpan.Start, unescapedUrlSpan.End);
+ var unescapedTitle = new StringSlice(text.Text, unescapedTitleSpan.Start, unescapedTitleSpan.End);
+
+ return new LinkInline()
+ {
+ TriviaBeforeUrl = wsBeforeLink,
+ Url = HtmlHelper.Unescape(url, removeBackSlash: false),
+ UnescapedUrl = unescapedUrl,
+ UrlHasPointyBrackets = urlHasPointyBrackets,
+ TriviaAfterUrl = wsAfterLink,
+ Title = HtmlHelper.Unescape(title, removeBackSlash: false),
+ UnescapedTitle = unescapedTitle,
+ TitleEnclosingCharacter = titleEnclosingCharacter,
+ TriviaAfterTitle = wsAfterTitle,
+ IsImage = openParent.IsImage,
+ LabelSpan = openParent.LabelSpan,
+ UrlSpan = inlineState.GetSourcePositionFromLocalSpan(linkSpan),
+ TitleSpan = inlineState.GetSourcePositionFromLocalSpan(titleSpan),
+ Span = new SourceSpan(openParent.Span.Start, inlineState.GetSourcePosition(text.Start - 1)),
+ Line = openParent.Line,
+ Column = openParent.Column,
+ };
+ }
+
+ return null;
+ }
+ }
+
+ private static void MarkParentAsInactive(Inline? inline)
+ {
+ while (inline != null)
+ {
+ if (inline is LinkDelimiterInline linkInline)
+ {
+ if (linkInline.IsImage)
+ {
+ break;
+ }
+
+ linkInline.IsActive = false;
+ }
+
+ inline = inline.Parent;
+ }
+ }
+}
diff --git a/src/Markdig/Syntax/LinkReferenceDefinition.cs b/src/Markdig/Syntax/LinkReferenceDefinition.cs
new file mode 100644
index 0000000..056d91d
--- /dev/null
+++ b/src/Markdig/Syntax/LinkReferenceDefinition.cs
@@ -0,0 +1,244 @@
+// Copyright (c) Alexandre Mutel. All rights reserved.
+// This file is licensed under the BSD-Clause 2 license.
+// See the license.txt file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+
+using Markdig.Helpers;
+using Markdig.Parsers;
+using Markdig.Syntax.Inlines;
+
+namespace Markdig.Syntax;
+
+///
+/// A link reference definition (Section 4.7 CommonMark specs)
+///
+///
+public class LinkReferenceDefinition : LeafBlock
+{
+ private TriviaProperties? _trivia => TryGetDerivedTrivia();
+ private TriviaProperties Trivia => GetOrSetDerivedTrivia();
+
+ ///
+ /// Creates an inline link for the specified .
+ ///
+ /// State of the inline.
+ /// The link reference.
+ /// The child.
+ /// An inline link or null to use the default implementation
+ public delegate Inline CreateLinkInlineDelegate(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline? child = null);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LinkReferenceDefinition() : base(null)
+ {
+ IsOpen = false;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The label.
+ /// The URL.
+ /// The title.
+ public LinkReferenceDefinition(string? label, string? url, string? title) : this()
+ {
+ Label = label;
+ Url = url;
+ Title = title;
+ }
+
+ ///
+ /// Gets or sets the label. Text is normalized according to spec.
+ ///
+ /// https://spec.commonmark.org/0.29/#matches
+ public string? Label { get; set; }
+
+ ///
+ /// The label span
+ ///
+ public SourceSpan LabelSpan;
+
+ ///
+ /// Non-normalized Label (includes trivia)
+ /// Trivia: only parsed when is enabled, otherwise
+ /// .
+ ///
+ public StringSlice LabelWithTrivia { get => _trivia?.LabelWithTrivia ?? StringSlice.Empty; set => Trivia.LabelWithTrivia = value; }
+
+ ///
+ /// Whitespace before the .
+ /// Trivia: only parsed when is enabled, otherwise
+ /// .
+ ///
+ public StringSlice TriviaBeforeUrl { get => _trivia?.TriviaBeforeUrl ?? StringSlice.Empty; set => Trivia.TriviaBeforeUrl = value; }
+
+ ///
+ /// Gets or sets the URL.
+ ///
+ public string? Url { get; set; }
+
+ ///
+ /// The URL span
+ ///
+ public SourceSpan UrlSpan;
+
+ ///
+ /// Non-normalized .
+ /// Trivia: only parsed when is enabled, otherwise
+ /// .
+ ///
+ public StringSlice UnescapedUrl { get => _trivia?.UnescapedUrl ?? StringSlice.Empty; set => Trivia.UnescapedUrl = value; }
+
+ ///
+ /// True when the is enclosed in point brackets in the source document.
+ /// Trivia: only parsed when is enabled, otherwise
+ /// false.
+ ///
+ public bool UrlHasPointyBrackets { get => _trivia?.UrlHasPointyBrackets ?? false; set => Trivia.UrlHasPointyBrackets = value; }
+
+ ///
+ /// gets or sets the whitespace before a .
+ /// Trivia: only parsed when is enabled, otherwise
+ /// .
+ ///
+ public StringSlice TriviaBeforeTitle { get => _trivia?.TriviaBeforeTitle ?? StringSlice.Empty; set => Trivia.TriviaBeforeTitle = value; }
+
+ ///
+ /// Gets or sets the title.
+ ///
+ public string? Title { get; set; }
+
+ ///
+ /// The title span
+ ///
+ public SourceSpan TitleSpan;
+
+ ///
+ /// Non-normalized .
+ /// Trivia: only parsed when is enabled, otherwise
+ /// .
+ ///
+ public StringSlice UnescapedTitle { get => _trivia?.UnescapedTitle ?? StringSlice.Empty; set => Trivia.UnescapedTitle = value; }
+
+ ///
+ /// Gets or sets the character the is enclosed in.
+ /// Trivia: only parsed when is enabled, otherwise \0.
+ ///
+ public char TitleEnclosingCharacter { get => _trivia?.TitleEnclosingCharacter ?? default; set => Trivia.TitleEnclosingCharacter = value; }
+
+ ///
+ /// Gets or sets the create link inline callback for this instance.
+ ///
+ ///
+ /// This callback is called when an inline link is matching this reference definition.
+ ///
+ public CreateLinkInlineDelegate? CreateLinkInline { get; set; }
+
+ ///
+ /// Tries to the parse the specified text into a definition.
+ ///
+ /// Type of the text
+ /// The text.
+ /// The block.
+ /// true if parsing is successful; false otherwise
+ public static bool TryParse(ref T text, [NotNullWhen(true)] out LinkReferenceDefinition? block) where T : ICharIterator
+ {
+ block = null;
+
+ var startSpan = text.Start;
+
+ if (!LinkHelper.TryParseLinkReferenceDefinition(ref text, out string? label, out string? url, out string? title, out SourceSpan labelSpan, out SourceSpan urlSpan, out SourceSpan titleSpan))
+ {
+ return false;
+ }
+
+ block = new LinkReferenceDefinition(label, url, title)
+ {
+ LabelSpan = labelSpan,
+ UrlSpan = urlSpan,
+ TitleSpan = titleSpan,
+ Span = new SourceSpan(startSpan, titleSpan.End > 0 ? titleSpan.End: urlSpan.End)
+ };
+ return true;
+ }
+
+ ///
+ /// Tries to the parse the specified text into a definition.
+ ///
+ /// Type of the text
+ /// The text.
+ /// The block.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// true if parsing is successful; false otherwise
+ public static bool TryParseTrivia(
+ ref T text,
+ [NotNullWhen(true)] out LinkReferenceDefinition? block,
+ out SourceSpan triviaBeforeLabel,
+ out SourceSpan labelWithTrivia,
+ out SourceSpan triviaBeforeUrl,
+ out SourceSpan unescapedUrl,
+ out SourceSpan triviaBeforeTitle,
+ out SourceSpan unescapedTitle,
+ out SourceSpan triviaAfterTitle) where T : ICharIterator
+ {
+ block = null;
+
+ var startSpan = text.Start;
+
+ if (!LinkHelper.TryParseLinkReferenceDefinitionTrivia(
+ ref text,
+ out triviaBeforeLabel,
+ out string? label,
+ out labelWithTrivia,
+ out triviaBeforeUrl,
+ out string? url,
+ out unescapedUrl,
+ out bool urlHasPointyBrackets,
+ out triviaBeforeTitle,
+ out string? title,
+ out unescapedTitle,
+ out char titleEnclosingCharacter,
+ out NewLine newLine,
+ out triviaAfterTitle,
+ out SourceSpan labelSpan,
+ out SourceSpan urlSpan,
+ out SourceSpan titleSpan))
+ {
+ return false;
+ }
+
+ block = new LinkReferenceDefinition(label, url, title)
+ {
+ UrlHasPointyBrackets = urlHasPointyBrackets,
+ TitleEnclosingCharacter = titleEnclosingCharacter,
+ //LabelWithWhitespace = labelWithWhitespace,
+ LabelSpan = labelSpan,
+ UrlSpan = urlSpan,
+ //UnescapedUrl = unescapedUrl,
+ //UnescapedTitle = unescapedTitle,
+ TitleSpan = titleSpan,
+ Span = new SourceSpan(startSpan, titleSpan.End > 0 ? titleSpan.End : urlSpan.End),
+ NewLine = newLine,
+ };
+ return true;
+ }
+
+ private sealed class TriviaProperties
+ {
+ public StringSlice LabelWithTrivia;
+ public StringSlice TriviaBeforeUrl;
+ public StringSlice UnescapedUrl;
+ public bool UrlHasPointyBrackets;
+ public StringSlice TriviaBeforeTitle;
+ public StringSlice UnescapedTitle;
+ public char TitleEnclosingCharacter;
+ }
+}
\ No newline at end of file