From c998a0ee737513be2aec4f4e45136c63e3bc203e Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 10 Jan 2025 17:49:01 +0100 Subject: [PATCH 1/8] Add exteranl links to links.json --- docs/source/docset.yml | 1 + docs/source/testing/external-links.md | 7 ++ .../Diagnostics/DiagnosticsChannel.cs | 4 ++ src/Elastic.Markdown/IO/LinkReference.cs | 8 ++- .../DiagnosticLinkInlineParser.cs | 29 ++++---- .../Inline/InlineLinkTests.cs | 71 +++++++++++++++++++ 6 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 docs/source/testing/external-links.md diff --git a/docs/source/docset.yml b/docs/source/docset.yml index c5deef422..b18eaaaa5 100644 --- a/docs/source/docset.yml +++ b/docs/source/docset.yml @@ -77,3 +77,4 @@ toc: - file: index.md - file: req.md - folder: nested + - file: external-links.md diff --git a/docs/source/testing/external-links.md b/docs/source/testing/external-links.md new file mode 100644 index 000000000..3318de633 --- /dev/null +++ b/docs/source/testing/external-links.md @@ -0,0 +1,7 @@ +--- +title: External Links +--- + +[Kibana][1] + +[1] kibana://index.md \ No newline at end of file diff --git a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs index 69b3f5f16..4115156d9 100644 --- a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs +++ b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs @@ -2,6 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Concurrent; using System.Threading.Channels; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -86,6 +87,8 @@ public class DiagnosticsCollector(ILoggerFactory loggerFactory, IReadOnlyCollect public HashSet OffendingFiles { get; } = new(); + public ConcurrentDictionary ExternalLinks { get; } = new(); + public Task StartAsync(Cancel ctx) { if (_started is not null) @@ -140,6 +143,7 @@ public virtual async Task StopAsync(CancellationToken cancellationToken) await Channel.Reader.Completion; } + public void EmitExternalLink(string link) => ExternalLinks.TryAdd(link, true); public void EmitError(string file, string message, Exception? e = null) { diff --git a/src/Elastic.Markdown/IO/LinkReference.cs b/src/Elastic.Markdown/IO/LinkReference.cs index da42a7964..41d4ab85b 100644 --- a/src/Elastic.Markdown/IO/LinkReference.cs +++ b/src/Elastic.Markdown/IO/LinkReference.cs @@ -17,8 +17,13 @@ public record LinkReference [JsonPropertyName("links")] public required string[] Links { get; init; } = []; + [JsonPropertyName("external_links")] + public required string[] ExternalLinks { get; init; } = []; + public static LinkReference Create(DocumentationSet set) { + var externalLinks = set.Context.Collector.ExternalLinks.Select(i => i.Key).ToArray(); + var links = set.FlatMappedFiles.Values .OfType() .Select(m => m.RelativePath).ToArray(); @@ -26,7 +31,8 @@ public static LinkReference Create(DocumentationSet set) { UrlPathPrefix = set.Context.UrlPathPrefix, Origin = set.Context.Git, - Links = links + Links = links, + ExternalLinks = externalLinks }; } } diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index d04c0ac17..6d43e6558 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -2,15 +2,14 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Immutable; using Elastic.Markdown.Diagnostics; using Elastic.Markdown.IO; -using Elastic.Markdown.Myst.Directives; using Markdig; using Markdig.Helpers; using Markdig.Parsers; using Markdig.Parsers.Inlines; using Markdig.Renderers; -using Markdig.Syntax; using Markdig.Syntax.Inlines; namespace Elastic.Markdown.Myst.InlineParsers; @@ -34,6 +33,9 @@ public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { } public class DiagnosticLinkInlineParser : LinkInlineParser { + + private static readonly ImmutableHashSet ExcludedSchemes = new[] { "http", "https", "tel", "jdbc" }.ToImmutableHashSet(); + public override bool Match(InlineProcessor processor, ref StringSlice slice) { var match = base.Match(processor, ref slice); @@ -44,10 +46,14 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; var url = link.Url; + var uri = Uri.TryCreate(url, UriKind.Absolute, out var u) ? u : null; var line = link.Line + 1; var column = link.Column; var length = url?.Length ?? 1; + if (IsExternalLink(uri)) + processor.GetContext().Build.Collector.EmitExternalLink(url!); + var context = processor.GetContext(); if (processor.GetContext().SkipValidation) return match; @@ -58,7 +64,7 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; } - if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && uri.Scheme.StartsWith("http")) + if (uri != null && uri.Scheme.StartsWith("http")) { var baseDomain = uri.Host == "localhost" ? "localhost" : string.Join('.', uri.Host.Split('.')[^2..]); if (!context.Configuration.ExternalLinkHosts.Contains(baseDomain)) @@ -82,15 +88,11 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) var anchor = anchors.Length > 1 ? anchors[1].Trim() : null; url = anchors[0]; - if (!string.IsNullOrWhiteSpace(url)) + if (!string.IsNullOrWhiteSpace(url) && uri != null) { var pathOnDisk = Path.Combine(includeFrom, url.TrimStart('/')); - if (!context.Build.ReadFileSystem.File.Exists(pathOnDisk)) + if (uri.IsFile && !context.Build.ReadFileSystem.File.Exists(pathOnDisk)) processor.EmitError(line, column, length, $"`{url}` does not exist. resolved to `{pathOnDisk}"); - else - { - - } } else link.Url = ""; @@ -128,8 +130,11 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) link.Url += $"#{anchor}"; return match; - - - } + + private static bool IsExternalLink(Uri? uri) => + uri != null + && !ExcludedSchemes.Contains(uri.Scheme) + && !uri.IsFile + && Path.GetExtension(uri.OriginalString) == ".md"; } diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs index 578c1f7b5..030848002 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs @@ -83,3 +83,74 @@ public void GeneratesHtml() => [Fact] public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); } + +public class LinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output, + """ + [test][test] + + [test]: testing/req.md + """ +) +{ + [Fact] + public void GeneratesHtml() => + // language=html + Html.Should().Contain( + """

test

""" + ); + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); +} + +public class ExternalLinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output, + """ + [test][test] + + [test]: kibana://index.md + """ +) +{ + [Fact] + public void GeneratesHtml() => + // language=html + Html.Should().Contain( + """

test

""" + ); + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); + + [Fact] + public void EmitsExternalLink() + { + Collector.ExternalLinks.Should().HaveCount(1); + Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); + } +} + +public class ExternalLinkTest(ITestOutputHelper output) : LinkTestBase(output, + """ + [test](kibana://index.md) + """ +) +{ + [Fact] + public void GeneratesHtml() => + // language=html + Html.Should().Contain( + """

test

""" + ); + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); + + [Fact] + public void EmitsExternalLink() + { + Collector.ExternalLinks.Should().HaveCount(1); + Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); + } +} + + From 9d1d169fc5e71abfbf26ec3cd1c769cea79f57f1 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 10 Jan 2025 21:28:23 +0100 Subject: [PATCH 2/8] Add tests --- docs/source/testing/external-links.md | 4 ++- .../Inline/InlineLinkTests.cs | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/source/testing/external-links.md b/docs/source/testing/external-links.md index 3318de633..664abb232 100644 --- a/docs/source/testing/external-links.md +++ b/docs/source/testing/external-links.md @@ -2,6 +2,8 @@ title: External Links --- +[Elasticsearch](elasticsearch://index.md) + [Kibana][1] -[1] kibana://index.md \ No newline at end of file +[1]: kibana://index.md \ No newline at end of file diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs index 030848002..ec4e9221d 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs @@ -115,6 +115,7 @@ public class ExternalLinkReferenceTest(ITestOutputHelper output) : LinkTestBase( public void GeneratesHtml() => // language=html Html.Should().Contain( + // TODO: The link is not rendered correctly yet, will be fixed in a follow-up """

test

""" ); @@ -139,6 +140,7 @@ public class ExternalLinkTest(ITestOutputHelper output) : LinkTestBase(output, public void GeneratesHtml() => // language=html Html.Should().Contain( + // TODO: The link is not rendered correctly yet, will be fixed in a follow-up """

test

""" ); @@ -153,4 +155,35 @@ public void EmitsExternalLink() } } +public class DuplicateExternalLinkTest(ITestOutputHelper output) : LinkTestBase(output, + """ + [a](kibana://index.md) + [b](kibana://index.md) + [c](elasticsearch://index.md) + """ +) +{ + [Fact] + public void GeneratesHtml() => + // language=html + Html.Should().Contain( + // TODO: The link is not rendered correctly yet, will be fixed in a follow-up + """ +

a
+ b
+ c

+ """ + ); + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); + + [Fact] + public void EmitsExternalLink() + { + Collector.ExternalLinks.Should().HaveCount(2); + Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); + Collector.ExternalLinks.Should().ContainKey("elasticsearch://index.md"); + } +} From da883fc8c735c79ca7d46b302279299102ab751d Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 10 Jan 2025 21:31:57 +0100 Subject: [PATCH 3/8] Add comment --- .../Myst/InlineParsers/DiagnosticLinkInlineParser.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 6d43e6558..8fa6eb352 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -33,8 +33,9 @@ public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { } public class DiagnosticLinkInlineParser : LinkInlineParser { - - private static readonly ImmutableHashSet ExcludedSchemes = new[] { "http", "https", "tel", "jdbc" }.ToImmutableHashSet(); + // See https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml for a list of URI schemes + // We can add more schemes as needed + private static readonly ImmutableHashSet ExcludedSchemes = ["http", "https", "tel", "jdbc"]; public override bool Match(InlineProcessor processor, ref StringSlice slice) { From a82df41eb019b8bf089babb22a7b1184dff9a10e Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 10 Jan 2025 21:34:34 +0100 Subject: [PATCH 4/8] format --- .../Inline/InlineLinkTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs index ec4e9221d..2c4166d25 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs @@ -175,15 +175,15 @@ public void GeneratesHtml() => """ ); - [Fact] - public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); - - [Fact] - public void EmitsExternalLink() - { - Collector.ExternalLinks.Should().HaveCount(2); - Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); - Collector.ExternalLinks.Should().ContainKey("elasticsearch://index.md"); - } + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); + + [Fact] + public void EmitsExternalLink() + { + Collector.ExternalLinks.Should().HaveCount(2); + Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); + Collector.ExternalLinks.Should().ContainKey("elasticsearch://index.md"); + } } From de786fc87d07ddf51727ec6e2eec57cc1d6b9895 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 10 Jan 2025 21:46:14 +0100 Subject: [PATCH 5/8] Rename to CrossLinks --- docs/source/docset.yml | 2 +- .../{external-links.md => cross-links.md} | 2 +- .../Diagnostics/DiagnosticsChannel.cs | 4 +-- src/Elastic.Markdown/IO/LinkReference.cs | 8 +++--- .../Inline/InlineLinkTests.cs | 26 +++++++++---------- 5 files changed, 21 insertions(+), 21 deletions(-) rename docs/source/testing/{external-links.md => cross-links.md} (79%) diff --git a/docs/source/docset.yml b/docs/source/docset.yml index b18eaaaa5..333a53dff 100644 --- a/docs/source/docset.yml +++ b/docs/source/docset.yml @@ -77,4 +77,4 @@ toc: - file: index.md - file: req.md - folder: nested - - file: external-links.md + - file: cross-links.md diff --git a/docs/source/testing/external-links.md b/docs/source/testing/cross-links.md similarity index 79% rename from docs/source/testing/external-links.md rename to docs/source/testing/cross-links.md index 664abb232..deff414ee 100644 --- a/docs/source/testing/external-links.md +++ b/docs/source/testing/cross-links.md @@ -1,5 +1,5 @@ --- -title: External Links +title: Cross Links --- [Elasticsearch](elasticsearch://index.md) diff --git a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs index 4115156d9..902808784 100644 --- a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs +++ b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs @@ -87,7 +87,7 @@ public class DiagnosticsCollector(ILoggerFactory loggerFactory, IReadOnlyCollect public HashSet OffendingFiles { get; } = new(); - public ConcurrentDictionary ExternalLinks { get; } = new(); + public ConcurrentDictionary CrossLinks { get; } = new(); public Task StartAsync(Cancel ctx) { @@ -143,7 +143,7 @@ public virtual async Task StopAsync(CancellationToken cancellationToken) await Channel.Reader.Completion; } - public void EmitExternalLink(string link) => ExternalLinks.TryAdd(link, true); + public void EmitExternalLink(string link) => CrossLinks.TryAdd(link, true); public void EmitError(string file, string message, Exception? e = null) { diff --git a/src/Elastic.Markdown/IO/LinkReference.cs b/src/Elastic.Markdown/IO/LinkReference.cs index 41d4ab85b..5c25827de 100644 --- a/src/Elastic.Markdown/IO/LinkReference.cs +++ b/src/Elastic.Markdown/IO/LinkReference.cs @@ -17,12 +17,12 @@ public record LinkReference [JsonPropertyName("links")] public required string[] Links { get; init; } = []; - [JsonPropertyName("external_links")] - public required string[] ExternalLinks { get; init; } = []; + [JsonPropertyName("cross_links")] + public required string[] CrossLinks { get; init; } = []; public static LinkReference Create(DocumentationSet set) { - var externalLinks = set.Context.Collector.ExternalLinks.Select(i => i.Key).ToArray(); + var crossLinks = set.Context.Collector.CrossLinks.Select(i => i.Key).ToArray(); var links = set.FlatMappedFiles.Values .OfType() @@ -32,7 +32,7 @@ public static LinkReference Create(DocumentationSet set) UrlPathPrefix = set.Context.UrlPathPrefix, Origin = set.Context.Git, Links = links, - ExternalLinks = externalLinks + CrossLinks = crossLinks }; } } diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs index 2c4166d25..457485e7b 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs @@ -103,7 +103,7 @@ public void GeneratesHtml() => public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); } -public class ExternalLinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output, +public class CrossLinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output, """ [test][test] @@ -123,14 +123,14 @@ public void GeneratesHtml() => public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); [Fact] - public void EmitsExternalLink() + public void EmitsCrossLink() { - Collector.ExternalLinks.Should().HaveCount(1); - Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); + Collector.CrossLinks.Should().HaveCount(1); + Collector.CrossLinks.Should().ContainKey("kibana://index.md"); } } -public class ExternalLinkTest(ITestOutputHelper output) : LinkTestBase(output, +public class CrossLinkTest(ITestOutputHelper output) : LinkTestBase(output, """ [test](kibana://index.md) """ @@ -148,14 +148,14 @@ public void GeneratesHtml() => public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); [Fact] - public void EmitsExternalLink() + public void EmitsCrossLink() { - Collector.ExternalLinks.Should().HaveCount(1); - Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); + Collector.CrossLinks.Should().HaveCount(1); + Collector.CrossLinks.Should().ContainKey("kibana://index.md"); } } -public class DuplicateExternalLinkTest(ITestOutputHelper output) : LinkTestBase(output, +public class DuplicateCrossLinkTest(ITestOutputHelper output) : LinkTestBase(output, """ [a](kibana://index.md) [b](kibana://index.md) @@ -179,11 +179,11 @@ public void GeneratesHtml() => public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); [Fact] - public void EmitsExternalLink() + public void EmitsCrossLink() { - Collector.ExternalLinks.Should().HaveCount(2); - Collector.ExternalLinks.Should().ContainKey("kibana://index.md"); - Collector.ExternalLinks.Should().ContainKey("elasticsearch://index.md"); + Collector.CrossLinks.Should().HaveCount(2); + Collector.CrossLinks.Should().ContainKey("kibana://index.md"); + Collector.CrossLinks.Should().ContainKey("elasticsearch://index.md"); } } From 744b138d98876c6a93ddafcb83ab09bf995412a9 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 10 Jan 2025 21:47:32 +0100 Subject: [PATCH 6/8] more --- src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs | 2 +- .../Myst/InlineParsers/DiagnosticLinkInlineParser.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs index 902808784..7bd2cb744 100644 --- a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs +++ b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs @@ -143,7 +143,7 @@ public virtual async Task StopAsync(CancellationToken cancellationToken) await Channel.Reader.Completion; } - public void EmitExternalLink(string link) => CrossLinks.TryAdd(link, true); + public void EmitCrossLink(string link) => CrossLinks.TryAdd(link, true); public void EmitError(string file, string message, Exception? e = null) { diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 8fa6eb352..0f8a261ff 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -52,8 +52,8 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) var column = link.Column; var length = url?.Length ?? 1; - if (IsExternalLink(uri)) - processor.GetContext().Build.Collector.EmitExternalLink(url!); + if (IsCrossLink(uri)) + processor.GetContext().Build.Collector.EmitCrossLink(url!); var context = processor.GetContext(); if (processor.GetContext().SkipValidation) @@ -133,7 +133,7 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; } - private static bool IsExternalLink(Uri? uri) => + private static bool IsCrossLink(Uri? uri) => uri != null && !ExcludedSchemes.Contains(uri.Scheme) && !uri.IsFile From 4e64c19b5a5fc8f539c2a35be99a6d7cda10b399 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 10 Jan 2025 22:18:39 +0100 Subject: [PATCH 7/8] Use ConcurrentBag and enhance tests --- .../Diagnostics/DiagnosticsChannel.cs | 4 +- src/Elastic.Markdown/IO/LinkReference.cs | 3 +- .../Inline/InlineLinkTests.cs | 60 +++++++------------ 3 files changed, 26 insertions(+), 41 deletions(-) diff --git a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs index 7bd2cb744..1ca8ac67e 100644 --- a/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs +++ b/src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs @@ -87,7 +87,7 @@ public class DiagnosticsCollector(ILoggerFactory loggerFactory, IReadOnlyCollect public HashSet OffendingFiles { get; } = new(); - public ConcurrentDictionary CrossLinks { get; } = new(); + public ConcurrentBag CrossLinks { get; } = new(); public Task StartAsync(Cancel ctx) { @@ -143,7 +143,7 @@ public virtual async Task StopAsync(CancellationToken cancellationToken) await Channel.Reader.Completion; } - public void EmitCrossLink(string link) => CrossLinks.TryAdd(link, true); + public void EmitCrossLink(string link) => CrossLinks.Add(link); public void EmitError(string file, string message, Exception? e = null) { diff --git a/src/Elastic.Markdown/IO/LinkReference.cs b/src/Elastic.Markdown/IO/LinkReference.cs index 5c25827de..43bafd400 100644 --- a/src/Elastic.Markdown/IO/LinkReference.cs +++ b/src/Elastic.Markdown/IO/LinkReference.cs @@ -22,8 +22,7 @@ public record LinkReference public static LinkReference Create(DocumentationSet set) { - var crossLinks = set.Context.Collector.CrossLinks.Select(i => i.Key).ToArray(); - + var crossLinks = set.Context.Collector.CrossLinks.ToHashSet().ToArray(); var links = set.FlatMappedFiles.Values .OfType() .Select(m => m.RelativePath).ToArray(); diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs index 457485e7b..d52b49260 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs @@ -65,6 +65,12 @@ public void GeneratesHtml() => [Fact] public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); + + [Fact] + public void EmitsCrossLink() + { + Collector.CrossLinks.Should().HaveCount(0); + } } public class InsertPageTitleTests(ITestOutputHelper output) : LinkTestBase(output, @@ -82,6 +88,12 @@ public void GeneratesHtml() => [Fact] public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); + + [Fact] + public void EmitsCrossLink() + { + Collector.CrossLinks.Should().HaveCount(0); + } } public class LinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output, @@ -101,6 +113,12 @@ public void GeneratesHtml() => [Fact] public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); + + [Fact] + public void EmitsCrossLink() + { + Collector.CrossLinks.Should().HaveCount(0); + } } public class CrossLinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output, @@ -126,40 +144,14 @@ public void GeneratesHtml() => public void EmitsCrossLink() { Collector.CrossLinks.Should().HaveCount(1); - Collector.CrossLinks.Should().ContainKey("kibana://index.md"); + Collector.CrossLinks.Should().Contain("kibana://index.md"); } } public class CrossLinkTest(ITestOutputHelper output) : LinkTestBase(output, """ - [test](kibana://index.md) - """ -) -{ - [Fact] - public void GeneratesHtml() => - // language=html - Html.Should().Contain( - // TODO: The link is not rendered correctly yet, will be fixed in a follow-up - """

test

""" - ); - [Fact] - public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0); - - [Fact] - public void EmitsCrossLink() - { - Collector.CrossLinks.Should().HaveCount(1); - Collector.CrossLinks.Should().ContainKey("kibana://index.md"); - } -} - -public class DuplicateCrossLinkTest(ITestOutputHelper output) : LinkTestBase(output, - """ - [a](kibana://index.md) - [b](kibana://index.md) - [c](elasticsearch://index.md) + Go to [test](kibana://index.md) """ ) { @@ -168,11 +160,7 @@ public void GeneratesHtml() => // language=html Html.Should().Contain( // TODO: The link is not rendered correctly yet, will be fixed in a follow-up - """ -

a
- b
- c

- """ + """

Go to test

""" ); [Fact] @@ -181,9 +169,7 @@ public void GeneratesHtml() => [Fact] public void EmitsCrossLink() { - Collector.CrossLinks.Should().HaveCount(2); - Collector.CrossLinks.Should().ContainKey("kibana://index.md"); - Collector.CrossLinks.Should().ContainKey("elasticsearch://index.md"); + Collector.CrossLinks.Should().HaveCount(1); + Collector.CrossLinks.Should().Contain("kibana://index.md"); } } - From 15055c0d5a23815d128de22a0a05e6867c0d5aeb Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 13 Jan 2025 09:30:06 +0100 Subject: [PATCH 8/8] Move Uri.TryCreate a little down in the method --- .../Myst/InlineParsers/DiagnosticLinkInlineParser.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 0f8a261ff..021c4ddbe 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -47,13 +47,10 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; var url = link.Url; - var uri = Uri.TryCreate(url, UriKind.Absolute, out var u) ? u : null; var line = link.Line + 1; var column = link.Column; var length = url?.Length ?? 1; - if (IsCrossLink(uri)) - processor.GetContext().Build.Collector.EmitCrossLink(url!); var context = processor.GetContext(); if (processor.GetContext().SkipValidation) @@ -65,6 +62,11 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; } + var uri = Uri.TryCreate(url, UriKind.Absolute, out var u) ? u : null; + + if (IsCrossLink(uri)) + processor.GetContext().Build.Collector.EmitCrossLink(url!); + if (uri != null && uri.Scheme.StartsWith("http")) { var baseDomain = uri.Host == "localhost" ? "localhost" : string.Join('.', uri.Host.Split('.')[^2..]);