Skip to content

Commit a1a1870

Browse files
Update navigation.yml (#780)
* add code comments with context * address feedback from @lcawl @bmorelli25 in elastic/docs-projects#464 * add release notes config from @KOTungseth * Revert "add release notes config from @KOTungseth" This reverts commit 417f7af. * net -> dotnet * update navigation.yml and assembler.yml * clean up * more clean up * Refactor navigation handling and introduce phantom support Simplified navigation logic by restructuring method signatures and refining table of contents processing. Added support for phantom definitions in navigation files, ensuring appropriate handling of unlinked TOC folders. Refactored related classes to align with the new navigation paradigm. --------- Co-authored-by: Martijn Laarman <[email protected]>
1 parent 9b2f9df commit a1a1870

File tree

7 files changed

+117
-154
lines changed

7 files changed

+117
-154
lines changed

src/docs-assembler/AssembleSources.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public record TocConfigurationMapping
3333

3434
public class AssembleSources
3535
{
36+
public AssembleContext AssembleContext { get; }
3637
public FrozenDictionary<string, AssemblerDocumentationSet> AssembleSets { get; }
3738

3839
public FrozenDictionary<Uri, TocTopLevelMapping> TocTopLevelMappings { get; }
@@ -51,16 +52,17 @@ public static async Task<AssembleSources> AssembleAsync(AssembleContext context,
5152
return sources;
5253
}
5354

54-
private AssembleSources(AssembleContext context, Checkout[] checkouts)
55+
private AssembleSources(AssembleContext assembleContext, Checkout[] checkouts)
5556
{
56-
TocTopLevelMappings = GetConfiguredSources(context);
57+
AssembleContext = assembleContext;
58+
TocTopLevelMappings = GetConfiguredSources(assembleContext);
5759

58-
var crossLinkFetcher = new AssemblerCrossLinkFetcher(NullLoggerFactory.Instance, context.Configuration);
59-
UriResolver = new PublishEnvironmentUriResolver(TocTopLevelMappings, context.Environment);
60+
var crossLinkFetcher = new AssemblerCrossLinkFetcher(NullLoggerFactory.Instance, assembleContext.Configuration);
61+
UriResolver = new PublishEnvironmentUriResolver(TocTopLevelMappings, assembleContext.Environment);
6062
var crossLinkResolver = new CrossLinkResolver(crossLinkFetcher, UriResolver);
6163
AssembleSets = checkouts
6264
.Where(c => !c.Repository.Skip)
63-
.Select(c => new AssemblerDocumentationSet(NullLoggerFactory.Instance, context, c, crossLinkResolver, TreeCollector))
65+
.Select(c => new AssemblerDocumentationSet(NullLoggerFactory.Instance, assembleContext, c, crossLinkResolver, TreeCollector))
6466
.ToDictionary(s => s.Checkout.Repository.Name, s => s)
6567
.ToFrozenDictionary();
6668

@@ -85,7 +87,7 @@ private AssembleSources(AssembleContext context, Checkout[] checkouts)
8587
var file = tocFiles.FirstOrDefault(f => f.Exists);
8688
if (file is null)
8789
{
88-
context.Collector.EmitWarning(context.ConfigurationPath.FullName, $"Unable to find toc file in {tocDirectory}");
90+
assembleContext.Collector.EmitWarning(assembleContext.ConfigurationPath.FullName, $"Unable to find toc file in {tocDirectory}");
8991
file = tocFiles.First();
9092
}
9193

src/docs-assembler/Cli/RepositoryCommands.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ public async Task<int> BuildAll(
8888

8989
var navigation = new GlobalNavigation(assembleSources, navigationFile);
9090

91-
var pathProvider = new GlobalNavigationPathProvider(assembleSources, assembleContext);
92-
var htmlWriter = new GlobalNavigationHtmlWriter(assembleContext, navigation, assembleSources);
91+
var pathProvider = new GlobalNavigationPathProvider(navigationFile, assembleSources, assembleContext);
92+
var htmlWriter = new GlobalNavigationHtmlWriter(navigationFile, assembleContext, navigation, assembleSources);
9393

9494
var builder = new AssemblerBuilder(logger, assembleContext, htmlWriter, pathProvider);
9595
await builder.BuildAllAsync(assembleSources.AssembleSets, ctx);

src/docs-assembler/Navigation/GlobalNavigationFile.cs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,31 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.Collections.Frozen;
6+
using System.IO.Abstractions;
67
using Documentation.Assembler.Configuration;
78
using Elastic.Markdown.IO.Configuration;
89
using Elastic.Markdown.IO.Navigation;
910
using YamlDotNet.RepresentationModel;
1011

1112
namespace Documentation.Assembler.Navigation;
1213

13-
public record GlobalNavigationFile
14+
public record GlobalNavigationFile : ITableOfContentsScope
1415
{
1516
private readonly AssembleContext _context;
1617
private readonly AssembleSources _assembleSources;
1718

1819
public IReadOnlyCollection<TocReference> TableOfContents { get; }
20+
public IReadOnlyCollection<TocReference> Phantoms { get; }
21+
22+
public IDirectoryInfo ScopeDirectory { get; }
1923

2024
public GlobalNavigationFile(AssembleContext context, AssembleSources assembleSources)
2125
{
2226
_context = context;
2327
_assembleSources = assembleSources;
24-
TableOfContents = Deserialize();
28+
TableOfContents = Deserialize("toc");
29+
Phantoms = Deserialize("phantoms");
30+
ScopeDirectory = _context.NavigationPath.Directory!;
2531
}
2632

2733
public void EmitWarning(string message) =>
@@ -31,18 +37,15 @@ public void EmitError(string message) =>
3137
_context.Collector.EmitWarning(_context.NavigationPath.FullName, message);
3238

3339

34-
private IReadOnlyCollection<TocReference> Deserialize()
40+
private IReadOnlyCollection<TocReference> Deserialize(string key)
3541
{
3642
var reader = new YamlStreamReader(_context.NavigationPath, _context.Collector);
3743
try
3844
{
3945
foreach (var entry in reader.Read())
4046
{
41-
switch (entry.Key)
42-
{
43-
case "toc":
44-
return ReadChildren(reader, entry.Entry, null, 0);
45-
}
47+
if (entry.Key == key)
48+
return ReadChildren(key, reader, entry.Entry, null, 0);
4649
}
4750
}
4851
catch (Exception e)
@@ -54,7 +57,8 @@ private IReadOnlyCollection<TocReference> Deserialize()
5457
return [];
5558
}
5659

57-
private IReadOnlyCollection<TocReference> ReadChildren(YamlStreamReader reader, KeyValuePair<YamlNode, YamlNode> entry, string? parent, int depth)
60+
private IReadOnlyCollection<TocReference> ReadChildren(string key, YamlStreamReader reader, KeyValuePair<YamlNode, YamlNode> entry, string? parent,
61+
int depth)
5862
{
5963
var entries = new List<TocReference>();
6064
if (entry.Key is not YamlScalarNode { Value: not null } scalarKey)
@@ -71,15 +75,39 @@ private IReadOnlyCollection<TocReference> ReadChildren(YamlStreamReader reader,
7175

7276
foreach (var tocEntry in sequence.Children.OfType<YamlMappingNode>())
7377
{
74-
var child = ReadChild(reader, tocEntry, parent, depth);
78+
79+
var child =
80+
key == "toc"
81+
? ReadTocDefinition(reader, tocEntry, parent, depth)
82+
: ReadPhantomDefinition(reader, tocEntry);
7583
if (child is not null)
7684
entries.Add(child);
7785
}
7886

7987
return entries;
8088
}
8189

82-
private TocReference? ReadChild(YamlStreamReader reader, YamlMappingNode tocEntry, string? parent, int depth)
90+
private TocReference? ReadPhantomDefinition(YamlStreamReader reader, YamlMappingNode tocEntry)
91+
{
92+
foreach (var entry in tocEntry.Children)
93+
{
94+
var key = ((YamlScalarNode)entry.Key).Value;
95+
switch (key)
96+
{
97+
case "toc":
98+
var source = reader.ReadString(entry);
99+
if (source != null && !source.Contains("://"))
100+
source = ContentSourceMoniker.CreateString(NarrativeRepository.RepositoryName, source);
101+
var sourceUri = new Uri(source!);
102+
var tocReference = new TocReference(sourceUri, this, "", true, []);
103+
return tocReference;
104+
}
105+
}
106+
107+
return null;
108+
}
109+
110+
private TocReference? ReadTocDefinition(YamlStreamReader reader, YamlMappingNode tocEntry, string? parent, int depth)
83111
{
84112
string? repository = null;
85113
string? source = null;
@@ -139,9 +167,6 @@ private IReadOnlyCollection<TocReference> ReadChildren(YamlStreamReader reader,
139167
}
140168

141169
var navigationItems = new List<ITocItem>();
142-
//TODO not needed
143-
//var tocChildren = mapping.TableOfContentsConfiguration.TableOfContents;
144-
//navigationItems.AddRange(tocChildren);
145170

146171
foreach (var entry in tocEntry.Children)
147172
{
@@ -155,7 +180,7 @@ private IReadOnlyCollection<TocReference> ReadChildren(YamlStreamReader reader,
155180
continue;
156181
}
157182

158-
var children = ReadChildren(reader, entry, parent, depth + 1);
183+
var children = ReadChildren("toc", reader, entry, parent, depth + 1);
159184
navigationItems.AddRange(children);
160185
break;
161186
}
@@ -166,4 +191,5 @@ private IReadOnlyCollection<TocReference> ReadChildren(YamlStreamReader reader,
166191
var tocReference = new TocReference(sourceUri, mapping.TableOfContentsConfiguration, path, true, navigationItems);
167192
return tocReference;
168193
}
194+
169195
}

src/docs-assembler/Navigation/GlobalNavigationHtmlWriter.cs

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,50 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.Collections.Concurrent;
6+
using System.Collections.Immutable;
67
using Elastic.Markdown.IO.Configuration;
78
using Elastic.Markdown.IO.Navigation;
89
using Elastic.Markdown.Slices;
910

1011
namespace Documentation.Assembler.Navigation;
1112

12-
public class GlobalNavigationHtmlWriter(AssembleContext assembleContext, GlobalNavigation navigation, AssembleSources assembleSources) : INavigationHtmlWriter
13+
public class GlobalNavigationHtmlWriter(
14+
GlobalNavigationFile navigationFile,
15+
AssembleContext assembleContext,
16+
GlobalNavigation globalNavigation,
17+
AssembleSources assembleSources) : INavigationHtmlWriter
1318
{
1419
private readonly AssembleContext _assembleContext = assembleContext;
1520
private readonly ConcurrentDictionary<Uri, string> _renderedNavigationCache = [];
1621

17-
private (DocumentationGroup, Uri) GetRealNavigationRoot(INavigation navigation)
18-
{
19-
if (navigation is not DocumentationGroup group)
20-
throw new InvalidOperationException($"Expected a {nameof(DocumentationGroup)}");
22+
private ImmutableHashSet<Uri> Phantoms { get; } = [.. navigationFile.Phantoms.Select(p => p.Source)];
2123

22-
23-
if (group.NavigationRoot is TableOfContentsTree tree)
24+
private (DocumentationGroup, Uri) GetRealNavigationRoot(TableOfContentsTree tree)
25+
{
26+
if (!assembleSources.TocTopLevelMappings.TryGetValue(tree.Source, out var topLevelUri))
2427
{
25-
if (!assembleSources.TocTopLevelMappings.TryGetValue(tree.Source, out var topLevelUri))
26-
{
27-
_assembleContext.Collector.EmitWarning(_assembleContext.NavigationPath.FullName, $"Could not find a top level mapping for {tree.Source}");
28-
return (tree, tree.Source);
29-
}
30-
31-
if (!assembleSources.TreeCollector.TryGetTableOfContentsTree(topLevelUri.TopLevelSource, out var topLevel))
32-
{
33-
_assembleContext.Collector.EmitWarning(_assembleContext.NavigationPath.FullName, $"Could not find a toc tree for {topLevelUri.TopLevelSource}");
34-
return (tree, tree.Source);
35-
36-
}
37-
return (topLevel, topLevelUri.TopLevelSource);
38-
28+
_assembleContext.Collector.EmitWarning(_assembleContext.NavigationPath.FullName, $"Could not find a top level mapping for {tree.Source}");
29+
return (tree, tree.Source);
3930
}
40-
else if (group.NavigationRoot is DocumentationGroup)
31+
32+
if (!assembleSources.TreeCollector.TryGetTableOfContentsTree(topLevelUri.TopLevelSource, out var topLevel))
4133
{
42-
var source = group.FolderName == "reference/index.md"
43-
? new Uri("docs-content://reference/")
44-
: throw new InvalidOperationException($"{group.FolderName} is not a valid navigation root");
45-
return (group, source);
34+
_assembleContext.Collector.EmitWarning(_assembleContext.NavigationPath.FullName, $"Could not find a toc tree for {topLevelUri.TopLevelSource}");
35+
return (tree, tree.Source);
36+
4637
}
47-
throw new InvalidOperationException($"Unknown navigation root {group.NavigationRoot}");
38+
return (topLevel, topLevelUri.TopLevelSource);
4839
}
4940

5041
public async Task<string> RenderNavigation(INavigation currentRootNavigation, Cancel ctx = default)
5142
{
52-
var (navigation, source) = GetRealNavigationRoot(currentRootNavigation);
43+
if (currentRootNavigation is not TableOfContentsTree tree)
44+
throw new InvalidOperationException($"Expected a {nameof(DocumentationGroup)}");
45+
46+
if (Phantoms.Contains(tree.Source))
47+
return string.Empty;
48+
49+
var (navigation, source) = GetRealNavigationRoot(tree);
5350
if (_renderedNavigationCache.TryGetValue(source, out var value))
5451
return value;
5552

@@ -64,17 +61,13 @@ public async Task<string> RenderNavigation(INavigation currentRootNavigation, Ca
6461
var model = CreateNavigationModel(navigation);
6562
value = await ((INavigationHtmlWriter)this).Render(model, ctx);
6663
_renderedNavigationCache[source] = value;
67-
if (source == new Uri("docs-content://extend"))
68-
{
69-
}
70-
7164

7265
return value;
7366
}
7467

7568
private NavigationViewModel CreateNavigationModel(DocumentationGroup group)
7669
{
77-
var topLevelItems = navigation.TopLevelItems;
70+
var topLevelItems = globalNavigation.TopLevelItems;
7871
return new NavigationViewModel
7972
{
8073
Title = group.Index?.NavigationTitle ?? "Docs",

src/docs-assembler/Navigation/GlobalNavigationPathProvider.cs

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,12 @@
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.Frozen;
65
using System.Collections.Immutable;
7-
using System.Diagnostics.CodeAnalysis;
86
using System.IO.Abstractions;
9-
using Documentation.Assembler.Building;
10-
using Documentation.Assembler.Configuration;
11-
using Documentation.Assembler.Sourcing;
127
using Elastic.Markdown;
13-
using Elastic.Markdown.CrossLinks;
148
using Elastic.Markdown.Diagnostics;
159
using Elastic.Markdown.IO;
1610
using Elastic.Markdown.IO.Configuration;
17-
using Elastic.Markdown.IO.Navigation;
18-
using Microsoft.Extensions.Logging.Abstractions;
1911

2012
namespace Documentation.Assembler.Navigation;
2113

@@ -25,8 +17,9 @@ public record GlobalNavigationPathProvider : IDocumentationFileOutputProvider
2517
private readonly AssembleContext _context;
2618

2719
private ImmutableSortedSet<string> TableOfContentsPrefixes { get; }
20+
private ImmutableSortedSet<string> PhantomPrefixes { get; }
2821

29-
public GlobalNavigationPathProvider(AssembleSources assembleSources, AssembleContext context)
22+
public GlobalNavigationPathProvider(GlobalNavigationFile navigationFile, AssembleSources assembleSources, AssembleContext context)
3023
{
3124
_assembleSources = assembleSources;
3225
_context = context;
@@ -36,45 +29,14 @@ public GlobalNavigationPathProvider(AssembleSources assembleSources, AssembleCon
3629
.Select(v => v.Source.ToString())
3730
.OrderByDescending(v => v.Length)
3831
];
39-
}
40-
41-
/*
42-
public IFileInfo? LocateDocSetYaml(Uri crossLinkUri)
43-
{
44-
if (!TryGetCheckout(crossLinkUri, out var checkout))
45-
return null;
46-
47-
var tocDirectory = _readFs.DirectoryInfo.New(Path.Combine(checkout.Directory.FullName, crossLinkUri.Host, crossLinkUri.AbsolutePath.TrimStart('/')));
48-
if (!tocDirectory.Exists)
49-
{
50-
_context.Collector.EmitError(_context.NavigationPath, $"Unable to find toc directory: {tocDirectory.FullName}");
51-
return null;
52-
}
53-
54-
var docsetYaml = _readFs.FileInfo.New(Path.Combine(tocDirectory.FullName, "docset.yml"));
55-
var tocYaml = _readFs.FileInfo.New(Path.Combine(tocDirectory.FullName, "toc.yml"));
56-
if (!docsetYaml.Exists && !tocYaml.Exists)
57-
{
58-
_context.Collector.EmitError(_context.NavigationPath, $"Unable to find docset.yml or toc.yml in: {tocDirectory.FullName}");
59-
return null;
60-
}
6132

62-
return docsetYaml.Exists ? docsetYaml : tocYaml;
33+
PhantomPrefixes = [..navigationFile.Phantoms
34+
.Select(p => p.Source.ToString())
35+
.OrderByDescending(v => v.Length)
36+
.ToArray()
37+
];
6338
}
6439

65-
public bool TryGetCheckout(Uri crossLinkUri, [NotNullWhen(true)] out Checkout? checkout)
66-
{
67-
if (_checkoutsLookup.TryGetValue(crossLinkUri.Scheme, out checkout))
68-
return true;
69-
70-
_context.Collector.EmitError(_context.ConfigurationPath,
71-
!_repoConfigLookup.TryGetValue(crossLinkUri.Scheme, out _)
72-
? $"Repository: '{crossLinkUri.Scheme}' is not defined in assembler.yml"
73-
: $"Unable to find checkout for repository: {crossLinkUri.Scheme}"
74-
);
75-
return false;
76-
}*/
77-
7840
public IFileInfo? OutputFile(DocumentationSet documentationSet, IFileInfo defaultOutputFile, string relativePath)
7941
{
8042
if (relativePath.StartsWith("_static/", StringComparison.Ordinal))
@@ -124,6 +86,12 @@ public bool TryGetCheckout(Uri crossLinkUri, [NotNullWhen(true)] out Checkout? c
12486
if (relativePath.EndsWith(".asciidoc", StringComparison.Ordinal))
12587
return null;
12688

89+
foreach (var prefix in PhantomPrefixes)
90+
{
91+
if (lookup.StartsWith(prefix, StringComparison.Ordinal))
92+
return null;
93+
}
94+
12795
var fallBack = fs.Path.Combine(outputDirectory.FullName, "_failed", repositoryName, relativePath);
12896
_context.Collector.EmitError(_context.NavigationPath, $"No toc for output path: '{lookup}' falling back to: '{fallBack}'");
12997
return fs.FileInfo.New(fallBack);

0 commit comments

Comments
 (0)