Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion src/Spectre.Console.Ansi/AnsiMarkup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,60 @@ public static IEnumerable<AnsiMarkupSegment> Parse(string markup, Style? style =
throw new InvalidOperationException("Unbalanced markup stack. Did you forget to close a tag?");
}

// Try resolving auto links (if any)
ResolveAutoLinks(result);

return result;
}

private static void ResolveAutoLinks(List<AnsiMarkupSegment> segments)
{
for (var segmentIndex = 0; segmentIndex < segments.Count; segmentIndex++)
{
var currentLink = segments[segmentIndex].Link;
if (currentLink?.Url.Equals(Constants.EmptyLink, StringComparison.Ordinal) != true)
{
// Not a link
continue;
}

// Find out where the link ends
var lastKnownLinkIndex = segmentIndex + 1;
while (lastKnownLinkIndex < segments.Count && ReferenceEquals(segments[lastKnownLinkIndex].Link, currentLink))
{
lastKnownLinkIndex++;
}

string url;
if (lastKnownLinkIndex - segmentIndex == 1)
{
// This is a plain [link]url[/], so just grab the text
url = segments[segmentIndex].Text;
}
else
{
// Build the URL using the text from the segments
var builder = new StringBuilder();
for (var i = segmentIndex; i < lastKnownLinkIndex; i++)
{
builder.Append(segments[i].Text);
}

url = builder.ToString();
}

// Set all segments to the same link
var resolved = new Link(url);
for (var i = segmentIndex; i < lastKnownLinkIndex; i++)
{
segments[i].Link = resolved;
}

// Continue at the next part
segmentIndex = lastKnownLinkIndex - 1;
}
}

/// <summary>
/// Escapes the specified text so that it won’t be interpreted as markup.
/// </summary>
Expand Down Expand Up @@ -183,7 +234,7 @@ public sealed class AnsiMarkupSegment
/// <summary>
/// Gets the segment link.
/// </summary>
public Link? Link { get; }
public Link? Link { get; internal set; }

/// <summary>
/// Initializes a new instance of the <see cref="AnsiMarkupSegment"/> class.
Expand Down
3 changes: 1 addition & 2 deletions src/Spectre.Console.Ansi/AnsiWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ public AnsiWriter Write(string text, Style style, Link? link = null)
{
if (link != null)
{
var url = link.Url.Equals(Constants.EmptyLink) ? text : link.Url;
BeginLink(url, link.Id);
BeginLink(link.Url, link.Id);
}

_styleBuffer.Clear();
Expand Down
56 changes: 53 additions & 3 deletions src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,9 @@ public void Should_Preserve_Link_When_Wrapped_Inside_Grid_Cell()
.SupportsAnsi(true)
.EmitAnsiSequences();

var grid = new Grid();
grid.AddColumn();
grid.AddRow("[link=https://example.com/readme.md]pneumonoultramicroscopicsilicovolcanoconiosis[/]");
var grid = new Grid()
.AddColumn()
.AddRow("[link=https://example.com/readme.md]pneumonoultramicroscopicsilicovolcanoconiosis[/]");

// When
console.Write(grid);
Expand All @@ -279,5 +279,55 @@ public void Should_Preserve_Link_When_Wrapped_Inside_Grid_Cell()
"https://example.com/readme.md")
.Count.ShouldBeGreaterThan(1);
}

[Fact]
[GitHubIssue("https://github.com/spectreconsole/spectre.console/issues/2145")]
public void Should_Preserve_Auto_Link_When_Wrapped_Inside_Grid_Cell()
{
// Given
var console = new TestConsole()
.Width(10)
.SupportsAnsi(true)
.EmitAnsiSequences();

var grid = new Grid()
.AddColumn()
.AddRow("[link]https://example.com/readme.md[/]");

// When
console.Write(grid);

// Then
Regex.Matches(
console.Output.NormalizeLineEndings(),
"https://example.com/readme.md")
.Count.ShouldBeGreaterThan(1);
}

[Fact]
[GitHubIssue("https://github.com/spectreconsole/spectre.console/issues/2145")]
public void Should_Resolve_Auto_Link_Url_From_Display_Text()
{
// Given, When
var segments = AnsiMarkup.Parse("[link]https://example.com/readme.md[/]").ToList();

// Then
segments.Count.ShouldBe(1);
segments[0].Link.ShouldNotBeNull();
segments[0].Link!.Url.ShouldBe("https://example.com/readme.md");
}

[Fact]
[GitHubIssue("https://github.com/spectreconsole/spectre.console/issues/2145")]
public void Should_Resolve_Auto_Link_Url_From_Full_Text_When_Nested()
{
// Given, When
var segments = AnsiMarkup.Parse("[link]https://[bold]example[/].com[/]").ToList();

// Then
segments.Count.ShouldBeGreaterThan(1);
segments.ShouldAllBe(segment => segment.Link!.Url == "https://example.com");
segments.Select(segment => segment.Link!.Id).Distinct().Count().ShouldBe(1);
}
}
}