Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation powered by docset.yml #61

Merged
merged 2 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/source/docset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ exclude:
- '_*.md'
toc:
- file: index.md
- folder: markup
- folder: elastic
children:
- file: index.md
Expand All @@ -13,6 +12,10 @@ toc:
- folder: search-labs
children:
- file: index.md
- file: install.md
children:
- file: install/cloud.md
- file: install/docker.md
- file: chat.md
children:
- file: chat/req.md
Expand All @@ -21,12 +24,9 @@ toc:
children:
- file: search/req.md
- file: search/setup.md
- file: install.md
children:
- file: install/cloud.md
- file: install/docker.md
- folder: markup
- folder: nested
children:
- folder: content
- file: index.md
- folder: versioning
- folder: versioning
74 changes: 32 additions & 42 deletions src/Elastic.Markdown/IO/ConfigurationFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class ConfigurationFile : DocumentationFile
public IReadOnlyCollection<ITocItem> TableOfContents { get; } = [];

public HashSet<string> Files { get; } = new(StringComparer.OrdinalIgnoreCase);
public HashSet<string> Folders { get; } = new(StringComparer.OrdinalIgnoreCase);
public HashSet<string> ImplicitFolders { get; } = new(StringComparer.OrdinalIgnoreCase);
public Glob[] Globs { get; } = [];

public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildContext context)
Expand Down Expand Up @@ -72,7 +72,7 @@ public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildCon
break;
}
}
Globs = Folders.Select(f=> Glob.Parse($"{f}/*.md")).ToArray();
Globs = ImplicitFolders.Select(f=> Glob.Parse($"{f}/*.md")).ToArray();
}

private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, string parentPath)
Expand All @@ -99,18 +99,19 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
{
string? file = null;
string? folder = null;
var found = false;
var fileFound = false;
var folderFound = false;
IReadOnlyCollection<ITocItem>? children = null;
foreach (var entry in tocEntry.Children)
{
var key = ((YamlScalarNode)entry.Key).Value;
switch (key)
{
case "file":
file = ReadFile(entry, parentPath);
file = ReadFile(entry, parentPath, out fileFound);
break;
case "folder":
folder = ReadString(entry);
folder = ReadFolder(entry, parentPath, out folderFound);
parentPath += $"/{folder}";
break;
case "children":
Expand All @@ -120,30 +121,47 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
}

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

if (folder is not null)
{
if (children is null)
Folders.Add(parentPath.TrimStart('/'));
ImplicitFolders.Add(parentPath.TrimStart('/'));

return new TocFolder(folder, children ?? []);
return new TocFolder($"{parentPath}".TrimStart('/'), folderFound, children ?? []);
}

return null;
}

private string? ReadFile(KeyValuePair<YamlNode, YamlNode> entry, string parentPath)
private string? ReadFolder(KeyValuePair<YamlNode, YamlNode> entry, string parentPath, out bool found)
{
var file = ReadString(entry);
if (file is not null)
found = false;
var folder = ReadString(entry);
if (folder is not null)
{
var path = Path.Combine(_rootPath.FullName, parentPath.TrimStart('/'), file);
if (!_context.ReadFileSystem.FileInfo.New(path).Exists)
EmitError($"File '{path}' does not exist", entry.Key);
var path = Path.Combine(_rootPath.FullName, parentPath.TrimStart('/'), folder);
if (!_context.ReadFileSystem.DirectoryInfo.New(path).Exists)
EmitError($"Directory '{path}' does not exist", entry.Key);
else
found = true;
}
return folder;
}

private string? ReadFile(KeyValuePair<YamlNode, YamlNode> entry, string parentPath, out bool found)
{
found = false;
var file = ReadString(entry);
if (file is null) return null;

var path = Path.Combine(_rootPath.FullName, parentPath.TrimStart('/'), file);
if (!_context.ReadFileSystem.FileInfo.New(path).Exists)
EmitError($"File '{path}' does not exist", entry.Key);
else
found = true;
Files.Add((parentPath + "/" + file).TrimStart('/'));

return file;
}

Expand Down Expand Up @@ -210,31 +228,3 @@ private void EmitWarning(string message, Mark? start = null, Mark? end = null, i
}
}

public interface ITocItem;

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

public record TocFolder(string Path, IReadOnlyCollection<ITocItem> Children) : ITocItem;


/*
exclude:
- notes.md
- '**ignore.md'
toc:
- file: index.md
- file: config.md
- file: search.md
children:
- file: search-part2.md
- folder: search
- folder: my-folder1
exclude:
- '_*.md'
- folder: my-folder2
children:
- file: subpath/file.md
- file: file.md
- pattern: *.md
- folder: sub/folder
*/
1 change: 1 addition & 0 deletions src/Elastic.Markdown/IO/DocumentationFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public abstract class DocumentationFile(IFileInfo sourceFile, IDirectoryInfo roo
{
public IFileInfo SourceFile { get; } = sourceFile;
public string RelativePath { get; } = Path.GetRelativePath(rootPath.FullName, sourceFile.FullName);
public string RelativeFolder { get; } = Path.GetRelativePath(rootPath.FullName, sourceFile.Directory!.FullName);

public FileInfo OutputFile(IDirectoryInfo outputPath) =>
new(Path.Combine(outputPath.FullName, RelativePath.Replace(".md", ".html")));
Expand Down
99 changes: 51 additions & 48 deletions src/Elastic.Markdown/IO/DocumentationFolder.cs
Original file line number Diff line number Diff line change
@@ -1,82 +1,85 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Markdig.Helpers;

namespace Elastic.Markdown.IO;

public class DocumentationFolder
{
public MarkdownFile? Index { get; }
private MarkdownFile[] Files { get; }
private DocumentationFolder[] Nested { get; }

public OrderedList<MarkdownFile> FilesInOrder { get; private set; }
public OrderedList<DocumentationFolder> GroupsInOrder { get; private set; }
public List<MarkdownFile> FilesInOrder { get; } = new();
public List<DocumentationFolder> GroupsInOrder { get; } = new();

private HashSet<MarkdownFile> OwnFiles { get; }

public int Level { get; }
public string? FolderName { get; }

public DocumentationFolder(Dictionary<string, MarkdownFile[]> markdownFiles, int level, string folderName)
public DocumentationFolder(IReadOnlyCollection<ITocItem> toc,
IDictionary<string, DocumentationFile> lookup,
IDictionary<string, DocumentationFile[]> folderLookup,
int level = 0,
MarkdownFile? index = null)
{
Level = level;
FolderName = folderName;

var files = markdownFiles
.Where(k => k.Key.EndsWith(".md")).SelectMany(g => g.Value)
.Where(file => file.ParentFolders.Count == level)
.ToArray();


Files = files
.Where(file => file.FileName != "index.md")
.ToArray();

FilesInOrder = new OrderedList<MarkdownFile>(Files);

Index = files.FirstOrDefault(f => f.FileName == "index.md");
Index = index;

var newLevel = level + 1;
var groups = new List<DocumentationFolder>();
foreach (var kv in markdownFiles.Where(kv=> !kv.Key.EndsWith(".md")))
foreach (var tocItem in toc)
{
var folder = kv.Key;
var folderFiles = kv.Value
.Where(file => file.ParentFolders.Count > level)
.Where(file => file.ParentFolders[level] == folder).ToArray();
var mapped = folderFiles
.GroupBy(file =>
{
var path = file.ParentFolders.Count > newLevel ? file.ParentFolders[newLevel] : file.FileName;
return path;
})
.ToDictionary(k => k.Key, v => v.ToArray());
var documentationGroup = new DocumentationFolder(mapped, newLevel, folder);
groups.Add(documentationGroup);
if (tocItem is TocFile file)
{
if (!lookup.TryGetValue(file.Path, out var d) || d is not MarkdownFile md)
continue;

if (file.Children.Count > 0 && d is MarkdownFile virtualIndex)
{
var group = new DocumentationFolder(file.Children, lookup, folderLookup, level + 1, virtualIndex);
GroupsInOrder.Add(group);
continue;
}

FilesInOrder.Add(md);
if (file.Path.EndsWith("index.md") && d is MarkdownFile i)
Index ??= i;
}
else if (tocItem is TocFolder folder)
{
var children = folder.Children;
if (children.Count == 0
&& folderLookup.TryGetValue(folder.Path, out var documentationFiles))
{
children = documentationFiles
.Select(d => new TocFile(d.RelativePath, true, []))
.ToArray();
}

var group = new DocumentationFolder(children, lookup, folderLookup, level + 1);
GroupsInOrder.Add(group);
}
}
Nested = groups.ToArray();
GroupsInOrder = new OrderedList<DocumentationFolder>(Nested);

Index ??= FilesInOrder.FirstOrDefault();
if (Index != null)
FilesInOrder = FilesInOrder.Except(new[] { Index }).ToList();
OwnFiles = [..FilesInOrder];
}

public bool HoldsCurrent(MarkdownFile current) =>
Index == current || Files.Contains(current) || Nested.Any(n => n.HoldsCurrent(current));
Index == current || OwnFiles.Contains(current) || GroupsInOrder.Any(n => n.HoldsCurrent(current));

private bool _resolved;

public async Task Resolve(Cancel ctx = default)
{
if (_resolved) return;

await Parallel.ForEachAsync(Files, ctx, async (file, token) => await file.ParseAsync(token));
await Parallel.ForEachAsync(Nested, ctx, async (group, token) => await group.Resolve(token));
await Parallel.ForEachAsync(FilesInOrder, ctx, async (file, token) => await file.ParseAsync(token));
await Parallel.ForEachAsync(GroupsInOrder, ctx, async (group, token) => await group.Resolve(token));

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

var fileList = new OrderedList<MarkdownFile>();
var groupList = new OrderedList<DocumentationFolder>();


FilesInOrder = fileList;
GroupsInOrder = groupList;
_resolved = true;
}
}
15 changes: 4 additions & 11 deletions src/Elastic.Markdown/IO/DocumentationSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,14 @@ public DocumentationSet(IDirectoryInfo? sourcePath, IDirectoryInfo? outputPath,

.ToList();


LastWrite = Files.Max(f => f.SourceFile.LastWriteTimeUtc);

FlatMappedFiles = Files.ToDictionary(file => file.RelativePath, file => file);
var folderFiles = Files
.GroupBy(file => file.RelativeFolder)
.ToDictionary(g=>g.Key, g=>g.ToArray());

var markdownFiles = Files.OfType<MarkdownFile>()
.Where(file => !file.RelativePath.StartsWith("_"))
.GroupBy(file =>
{
var path = file.ParentFolders.Count >= 1 ? file.ParentFolders[0] : file.FileName;
return path;
})
.ToDictionary(k => k.Key, v => v.ToArray());

Tree = new DocumentationFolder(markdownFiles, 0, "");
Tree = new DocumentationFolder(Configuration.TableOfContents, FlatMappedFiles, folderFiles);
}

private DocumentationFile CreateMarkDownFile(IFileInfo file, BuildContext context)
Expand Down
11 changes: 11 additions & 0 deletions src/Elastic.Markdown/IO/ITocItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Elastic.Markdown.IO;

public interface ITocItem;

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

public record TocFolder(string Path, bool Found, IReadOnlyCollection<ITocItem> Children) : ITocItem;
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/Slices/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = defau
PageTocItems = markdown.TableOfContents,
Tree = DocumentationSet.Tree,
CurrentDocument = markdown,
Navigation = navigationHtml,
NavigationHtml = navigationHtml,
UrlPathPrefix = markdown.UrlPathPrefix
});
return await slice.RenderAsync(cancellationToken: ctx);
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/Slices/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
PageTocItems = Model.PageTocItems,
Tree = Model.Tree,
CurrentDocument = Model.CurrentDocument,
Navigation = Model.Navigation,
NavigationHtml = Model.NavigationHtml,
UrlPathPrefix = Model.UrlPathPrefix,
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/Slices/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</div>
</div>
<div class="sy-page sy-container flex mx-auto">
@(new HtmlString(Model.Navigation))
@(new HtmlString(Model.NavigationHtml))
@*@(await RenderPartialAsync(Elastic.Markdown.Slices.Layout._TocTree.Create(Model)))*@
@(await RenderPartialAsync(_TableOfContents.Create(Model)))
<main class="sy-main w-full max-sm:max-w-full print:pt-6">
Expand Down
4 changes: 2 additions & 2 deletions src/Elastic.Markdown/Slices/_ViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class IndexViewModel
public required DocumentationFolder Tree { get; init; }
public required IReadOnlyCollection<PageTocItem> PageTocItems { get; init; }
public required MarkdownFile CurrentDocument { get; init; }
public required string Navigation { get; init; }
public required string NavigationHtml { get; init; }
public required string? UrlPathPrefix { get; init; }
}

Expand All @@ -22,7 +22,7 @@ public class LayoutViewModel
public required IReadOnlyCollection<PageTocItem> PageTocItems { get; init; }
public required DocumentationFolder Tree { get; init; }
public required MarkdownFile CurrentDocument { get; init; }
public required string Navigation { get; set; }
public required string NavigationHtml { get; set; }
public required string? UrlPathPrefix { get; set; }


Expand Down
Loading