Skip to content

Commit c998a0e

Browse files
committed
Add exteranl links to links.json
1 parent 085b9fe commit c998a0e

File tree

6 files changed

+107
-13
lines changed

6 files changed

+107
-13
lines changed

docs/source/docset.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,4 @@ toc:
7777
- file: index.md
7878
- file: req.md
7979
- folder: nested
80+
- file: external-links.md
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
title: External Links
3+
---
4+
5+
[Kibana][1]
6+
7+
[1] kibana://index.md

src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using System.Collections.Concurrent;
56
using System.Threading.Channels;
67
using Microsoft.Extensions.Hosting;
78
using Microsoft.Extensions.Logging;
@@ -86,6 +87,8 @@ public class DiagnosticsCollector(ILoggerFactory loggerFactory, IReadOnlyCollect
8687

8788
public HashSet<string> OffendingFiles { get; } = new();
8889

90+
public ConcurrentDictionary<string, bool> ExternalLinks { get; } = new();
91+
8992
public Task StartAsync(Cancel ctx)
9093
{
9194
if (_started is not null)
@@ -140,6 +143,7 @@ public virtual async Task StopAsync(CancellationToken cancellationToken)
140143
await Channel.Reader.Completion;
141144
}
142145

146+
public void EmitExternalLink(string link) => ExternalLinks.TryAdd(link, true);
143147

144148
public void EmitError(string file, string message, Exception? e = null)
145149
{

src/Elastic.Markdown/IO/LinkReference.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,22 @@ public record LinkReference
1717
[JsonPropertyName("links")]
1818
public required string[] Links { get; init; } = [];
1919

20+
[JsonPropertyName("external_links")]
21+
public required string[] ExternalLinks { get; init; } = [];
22+
2023
public static LinkReference Create(DocumentationSet set)
2124
{
25+
var externalLinks = set.Context.Collector.ExternalLinks.Select(i => i.Key).ToArray();
26+
2227
var links = set.FlatMappedFiles.Values
2328
.OfType<MarkdownFile>()
2429
.Select(m => m.RelativePath).ToArray();
2530
return new LinkReference
2631
{
2732
UrlPathPrefix = set.Context.UrlPathPrefix,
2833
Origin = set.Context.Git,
29-
Links = links
34+
Links = links,
35+
ExternalLinks = externalLinks
3036
};
3137
}
3238
}

src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using System.Collections.Immutable;
56
using Elastic.Markdown.Diagnostics;
67
using Elastic.Markdown.IO;
7-
using Elastic.Markdown.Myst.Directives;
88
using Markdig;
99
using Markdig.Helpers;
1010
using Markdig.Parsers;
1111
using Markdig.Parsers.Inlines;
1212
using Markdig.Renderers;
13-
using Markdig.Syntax;
1413
using Markdig.Syntax.Inlines;
1514

1615
namespace Elastic.Markdown.Myst.InlineParsers;
@@ -34,6 +33,9 @@ public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { }
3433

3534
public class DiagnosticLinkInlineParser : LinkInlineParser
3635
{
36+
37+
private static readonly ImmutableHashSet<string> ExcludedSchemes = new[] { "http", "https", "tel", "jdbc" }.ToImmutableHashSet();
38+
3739
public override bool Match(InlineProcessor processor, ref StringSlice slice)
3840
{
3941
var match = base.Match(processor, ref slice);
@@ -44,10 +46,14 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
4446
return match;
4547

4648
var url = link.Url;
49+
var uri = Uri.TryCreate(url, UriKind.Absolute, out var u) ? u : null;
4750
var line = link.Line + 1;
4851
var column = link.Column;
4952
var length = url?.Length ?? 1;
5053

54+
if (IsExternalLink(uri))
55+
processor.GetContext().Build.Collector.EmitExternalLink(url!);
56+
5157
var context = processor.GetContext();
5258
if (processor.GetContext().SkipValidation)
5359
return match;
@@ -58,7 +64,7 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
5864
return match;
5965
}
6066

61-
if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && uri.Scheme.StartsWith("http"))
67+
if (uri != null && uri.Scheme.StartsWith("http"))
6268
{
6369
var baseDomain = uri.Host == "localhost" ? "localhost" : string.Join('.', uri.Host.Split('.')[^2..]);
6470
if (!context.Configuration.ExternalLinkHosts.Contains(baseDomain))
@@ -82,15 +88,11 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
8288
var anchor = anchors.Length > 1 ? anchors[1].Trim() : null;
8389
url = anchors[0];
8490

85-
if (!string.IsNullOrWhiteSpace(url))
91+
if (!string.IsNullOrWhiteSpace(url) && uri != null)
8692
{
8793
var pathOnDisk = Path.Combine(includeFrom, url.TrimStart('/'));
88-
if (!context.Build.ReadFileSystem.File.Exists(pathOnDisk))
94+
if (uri.IsFile && !context.Build.ReadFileSystem.File.Exists(pathOnDisk))
8995
processor.EmitError(line, column, length, $"`{url}` does not exist. resolved to `{pathOnDisk}");
90-
else
91-
{
92-
93-
}
9496
}
9597
else
9698
link.Url = "";
@@ -128,8 +130,11 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
128130
link.Url += $"#{anchor}";
129131

130132
return match;
131-
132-
133-
134133
}
134+
135+
private static bool IsExternalLink(Uri? uri) =>
136+
uri != null
137+
&& !ExcludedSchemes.Contains(uri.Scheme)
138+
&& !uri.IsFile
139+
&& Path.GetExtension(uri.OriginalString) == ".md";
135140
}

tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,74 @@ public void GeneratesHtml() =>
8383
[Fact]
8484
public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0);
8585
}
86+
87+
public class LinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output,
88+
"""
89+
[test][test]
90+
91+
[test]: testing/req.md
92+
"""
93+
)
94+
{
95+
[Fact]
96+
public void GeneratesHtml() =>
97+
// language=html
98+
Html.Should().Contain(
99+
"""<p><a href="testing/req.html">test</a></p>"""
100+
);
101+
102+
[Fact]
103+
public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0);
104+
}
105+
106+
public class ExternalLinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output,
107+
"""
108+
[test][test]
109+
110+
[test]: kibana://index.md
111+
"""
112+
)
113+
{
114+
[Fact]
115+
public void GeneratesHtml() =>
116+
// language=html
117+
Html.Should().Contain(
118+
"""<p><a href="kibana://index.html">test</a></p>"""
119+
);
120+
121+
[Fact]
122+
public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0);
123+
124+
[Fact]
125+
public void EmitsExternalLink()
126+
{
127+
Collector.ExternalLinks.Should().HaveCount(1);
128+
Collector.ExternalLinks.Should().ContainKey("kibana://index.md");
129+
}
130+
}
131+
132+
public class ExternalLinkTest(ITestOutputHelper output) : LinkTestBase(output,
133+
"""
134+
[test](kibana://index.md)
135+
"""
136+
)
137+
{
138+
[Fact]
139+
public void GeneratesHtml() =>
140+
// language=html
141+
Html.Should().Contain(
142+
"""<p><a href="kibana://index.html">test</a></p>"""
143+
);
144+
145+
[Fact]
146+
public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0);
147+
148+
[Fact]
149+
public void EmitsExternalLink()
150+
{
151+
Collector.ExternalLinks.Should().HaveCount(1);
152+
Collector.ExternalLinks.Should().ContainKey("kibana://index.md");
153+
}
154+
}
155+
156+

0 commit comments

Comments
 (0)