Skip to content

Commit 816c727

Browse files
authored
Introduce the notion of minimal parsing (#73)
1 parent b04f097 commit 816c727

File tree

13 files changed

+69
-56
lines changed

13 files changed

+69
-56
lines changed

src/Elastic.Markdown/DocumentationGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ await Parallel.ForEachAsync(DocumentationSet.Files, ctx, async (file, token) =>
124124
var outputFile = OutputFile(file.RelativePath);
125125
if (file is MarkdownFile markdown)
126126
{
127-
await markdown.ParseAsync(token);
127+
await markdown.ParseFullAsync(token);
128128
await HtmlWriter.WriteAsync(outputFile, markdown, token);
129129
}
130130
else

src/Elastic.Markdown/IO/ConfigurationFile.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace Elastic.Markdown.IO;
1212

13-
public class ConfigurationFile : DocumentationFile
13+
public record ConfigurationFile : DocumentationFile
1414
{
1515
private readonly IFileInfo _sourceFile;
1616
private readonly IDirectoryInfo _rootPath;

src/Elastic.Markdown/IO/DocumentationFile.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,17 @@
55

66
namespace Elastic.Markdown.IO;
77

8-
public abstract class DocumentationFile(IFileInfo sourceFile, IDirectoryInfo rootPath)
8+
public abstract record DocumentationFile(IFileInfo SourceFile, IDirectoryInfo RootPath)
99
{
10-
public IFileInfo SourceFile { get; } = sourceFile;
11-
public string RelativePath { get; } = Path.GetRelativePath(rootPath.FullName, sourceFile.FullName);
12-
public string RelativeFolder { get; } = Path.GetRelativePath(rootPath.FullName, sourceFile.Directory!.FullName);
13-
14-
public FileInfo OutputFile(IDirectoryInfo outputPath) =>
15-
new(Path.Combine(outputPath.FullName, RelativePath.Replace(".md", ".html")));
10+
public string RelativePath { get; } = Path.GetRelativePath(RootPath.FullName, SourceFile.FullName);
11+
public string RelativeFolder { get; } = Path.GetRelativePath(RootPath.FullName, SourceFile.Directory!.FullName);
1612
}
1713

18-
public class ImageFile(IFileInfo sourceFile, IDirectoryInfo rootPath, string mimeType = "image/png")
19-
: DocumentationFile(sourceFile, rootPath)
20-
{
21-
public string MimeType { get; } = mimeType;
22-
}
14+
public record ImageFile(IFileInfo SourceFile, IDirectoryInfo RootPath, string MimeType = "image/png")
15+
: DocumentationFile(SourceFile, RootPath);
2316

24-
public class StaticFile(IFileInfo sourceFile, IDirectoryInfo rootPath)
25-
: DocumentationFile(sourceFile, rootPath);
17+
public record StaticFile(IFileInfo SourceFile, IDirectoryInfo RootPath)
18+
: DocumentationFile(SourceFile, RootPath);
2619

27-
public class ExcludedFile(IFileInfo sourceFile, IDirectoryInfo rootPath)
28-
: DocumentationFile(sourceFile, rootPath);
20+
public record ExcludedFile(IFileInfo SourceFile, IDirectoryInfo RootPath)
21+
: DocumentationFile(SourceFile, RootPath);

src/Elastic.Markdown/IO/DocumentationFolder.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
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 Markdig.Helpers;
6-
75
namespace Elastic.Markdown.IO;
86

97
public class DocumentationFolder
@@ -75,10 +73,10 @@ public async Task Resolve(Cancel ctx = default)
7573
{
7674
if (_resolved) return;
7775

78-
await Parallel.ForEachAsync(FilesInOrder, ctx, async (file, token) => await file.ParseAsync(token));
76+
await Parallel.ForEachAsync(FilesInOrder, ctx, async (file, token) => await file.MinimalParse(token));
7977
await Parallel.ForEachAsync(GroupsInOrder, ctx, async (group, token) => await group.Resolve(token));
8078

81-
await (Index?.ParseAsync(ctx) ?? Task.CompletedTask);
79+
await (Index?.MinimalParse(ctx) ?? Task.CompletedTask);
8280

8381
_resolved = true;
8482
}

src/Elastic.Markdown/IO/DocumentationSet.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
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-
using System.Globalization;
4+
55
using System.IO.Abstractions;
6-
using System.Text.Json;
76
using Elastic.Markdown.Diagnostics;
87
using Elastic.Markdown.Myst;
98

src/Elastic.Markdown/IO/MarkdownFile.cs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,29 @@
33
// See the LICENSE file in the project root for more information
44
using System.IO.Abstractions;
55
using Elastic.Markdown.Myst;
6-
using Elastic.Markdown.Myst.Directives;
76
using Elastic.Markdown.Slices;
87
using Markdig;
98
using Markdig.Extensions.Yaml;
10-
using Markdig.Helpers;
119
using Markdig.Syntax;
1210
using Slugify;
1311

1412
namespace Elastic.Markdown.IO;
1513

16-
public class MarkdownFile : DocumentationFile
14+
public record MarkdownFile : DocumentationFile
1715
{
1816
private readonly SlugHelper _slugHelper = new();
1917
private string? _navigationTitle;
2018

2119
public MarkdownFile(IFileInfo sourceFile, IDirectoryInfo rootPath, MarkdownParser parser, BuildContext context)
2220
: base(sourceFile, rootPath)
2321
{
24-
ParentFolders = RelativePath.Split(Path.DirectorySeparatorChar).SkipLast(1).ToArray();
2522
FileName = sourceFile.Name;
2623
UrlPathPrefix = context.UrlPathPrefix;
2724
MarkdownParser = parser;
2825
}
2926

3027
public string? UrlPathPrefix { get; }
3128
private MarkdownParser MarkdownParser { get; }
32-
private FrontMatterParser FrontMatterParser { get; } = new();
3329
public YamlFrontMatter? YamlFrontMatter { get; private set; }
3430
public string? Title { get; private set; }
3531
public string? NavigationTitle
@@ -38,16 +34,32 @@ public string? NavigationTitle
3834
private set => _navigationTitle = value;
3935
}
4036

41-
public List<PageTocItem> TableOfContents { get; } = new();
42-
public IReadOnlyList<string> ParentFolders { get; }
37+
private readonly List<PageTocItem> _tableOfContent = new();
38+
public IReadOnlyCollection<PageTocItem> TableOfContents => _tableOfContent;
39+
4340
public string FileName { get; }
4441
public string Url => $"{UrlPathPrefix}/{RelativePath.Replace(".md", ".html")}";
4542

46-
public async Task ParseAsync(Cancel ctx) => await ParseFullAsync(ctx);
43+
private bool _instructionsParsed;
44+
45+
public async Task<MarkdownDocument> MinimalParse(Cancel ctx)
46+
{
47+
var document = await MarkdownParser.MinimalParseAsync(SourceFile, ctx);
48+
ReadDocumentInstructions(document);
49+
return document;
50+
}
4751

4852
public async Task<MarkdownDocument> ParseFullAsync(Cancel ctx)
4953
{
50-
var document = await MarkdownParser.QuickParseAsync(SourceFile, ctx);
54+
if (!_instructionsParsed)
55+
await MinimalParse(ctx);
56+
57+
var document = await MarkdownParser.ParseAsync(SourceFile, YamlFrontMatter, ctx);
58+
return document;
59+
}
60+
61+
private void ReadDocumentInstructions(MarkdownDocument document)
62+
{
5163
if (document.FirstOrDefault() is YamlFrontMatterBlock yaml)
5264
{
5365
var raw = string.Join(Environment.NewLine, yaml.Lines.Lines);
@@ -63,14 +75,20 @@ public async Task<MarkdownDocument> ParseFullAsync(Cancel ctx)
6375
.Where(title => !string.IsNullOrWhiteSpace(title))
6476
.Select(title => new PageTocItem { Heading = title!, Slug = _slugHelper.GenerateSlug(title) })
6577
.ToList();
66-
TableOfContents.Clear();
67-
TableOfContents.AddRange(contents);
68-
return document;
78+
_tableOfContent.Clear();
79+
_tableOfContent.AddRange(contents);
80+
_instructionsParsed = true;
6981
}
7082

71-
public async Task<string> CreateHtmlAsync(YamlFrontMatter? matter, Cancel ctx)
72-
{
73-
var document = await MarkdownParser.ParseAsync(SourceFile, matter, ctx);
74-
return document.ToHtml(MarkdownParser.Pipeline);
75-
}
83+
84+
public string CreateHtml(MarkdownDocument document) =>
85+
// var writer = new StringWriter();
86+
// var renderer = new HtmlRenderer(writer);
87+
// renderer.LinkRewriter = (s => s);
88+
// MarkdownParser.Pipeline.Setup(renderer);
89+
//
90+
// var document = MarkdownParser.Parse(markdown, pipeline);
91+
// renderer.Render(document);
92+
// writer.Flush();
93+
document.ToHtml(MarkdownParser.Pipeline);
7694
}

src/Elastic.Markdown/Myst/FrontMatterParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ public class YamlFrontMatter
2222
public Dictionary<string, string>? Properties { get; set; }
2323
}
2424

25-
public class FrontMatterParser
25+
public static class FrontMatterParser
2626
{
27-
public YamlFrontMatter Deserialize(string yaml)
27+
public static YamlFrontMatter Deserialize(string yaml)
2828
{
2929
var input = new StringReader(yaml);
3030

src/Elastic.Markdown/Myst/MarkdownParser.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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 System.IO.Abstractions;
56
using Cysharp.IO;
67
using Elastic.Markdown.Myst.Comments;
@@ -17,7 +18,13 @@ public class MarkdownParser(IDirectoryInfo sourcePath, BuildContext context)
1718
public IDirectoryInfo SourcePath { get; } = sourcePath;
1819
public BuildContext Context { get; } = context;
1920

20-
public MarkdownPipeline Pipeline =>
21+
public MarkdownPipeline MinimalPipeline { get; } =
22+
new MarkdownPipelineBuilder()
23+
.UseSubstitution()
24+
.UseYamlFrontMatter()
25+
.Build();
26+
27+
public MarkdownPipeline Pipeline { get; } =
2128
new MarkdownPipelineBuilder()
2229
.EnableTrackTrivia()
2330
.UsePreciseSourceLocation()
@@ -30,17 +37,14 @@ public class MarkdownParser(IDirectoryInfo sourcePath, BuildContext context)
3037
.UseGridTables()
3138
.UsePipeTables()
3239
.UseDirectives()
40+
.DisableHtml()
3341
.Build();
3442

3543

36-
// TODO only scan for yaml front matter and toc information
37-
public Task<MarkdownDocument> QuickParseAsync(IFileInfo path, Cancel ctx)
44+
public Task<MarkdownDocument> MinimalParseAsync(IFileInfo path, Cancel ctx)
3845
{
39-
var context = new ParserContext(this, path, null, Context)
40-
{
41-
SkipValidation = true
42-
};
43-
return ParseAsync(path, context, Pipeline, ctx);
46+
var context = new ParserContext(this, path, null, Context) { SkipValidation = true };
47+
return ParseAsync(path, context, MinimalPipeline, ctx);
4448
}
4549

4650
public Task<MarkdownDocument> ParseAsync(IFileInfo path, YamlFrontMatter? matter, Cancel ctx)

src/Elastic.Markdown/Slices/HtmlWriter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ private async Task<string> RenderNavigation(MarkdownFile markdown, Cancel ctx =
4040

4141
public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = default)
4242
{
43-
var html = await markdown.CreateHtmlAsync(markdown.YamlFrontMatter, ctx);
43+
var document = await markdown.ParseFullAsync(ctx);
44+
var html = markdown.CreateHtml(document);
4445
await DocumentationSet.Tree.Resolve(ctx);
4546
var navigationHtml = await RenderNavigation(markdown, ctx);
4647
var slice = Index.Create(new IndexViewModel

src/docs-builder/Http/DocumentationWebHost.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private static async Task<IResult> ServeDocumentationFile(ReloadableGeneratorSta
7070
{
7171
case MarkdownFile markdown:
7272
{
73-
await markdown.ParseAsync(ctx);
73+
await markdown.ParseFullAsync(ctx);
7474
var rendered = await generator.RenderLayout(markdown, ctx);
7575
return Results.Content(rendered, "text/html");
7676
}

0 commit comments

Comments
 (0)