Skip to content

Commit 12f9ed1

Browse files
authored
Add support for hidden files in documentation TOC (#381)
Hidden files can now be excluded from navigation and indexing but still linked directly if needed. Adjustments include handling hidden attributes in TOC parsing and ensuring related navigation logic respects these exclusions.
1 parent 8e2adbc commit 12f9ed1

File tree

11 files changed

+116
-21
lines changed

11 files changed

+116
-21
lines changed

docs/configure/content-set/navigation.md

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,19 @@ toc:
7373
- file: index.md
7474
```
7575

76-
The table of contents can be created independent of the directory structure of the files it defines. You can use directories to define nesting in the TOC, but you don't have to. For example, both of the following create the same nav structure:
76+
The TOC in principle follows the directory structure on disk.
77+
78+
#### `folder:`
7779

7880
```yaml
7981
...
80-
- file: subsection/index.md
81-
children:
82-
- file: subsection/page-one.md
83-
- file: subsection/page-two.md
82+
- folder: subsection
8483
```
8584

85+
If a folder does not explicitly define `children` all markdown files within that folder are included automatically
86+
87+
If a folder does define `children` all markdown files within that folder have to be included. `docs-builder` will error if it detects dangling documentation files.
88+
8689
```yaml
8790
...
8891
- folder: subsection
@@ -92,7 +95,33 @@ The table of contents can be created independent of the directory structure of t
9295
- file: page-two.md
9396
```
9497

95-
#### Nest `toc`
98+
#### Virtual grouping
99+
100+
A `file` element may include children to create a virtual grouping that
101+
does not match the directory structure.
102+
103+
```yaml
104+
...
105+
- file: subsection/index.md
106+
children:
107+
- file: subsection/page-one.md
108+
- file: subsection/page-two.md
109+
```
110+
111+
A `file` may only select siblings and more deeply nested files as its children. It may not select files outside its own subtree on disk.
112+
113+
#### Hidden files
114+
115+
A hidden file can be declared in the TOC.
116+
```yaml
117+
- hidden: developer-pages.md
118+
```
119+
120+
It may not have any children and won't show up in the navigation.
121+
122+
It [may be linked to locally however](../../developer-notes.md)
123+
124+
#### Nesting `toc`
96125

97126
The `toc` key can include nested `toc.yml` files.
98127

@@ -122,4 +151,6 @@ See [Attributes](./attributes.md) to learn more.
122151

123152
As a rule, each `docset.yml` file can only be included once in the assembler. This prevents us from accidentally duplicating pages in the docs. However, there are times when you want to split content sets and include them partially in different areas of the TOC. That's what `toc.yml` files are for. These files split one documentation set into multiple “sub-TOCs,” each mapped to a different navigation node.
124153

125-
All configuration options that `docset.yml` supports are supported by `toc.yml`.
154+
A `toc.yml` file may only declare a nested [TOC](#toc), other options are ignored.
155+
156+
A `toc.yml` may not link to further nested `toc.yml` files. Doing so will result in an error

docs/developer-notes.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# These are developer notes
2+
3+
Because this page is included as hidden:
4+
5+
6+
```yaml
7+
toc:
8+
- file: index.md
9+
- hidden: developer-notes.md
10+
```
11+
12+
This page
13+
14+
- will not appear in the TOC navigation
15+
- will always include a `noindexed` meta header.
16+
17+
18+
This page **can** be linked to using [a regular link](developer-notes.md)

docs/docset.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ subs:
2424
a-global-variable: "This was defined in docset.yml"
2525
toc:
2626
- file: index.md
27+
- hidden: developer-notes.md
2728
- folder: contribute
2829
children:
2930
- file: index.md

src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildCon
3939
if (!sourceFile.Exists)
4040
{
4141
Project = "unknown";
42-
TableOfContents = [];
4342
context.EmitWarning(sourceFile, "No configuration file found");
4443
return;
4544
}
@@ -138,6 +137,7 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
138137
ConfigurationFile? toc = null;
139138
var fileFound = false;
140139
var folderFound = false;
140+
var hiddenFile = false;
141141
IReadOnlyCollection<ITocItem>? children = null;
142142
foreach (var entry in tocEntry.Children)
143143
{
@@ -147,8 +147,10 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
147147
case "toc":
148148
toc = ReadNestedToc(entry, parentPath, out fileFound);
149149
break;
150+
case "hidden":
150151
case "file":
151-
file = ReadFile(entry, parentPath, out fileFound);
152+
hiddenFile = key == "hidden";
153+
file = ReadFile(entry, parentPath, key, out fileFound);
152154
break;
153155
case "folder":
154156
folder = ReadFolder(entry, parentPath, out folderFound);
@@ -169,7 +171,7 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
169171
}
170172

171173
if (file is not null)
172-
return [new FileReference($"{parentPath}/{file}".TrimStart('/'), fileFound, children ?? [])];
174+
return [new FileReference($"{parentPath}/{file}".TrimStart('/'), fileFound, hiddenFile, children ?? [])];
173175

174176
if (folder is not null)
175177
{
@@ -227,7 +229,7 @@ private Dictionary<string, string> ReadDictionary(KeyValuePair<YamlNode, YamlNod
227229
return folder;
228230
}
229231

230-
private string? ReadFile(KeyValuePair<YamlNode, YamlNode> entry, string parentPath, out bool found)
232+
private string? ReadFile(KeyValuePair<YamlNode, YamlNode> entry, string parentPath, string key, out bool found)
231233
{
232234
found = false;
233235
var file = ReadString(entry);

src/Elastic.Markdown/IO/Configuration/ITocItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ namespace Elastic.Markdown.IO.Configuration;
66

77
public interface ITocItem;
88

9-
public record FileReference(string Path, bool Found, IReadOnlyCollection<ITocItem> Children) : ITocItem;
9+
public record FileReference(string Path, bool Found, bool Hidden, IReadOnlyCollection<ITocItem> Children) : ITocItem;
1010

1111
public record FolderReference(string Path, bool Found, IReadOnlyCollection<ITocItem> Children) : ITocItem;

src/Elastic.Markdown/IO/DocumentationSet.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,39 @@ public DocumentationSet(BuildContext context)
9292
return null;
9393
}
9494

95+
public MarkdownFile? GetPrevious(MarkdownFile current)
96+
{
97+
var index = current.NavigationIndex;
98+
do
99+
{
100+
var previous = MarkdownFiles.GetValueOrDefault(index - 1);
101+
if (previous is null)
102+
return null;
103+
if (!previous.Hidden)
104+
return previous;
105+
index--;
106+
} while (index > 0);
107+
108+
return null;
109+
}
110+
111+
public MarkdownFile? GetNext(MarkdownFile current)
112+
{
113+
var index = current.NavigationIndex;
114+
do
115+
{
116+
var previous = MarkdownFiles.GetValueOrDefault(index + 1);
117+
if (previous is null)
118+
return null;
119+
if (!previous.Hidden)
120+
return previous;
121+
index++;
122+
} while (index <= MarkdownFiles.Count - 1);
123+
124+
return null;
125+
}
126+
127+
95128
public async Task ResolveDirectoryTree(Cancel ctx) =>
96129
await Tree.Resolve(ctx);
97130

src/Elastic.Markdown/IO/MarkdownFile.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public DocumentationGroup? Parent
3939
set => _parent = value;
4040
}
4141

42+
public bool Hidden { get; internal set; }
4243
public string? UrlPathPrefix { get; }
4344
private MarkdownParser MarkdownParser { get; }
4445
public YamlFrontMatter? YamlFrontMatter { get; private set; }

src/Elastic.Markdown/IO/Navigation/DocumentationGroup.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,15 @@ public DocumentationGroup(
8989
continue;
9090

9191
md.Parent = this;
92+
md.Hidden = file.Hidden;
9293
var navigationIndex = Interlocked.Increment(ref fileIndex);
9394
md.NavigationIndex = navigationIndex;
9495

9596
if (file.Children.Count > 0 && d is MarkdownFile virtualIndex)
9697
{
98+
if (file.Hidden)
99+
context.EmitError(context.ConfigurationPath, $"The following file is hidden but has children: {file.Path}");
100+
97101
var group = new DocumentationGroup(context, file.Children, lookup, folderLookup, ref fileIndex, depth + 1, virtualIndex)
98102
{
99103
Parent = this
@@ -110,7 +114,8 @@ public DocumentationGroup(
110114
// add the page to navigation items unless it's the index file
111115
// the index file can either be the discovered `index.md` or the parent group's
112116
// explicit index page. E.g. when grouping related files together.
113-
if (indexFile != md)
117+
// if the page is referenced as hidden in the TOC do not include it in the navigation
118+
if (indexFile != md && !md.Hidden)
114119
navigationItems.Add(new FileNavigation(index, depth, md));
115120
}
116121
else if (tocItem is FolderReference folder)
@@ -120,7 +125,7 @@ public DocumentationGroup(
120125
&& folderLookup.TryGetValue(folder.Path, out var documentationFiles))
121126
{
122127
children = documentationFiles
123-
.Select(d => new FileReference(d.RelativePath, true, []))
128+
.Select(d => new FileReference(d.RelativePath, true, false, []))
124129
.ToArray();
125130
}
126131

src/Elastic.Markdown/IO/State/LinkReference.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ public record LinkMetadata
1212
[JsonPropertyName("anchors")]
1313
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
1414
public required string[]? Anchors { get; init; } = [];
15+
16+
[JsonPropertyName("hidden")]
17+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
18+
public required bool Hidden { get; init; }
1519
}
1620

1721
public record LinkReference
@@ -33,11 +37,11 @@ public static LinkReference Create(DocumentationSet set)
3337
{
3438
var crossLinks = set.Context.Collector.CrossLinks.ToHashSet().ToArray();
3539
var links = set.MarkdownFiles.Values
36-
.Select(m => (m.RelativePath, m.Anchors))
40+
.Select(m => (m.RelativePath, File: m))
3741
.ToDictionary(k => k.RelativePath, v =>
3842
{
39-
var anchors = v.Anchors.Count == 0 ? null : v.Anchors.ToArray();
40-
return new LinkMetadata { Anchors = anchors };
43+
var anchors = v.File.Anchors.Count == 0 ? null : v.File.Anchors.ToArray();
44+
return new LinkMetadata { Anchors = anchors, Hidden = v.File.Hidden };
4145
});
4246
return new LinkReference
4347
{

src/Elastic.Markdown/Slices/HtmlWriter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = defau
4545
await DocumentationSet.Tree.Resolve(ctx);
4646
var navigationHtml = await RenderNavigation(markdown, ctx);
4747

48-
var previous = DocumentationSet.MarkdownFiles.GetValueOrDefault(markdown.NavigationIndex - 1);
49-
var next = DocumentationSet.MarkdownFiles.GetValueOrDefault(markdown.NavigationIndex + 1);
48+
var previous = DocumentationSet.GetPrevious(markdown);
49+
var next = DocumentationSet.GetNext(markdown);
5050

5151
var remote = DocumentationSet.Context.Git.RepositoryName;
5252
var branch = DocumentationSet.Context.Git.Branch;
@@ -67,7 +67,7 @@ public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = defau
6767
UrlPathPrefix = markdown.UrlPathPrefix,
6868
Applies = markdown.YamlFrontMatter?.AppliesTo,
6969
GithubEditUrl = editUrl,
70-
AllowIndexing = DocumentationSet.Context.AllowIndexing
70+
AllowIndexing = DocumentationSet.Context.AllowIndexing && !markdown.Hidden
7171
});
7272
return await slice.RenderAsync(cancellationToken: ctx);
7373
}

0 commit comments

Comments
 (0)