in src/tooling/docs-assembler/Navigation/GlobalNavigation.cs [87:171]
private IReadOnlyCollection<INavigationItem> BuildNavigation(IReadOnlyCollection<TocReference> node, int depth, INavigationItem? parent = null)
{
var list = new List<INavigationItem>();
foreach (var toc in node)
{
if (!_assembleSources.TreeCollector.TryGetTableOfContentsTree(toc.Source, out var tree))
{
_navigationFile.EmitWarning($"No {nameof(TableOfContentsTree)} found for {toc.Source}");
if (!_assembleSources.TocTopLevelMappings.TryGetValue(toc.Source, out var topLevel))
{
_navigationFile.EmitError(
$"Can not create temporary {nameof(TableOfContentsTree)} for {toc.Source} since no top level source could be located for it"
);
continue;
}
// TODO passing DocumentationSet to TableOfContentsTree constructor is temporary
// We only build this fallback in order to aid with bootstrapping the navigation
if (!_assembleSources.TreeCollector.TryGetTableOfContentsTree(topLevel.TopLevelSource, out tree))
{
_navigationFile.EmitError(
$"Can not create temporary {nameof(TableOfContentsTree)} for {topLevel.TopLevelSource} since no top level source could be located for it"
);
continue;
}
var documentationSet = tree.DocumentationSet ?? (tree.Parent as TableOfContentsTree)?.DocumentationSet
?? throw new InvalidOperationException($"Can not fall back for {toc.Source} because no documentation set is available");
var lookups = new NavigationLookups
{
FlatMappedFiles = new Dictionary<string, DocumentationFile>().ToFrozenDictionary(),
TableOfContents = [],
EnabledExtensions = documentationSet.EnabledExtensions,
FilesGroupedByFolder = new Dictionary<string, DocumentationFile[]>().ToFrozenDictionary(),
};
var fileIndex = 0;
tree = new TableOfContentsTree(
documentationSet,
toc.Source,
documentationSet.Context,
lookups,
_assembleSources.TreeCollector, ref fileIndex);
}
var navigationItem = new TocNavigationItem(depth, tree, toc.Source, parent);
var tocChildren = toc.Children.OfType<TocReference>().ToArray();
var tocNavigationItems = BuildNavigation(tocChildren, depth + 1);
var allNavigationItems =
depth == 0
? tocNavigationItems.Concat(tree.NavigationItems)
: tree.NavigationItems.Concat(tocNavigationItems);
var cleanNavigationItems = new List<INavigationItem>();
var seenSources = new HashSet<Uri>();
foreach (var item in allNavigationItems)
{
if (item is not TocNavigationItem tocNav)
{
cleanNavigationItems.Add(item);
continue;
}
if (seenSources.Contains(tocNav.Source))
continue;
if (!_assembleSources.TocTopLevelMappings.TryGetValue(tocNav.Source, out var mapping))
continue;
if (mapping.ParentSource != tree.Source)
continue;
_ = seenSources.Add(tocNav.Source);
cleanNavigationItems.Add(item);
item.Parent = navigationItem;
}
tree.NavigationItems = cleanNavigationItems.ToArray();
list.Add(navigationItem);
}
return list.ToArray().AsReadOnly();
}