Skip to content

Commit 05547a3

Browse files
Copilotreakaleek
andcommitted
Implement crosslink empty text auto-title feature
Co-authored-by: reakaleek <[email protected]>
1 parent e56941f commit 05547a3

File tree

6 files changed

+72
-1
lines changed

6 files changed

+72
-1
lines changed

src/Elastic.Documentation.Links/CrossLinks/CrossLinkResolver.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Elastic.Documentation.Links.CrossLinks;
1010
public interface ICrossLinkResolver
1111
{
1212
bool TryResolve(Action<string> errorEmitter, Uri crossLinkUri, [NotNullWhen(true)] out Uri? resolvedUri);
13+
bool TryGetLinkMetadata(Uri crossLinkUri, [NotNullWhen(true)] out LinkMetadata? linkMetadata);
1314
IUriEnvironmentResolver UriResolver { get; }
1415
}
1516

@@ -24,6 +25,13 @@ public bool TryResolve(Action<string> errorEmitter, Uri crossLinkUri, [NotNullWh
2425
return false;
2526
}
2627

28+
/// <inheritdoc />
29+
public bool TryGetLinkMetadata(Uri crossLinkUri, [NotNullWhen(true)] out LinkMetadata? linkMetadata)
30+
{
31+
linkMetadata = null;
32+
return false;
33+
}
34+
2735
/// <inheritdoc />
2836
public IUriEnvironmentResolver UriResolver { get; } = new IsolatedBuildEnvironmentUriResolver();
2937

@@ -39,6 +47,20 @@ public class CrossLinkResolver(FetchedCrossLinks crossLinks, IUriEnvironmentReso
3947
public bool TryResolve(Action<string> errorEmitter, Uri crossLinkUri, [NotNullWhen(true)] out Uri? resolvedUri) =>
4048
TryResolve(errorEmitter, _crossLinks, UriResolver, crossLinkUri, out resolvedUri);
4149

50+
public bool TryGetLinkMetadata(Uri crossLinkUri, [NotNullWhen(true)] out LinkMetadata? linkMetadata)
51+
{
52+
linkMetadata = null;
53+
54+
if (!_crossLinks.LinkReferences.TryGetValue(crossLinkUri.Scheme, out var sourceLinkReference))
55+
return false;
56+
57+
var originalLookupPath = (crossLinkUri.Host + '/' + crossLinkUri.AbsolutePath.TrimStart('/')).Trim('/');
58+
if (string.IsNullOrEmpty(originalLookupPath) && crossLinkUri.Host.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
59+
originalLookupPath = crossLinkUri.Host;
60+
61+
return sourceLinkReference.Links.TryGetValue(originalLookupPath, out linkMetadata);
62+
}
63+
4264
public FetchedCrossLinks UpdateLinkReference(string repository, RepositoryLinks repositoryLinks)
4365
{
4466
var dictionary = _crossLinks.LinkReferences.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

src/Elastic.Documentation/Links/RepositoryLinks.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public record LinkMetadata
1717
[JsonPropertyName("hidden")]
1818
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
1919
public bool Hidden { get; init; }
20+
21+
[JsonPropertyName("title")]
22+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
23+
public string? Title { get; init; }
2024
}
2125

2226
public record LinkSingleRedirect

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,14 @@ private static void ProcessCrossLink(LinkInline link, InlineProcessor processor,
184184
uri, out var resolvedUri)
185185
)
186186
link.Url = resolvedUri.ToString();
187+
188+
// Handle empty link text by trying to get title from crosslink metadata
189+
if (link.FirstChild == null && context.CrossLinkResolver.TryGetLinkMetadata(uri, out var linkMetadata))
190+
{
191+
var title = linkMetadata.Title;
192+
if (!string.IsNullOrEmpty(title))
193+
_ = link.AppendChild(new LiteralInline(title));
194+
}
187195
}
188196

189197
private static void ProcessInternalLink(LinkInline link, InlineProcessor processor, ParserContext context)

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,31 @@ public void EmitsCrossLink()
164164
}
165165
}
166166

167+
public class CrossLinkEmptyTextTest(ITestOutputHelper output) : LinkTestBase(output,
168+
"""
169+
170+
Go to [](kibana://index.md)
171+
"""
172+
)
173+
{
174+
[Fact]
175+
public void GeneratesHtml() =>
176+
// language=html
177+
Html.Should().Contain(
178+
"""<p>Go to <a href="https://docs-v3-preview.elastic.dev/elastic/kibana/tree/main/" hx-select-oob="#main-container" preload="mousedown">Kibana Guide</a></p>"""
179+
);
180+
181+
[Fact]
182+
public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0);
183+
184+
[Fact]
185+
public void EmitsCrossLink()
186+
{
187+
Collector.CrossLinks.Should().HaveCount(1);
188+
Collector.CrossLinks.Should().Contain("kibana://index.md");
189+
}
190+
}
191+
167192
public class LinkWithUnresolvedInterpolationError(ITestOutputHelper output) : LinkTestBase(output,
168193
"""
169194
[global search field]({{this-variable-does-not-exist}}/introduction.html#kibana-navigation-search)

tests/Elastic.Markdown.Tests/TestCrossLinkResolver.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ public TestCrossLinkResolver()
3030
"url_path_prefix": "/elastic/docs-content/tree/main",
3131
"cross_links": [],
3232
"links": {
33-
"index.md": {},
33+
"index.md": {
34+
"title": "Kibana Guide"
35+
},
3436
"get-started/index.md": {
3537
"anchors": [
3638
"elasticsearch-intro-elastic-stack",
@@ -68,4 +70,10 @@ public TestCrossLinkResolver()
6870

6971
public bool TryResolve(Action<string> errorEmitter, Uri crossLinkUri, [NotNullWhen(true)] out Uri? resolvedUri) =>
7072
CrossLinkResolver.TryResolve(errorEmitter, _crossLinks, UriResolver, crossLinkUri, out resolvedUri);
73+
74+
public bool TryGetLinkMetadata(Uri crossLinkUri, [NotNullWhen(true)] out LinkMetadata? linkMetadata)
75+
{
76+
var resolver = new CrossLinkResolver(_crossLinks, UriResolver);
77+
return resolver.TryGetLinkMetadata(crossLinkUri, out linkMetadata);
78+
}
7179
}

tests/authoring/Framework/TestCrossLinkResolver.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,8 @@ type TestCrossLinkResolver (config: ConfigurationFile) =
8989
member this.TryResolve(errorEmitter, crossLinkUri, [<Out>]resolvedUri : byref<Uri|null>) =
9090
CrossLinkResolver.TryResolve(errorEmitter, crossLinks, uriResolver, crossLinkUri, &resolvedUri)
9191

92+
member this.TryGetLinkMetadata(crossLinkUri, [<Out>]linkMetadata : byref<LinkMetadata|null>) =
93+
let resolver = new CrossLinkResolver(crossLinks, uriResolver)
94+
resolver.TryGetLinkMetadata(crossLinkUri, &linkMetadata)
95+
9296

0 commit comments

Comments
 (0)