Skip to content

Commit f86d89e

Browse files
authored
Navigation powered by docset.yml (#61)
1 parent 2b862bc commit f86d89e

File tree

13 files changed

+130
-129
lines changed

13 files changed

+130
-129
lines changed

docs/source/docset.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ exclude:
33
- '_*.md'
44
toc:
55
- file: index.md
6-
- folder: markup
76
- folder: elastic
87
children:
98
- file: index.md
@@ -13,6 +12,10 @@ toc:
1312
- folder: search-labs
1413
children:
1514
- file: index.md
15+
- file: install.md
16+
children:
17+
- file: install/cloud.md
18+
- file: install/docker.md
1619
- file: chat.md
1720
children:
1821
- file: chat/req.md
@@ -21,12 +24,9 @@ toc:
2124
children:
2225
- file: search/req.md
2326
- file: search/setup.md
24-
- file: install.md
25-
children:
26-
- file: install/cloud.md
27-
- file: install/docker.md
27+
- folder: markup
2828
- folder: nested
2929
children:
3030
- folder: content
3131
- file: index.md
32-
- folder: versioning
32+
- folder: versioning

src/Elastic.Markdown/IO/ConfigurationFile.cs

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class ConfigurationFile : DocumentationFile
2121
public IReadOnlyCollection<ITocItem> TableOfContents { get; } = [];
2222

2323
public HashSet<string> Files { get; } = new(StringComparer.OrdinalIgnoreCase);
24-
public HashSet<string> Folders { get; } = new(StringComparer.OrdinalIgnoreCase);
24+
public HashSet<string> ImplicitFolders { get; } = new(StringComparer.OrdinalIgnoreCase);
2525
public Glob[] Globs { get; } = [];
2626

2727
public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildContext context)
@@ -72,7 +72,7 @@ public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildCon
7272
break;
7373
}
7474
}
75-
Globs = Folders.Select(f=> Glob.Parse($"{f}/*.md")).ToArray();
75+
Globs = ImplicitFolders.Select(f=> Glob.Parse($"{f}/*.md")).ToArray();
7676
}
7777

7878
private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, string parentPath)
@@ -99,18 +99,19 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
9999
{
100100
string? file = null;
101101
string? folder = null;
102-
var found = false;
102+
var fileFound = false;
103+
var folderFound = false;
103104
IReadOnlyCollection<ITocItem>? children = null;
104105
foreach (var entry in tocEntry.Children)
105106
{
106107
var key = ((YamlScalarNode)entry.Key).Value;
107108
switch (key)
108109
{
109110
case "file":
110-
file = ReadFile(entry, parentPath);
111+
file = ReadFile(entry, parentPath, out fileFound);
111112
break;
112113
case "folder":
113-
folder = ReadString(entry);
114+
folder = ReadFolder(entry, parentPath, out folderFound);
114115
parentPath += $"/{folder}";
115116
break;
116117
case "children":
@@ -120,30 +121,47 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
120121
}
121122

122123
if (file is not null)
123-
return new TocFile(file, found, children ?? []);
124+
return new TocFile($"{parentPath}/{file}".TrimStart('/'), fileFound, children ?? []);
124125

125126
if (folder is not null)
126127
{
127128
if (children is null)
128-
Folders.Add(parentPath.TrimStart('/'));
129+
ImplicitFolders.Add(parentPath.TrimStart('/'));
129130

130-
return new TocFolder(folder, children ?? []);
131+
return new TocFolder($"{parentPath}".TrimStart('/'), folderFound, children ?? []);
131132
}
132133

133134
return null;
134135
}
135136

136-
private string? ReadFile(KeyValuePair<YamlNode, YamlNode> entry, string parentPath)
137+
private string? ReadFolder(KeyValuePair<YamlNode, YamlNode> entry, string parentPath, out bool found)
137138
{
138-
var file = ReadString(entry);
139-
if (file is not null)
139+
found = false;
140+
var folder = ReadString(entry);
141+
if (folder is not null)
140142
{
141-
var path = Path.Combine(_rootPath.FullName, parentPath.TrimStart('/'), file);
142-
if (!_context.ReadFileSystem.FileInfo.New(path).Exists)
143-
EmitError($"File '{path}' does not exist", entry.Key);
143+
var path = Path.Combine(_rootPath.FullName, parentPath.TrimStart('/'), folder);
144+
if (!_context.ReadFileSystem.DirectoryInfo.New(path).Exists)
145+
EmitError($"Directory '{path}' does not exist", entry.Key);
146+
else
147+
found = true;
144148
}
149+
return folder;
150+
}
145151

152+
private string? ReadFile(KeyValuePair<YamlNode, YamlNode> entry, string parentPath, out bool found)
153+
{
154+
found = false;
155+
var file = ReadString(entry);
156+
if (file is null) return null;
157+
158+
var path = Path.Combine(_rootPath.FullName, parentPath.TrimStart('/'), file);
159+
if (!_context.ReadFileSystem.FileInfo.New(path).Exists)
160+
EmitError($"File '{path}' does not exist", entry.Key);
161+
else
162+
found = true;
146163
Files.Add((parentPath + "/" + file).TrimStart('/'));
164+
147165
return file;
148166
}
149167

@@ -210,31 +228,3 @@ private void EmitWarning(string message, Mark? start = null, Mark? end = null, i
210228
}
211229
}
212230

213-
public interface ITocItem;
214-
215-
public record TocFile(string Path, bool Found, IReadOnlyCollection<ITocItem> Children) : ITocItem;
216-
217-
public record TocFolder(string Path, IReadOnlyCollection<ITocItem> Children) : ITocItem;
218-
219-
220-
/*
221-
exclude:
222-
- notes.md
223-
- '**ignore.md'
224-
toc:
225-
- file: index.md
226-
- file: config.md
227-
- file: search.md
228-
children:
229-
- file: search-part2.md
230-
- folder: search
231-
- folder: my-folder1
232-
exclude:
233-
- '_*.md'
234-
- folder: my-folder2
235-
children:
236-
- file: subpath/file.md
237-
- file: file.md
238-
- pattern: *.md
239-
- folder: sub/folder
240-
*/

src/Elastic.Markdown/IO/DocumentationFile.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public abstract class DocumentationFile(IFileInfo sourceFile, IDirectoryInfo roo
99
{
1010
public IFileInfo SourceFile { get; } = sourceFile;
1111
public string RelativePath { get; } = Path.GetRelativePath(rootPath.FullName, sourceFile.FullName);
12+
public string RelativeFolder { get; } = Path.GetRelativePath(rootPath.FullName, sourceFile.Directory!.FullName);
1213

1314
public FileInfo OutputFile(IDirectoryInfo outputPath) =>
1415
new(Path.Combine(outputPath.FullName, RelativePath.Replace(".md", ".html")));
Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,85 @@
11
// Licensed to Elasticsearch B.V under one or more agreements.
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
4+
45
using Markdig.Helpers;
56

67
namespace Elastic.Markdown.IO;
78

89
public class DocumentationFolder
910
{
1011
public MarkdownFile? Index { get; }
11-
private MarkdownFile[] Files { get; }
12-
private DocumentationFolder[] Nested { get; }
1312

14-
public OrderedList<MarkdownFile> FilesInOrder { get; private set; }
15-
public OrderedList<DocumentationFolder> GroupsInOrder { get; private set; }
13+
public List<MarkdownFile> FilesInOrder { get; } = new();
14+
public List<DocumentationFolder> GroupsInOrder { get; } = new();
15+
16+
private HashSet<MarkdownFile> OwnFiles { get; }
17+
1618
public int Level { get; }
17-
public string? FolderName { get; }
1819

19-
public DocumentationFolder(Dictionary<string, MarkdownFile[]> markdownFiles, int level, string folderName)
20+
public DocumentationFolder(IReadOnlyCollection<ITocItem> toc,
21+
IDictionary<string, DocumentationFile> lookup,
22+
IDictionary<string, DocumentationFile[]> folderLookup,
23+
int level = 0,
24+
MarkdownFile? index = null)
2025
{
2126
Level = level;
22-
FolderName = folderName;
23-
24-
var files = markdownFiles
25-
.Where(k => k.Key.EndsWith(".md")).SelectMany(g => g.Value)
26-
.Where(file => file.ParentFolders.Count == level)
27-
.ToArray();
28-
29-
30-
Files = files
31-
.Where(file => file.FileName != "index.md")
32-
.ToArray();
33-
34-
FilesInOrder = new OrderedList<MarkdownFile>(Files);
35-
36-
Index = files.FirstOrDefault(f => f.FileName == "index.md");
27+
Index = index;
3728

38-
var newLevel = level + 1;
39-
var groups = new List<DocumentationFolder>();
40-
foreach (var kv in markdownFiles.Where(kv=> !kv.Key.EndsWith(".md")))
29+
foreach (var tocItem in toc)
4130
{
42-
var folder = kv.Key;
43-
var folderFiles = kv.Value
44-
.Where(file => file.ParentFolders.Count > level)
45-
.Where(file => file.ParentFolders[level] == folder).ToArray();
46-
var mapped = folderFiles
47-
.GroupBy(file =>
48-
{
49-
var path = file.ParentFolders.Count > newLevel ? file.ParentFolders[newLevel] : file.FileName;
50-
return path;
51-
})
52-
.ToDictionary(k => k.Key, v => v.ToArray());
53-
var documentationGroup = new DocumentationFolder(mapped, newLevel, folder);
54-
groups.Add(documentationGroup);
31+
if (tocItem is TocFile file)
32+
{
33+
if (!lookup.TryGetValue(file.Path, out var d) || d is not MarkdownFile md)
34+
continue;
5535

36+
if (file.Children.Count > 0 && d is MarkdownFile virtualIndex)
37+
{
38+
var group = new DocumentationFolder(file.Children, lookup, folderLookup, level + 1, virtualIndex);
39+
GroupsInOrder.Add(group);
40+
continue;
41+
}
42+
43+
FilesInOrder.Add(md);
44+
if (file.Path.EndsWith("index.md") && d is MarkdownFile i)
45+
Index ??= i;
46+
}
47+
else if (tocItem is TocFolder folder)
48+
{
49+
var children = folder.Children;
50+
if (children.Count == 0
51+
&& folderLookup.TryGetValue(folder.Path, out var documentationFiles))
52+
{
53+
children = documentationFiles
54+
.Select(d => new TocFile(d.RelativePath, true, []))
55+
.ToArray();
56+
}
57+
58+
var group = new DocumentationFolder(children, lookup, folderLookup, level + 1);
59+
GroupsInOrder.Add(group);
60+
}
5661
}
57-
Nested = groups.ToArray();
58-
GroupsInOrder = new OrderedList<DocumentationFolder>(Nested);
62+
63+
Index ??= FilesInOrder.FirstOrDefault();
64+
if (Index != null)
65+
FilesInOrder = FilesInOrder.Except(new[] { Index }).ToList();
66+
OwnFiles = [..FilesInOrder];
5967
}
6068

6169
public bool HoldsCurrent(MarkdownFile current) =>
62-
Index == current || Files.Contains(current) || Nested.Any(n => n.HoldsCurrent(current));
70+
Index == current || OwnFiles.Contains(current) || GroupsInOrder.Any(n => n.HoldsCurrent(current));
6371

6472
private bool _resolved;
73+
6574
public async Task Resolve(Cancel ctx = default)
6675
{
6776
if (_resolved) return;
6877

69-
await Parallel.ForEachAsync(Files, ctx, async (file, token) => await file.ParseAsync(token));
70-
await Parallel.ForEachAsync(Nested, ctx, async (group, token) => await group.Resolve(token));
78+
await Parallel.ForEachAsync(FilesInOrder, ctx, async (file, token) => await file.ParseAsync(token));
79+
await Parallel.ForEachAsync(GroupsInOrder, ctx, async (group, token) => await group.Resolve(token));
7180

7281
await (Index?.ParseAsync(ctx) ?? Task.CompletedTask);
7382

74-
var fileList = new OrderedList<MarkdownFile>();
75-
var groupList = new OrderedList<DocumentationFolder>();
76-
77-
78-
FilesInOrder = fileList;
79-
GroupsInOrder = groupList;
8083
_resolved = true;
8184
}
8285
}

src/Elastic.Markdown/IO/DocumentationSet.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,14 @@ public DocumentationSet(IDirectoryInfo? sourcePath, IDirectoryInfo? outputPath,
4747

4848
.ToList();
4949

50-
5150
LastWrite = Files.Max(f => f.SourceFile.LastWriteTimeUtc);
5251

5352
FlatMappedFiles = Files.ToDictionary(file => file.RelativePath, file => file);
53+
var folderFiles = Files
54+
.GroupBy(file => file.RelativeFolder)
55+
.ToDictionary(g=>g.Key, g=>g.ToArray());
5456

55-
var markdownFiles = Files.OfType<MarkdownFile>()
56-
.Where(file => !file.RelativePath.StartsWith("_"))
57-
.GroupBy(file =>
58-
{
59-
var path = file.ParentFolders.Count >= 1 ? file.ParentFolders[0] : file.FileName;
60-
return path;
61-
})
62-
.ToDictionary(k => k.Key, v => v.ToArray());
63-
64-
Tree = new DocumentationFolder(markdownFiles, 0, "");
57+
Tree = new DocumentationFolder(Configuration.TableOfContents, FlatMappedFiles, folderFiles);
6558
}
6659

6760
private DocumentationFile CreateMarkDownFile(IFileInfo file, BuildContext context)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
namespace Elastic.Markdown.IO;
6+
7+
public interface ITocItem;
8+
9+
public record TocFile(string Path, bool Found, IReadOnlyCollection<ITocItem> Children) : ITocItem;
10+
11+
public record TocFolder(string Path, bool Found, IReadOnlyCollection<ITocItem> Children) : ITocItem;

src/Elastic.Markdown/Slices/HtmlWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = defau
5050
PageTocItems = markdown.TableOfContents,
5151
Tree = DocumentationSet.Tree,
5252
CurrentDocument = markdown,
53-
Navigation = navigationHtml,
53+
NavigationHtml = navigationHtml,
5454
UrlPathPrefix = markdown.UrlPathPrefix
5555
});
5656
return await slice.RenderAsync(cancellationToken: ctx);

src/Elastic.Markdown/Slices/Index.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
PageTocItems = Model.PageTocItems,
88
Tree = Model.Tree,
99
CurrentDocument = Model.CurrentDocument,
10-
Navigation = Model.Navigation,
10+
NavigationHtml = Model.NavigationHtml,
1111
UrlPathPrefix = Model.UrlPathPrefix,
1212
};
1313
}

src/Elastic.Markdown/Slices/_Layout.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</div>
1717
</div>
1818
<div class="sy-page sy-container flex mx-auto">
19-
@(new HtmlString(Model.Navigation))
19+
@(new HtmlString(Model.NavigationHtml))
2020
@*@(await RenderPartialAsync(Elastic.Markdown.Slices.Layout._TocTree.Create(Model)))*@
2121
@(await RenderPartialAsync(_TableOfContents.Create(Model)))
2222
<main class="sy-main w-full max-sm:max-w-full print:pt-6">

src/Elastic.Markdown/Slices/_ViewModels.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class IndexViewModel
1212
public required DocumentationFolder Tree { get; init; }
1313
public required IReadOnlyCollection<PageTocItem> PageTocItems { get; init; }
1414
public required MarkdownFile CurrentDocument { get; init; }
15-
public required string Navigation { get; init; }
15+
public required string NavigationHtml { get; init; }
1616
public required string? UrlPathPrefix { get; init; }
1717
}
1818

@@ -22,7 +22,7 @@ public class LayoutViewModel
2222
public required IReadOnlyCollection<PageTocItem> PageTocItems { get; init; }
2323
public required DocumentationFolder Tree { get; init; }
2424
public required MarkdownFile CurrentDocument { get; init; }
25-
public required string Navigation { get; set; }
25+
public required string NavigationHtml { get; set; }
2626
public required string? UrlPathPrefix { get; set; }
2727

2828

0 commit comments

Comments
 (0)