From 8300ed71d6afd0c2f73787910efc2c7feb4bb3cd Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 25 Feb 2025 14:55:21 +0100 Subject: [PATCH 1/8] POC: Get currentl url to link renderer --- .../ProcessorDiagnosticExtensions.cs | 12 ++++++------ .../Myst/CodeBlocks/EnhancedCodeBlock.cs | 2 +- .../Myst/Directives/DirectiveBlock.cs | 2 +- .../Myst/Directives/ImageBlock.cs | 2 +- .../Myst/Directives/IncludeBlock.cs | 6 +++--- .../Myst/Directives/SettingsBlock.cs | 6 +++--- .../InlineParsers/DiagnosticLinkInlineParser.cs | 11 +++++++---- src/Elastic.Markdown/Myst/MarkdownParser.cs | 17 +++++------------ src/Elastic.Markdown/Myst/ParserContext.cs | 16 ++++++++++++---- .../Myst/Renderers/HtmxLinkInlineRenderer.cs | 9 +++++++++ 10 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs b/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs index 2f92b6f2f..577e55146 100644 --- a/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs +++ b/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs @@ -20,7 +20,7 @@ public static void EmitError(this InlineProcessor processor, int line, int colum var d = new Diagnostic { Severity = Severity.Error, - File = processor.GetContext().Path.FullName, + File = processor.GetContext().CurrentPath.FullName, Column = column, Line = line, Message = message, @@ -38,7 +38,7 @@ public static void EmitWarning(this InlineProcessor processor, int line, int col var d = new Diagnostic { Severity = Severity.Warning, - File = processor.GetContext().Path.FullName, + File = processor.GetContext().CurrentPath.FullName, Column = column, Line = line, Message = message, @@ -54,7 +54,7 @@ public static void EmitError(this ParserContext context, string message, Excepti var d = new Diagnostic { Severity = Severity.Error, - File = context.Path.FullName, + File = context.CurrentPath.FullName, Message = message + (e != null ? Environment.NewLine + e : string.Empty), }; context.Build.Collector.Channel.Write(d); @@ -67,7 +67,7 @@ public static void EmitWarning(this ParserContext context, int line, int column, var d = new Diagnostic { Severity = Severity.Warning, - File = context.Path.FullName, + File = context.CurrentPath.FullName, Column = column, Line = line, Message = message, @@ -146,7 +146,7 @@ public static void EmitError(this InlineProcessor processor, LinkInline inline, var d = new Diagnostic { Severity = Severity.Error, - File = processor.GetContext().Path.FullName, + File = processor.GetContext().CurrentPath.FullName, Column = column, Line = line, Message = message, @@ -169,7 +169,7 @@ public static void EmitWarning(this InlineProcessor processor, LinkInline inline var d = new Diagnostic { Severity = Severity.Warning, - File = processor.GetContext().Path.FullName, + File = processor.GetContext().CurrentPath.FullName, Column = column, Line = line, Message = message, diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs index 54971b050..4df649031 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs @@ -21,7 +21,7 @@ public class EnhancedCodeBlock(BlockParser parser, ParserContext context) { public BuildContext Build { get; } = context.Build; - public IFileInfo CurrentFile { get; } = context.Path; + public IFileInfo CurrentFile { get; } = context.CurrentPath; public bool SkipValidation { get; } = context.SkipValidation; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs index ca142dcc8..870f3b6f7 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs @@ -43,7 +43,7 @@ public abstract class DirectiveBlock( public BuildContext Build { get; } = context.Build; - public IFileInfo CurrentFile { get; } = context.Path; + public IFileInfo CurrentFile { get; } = context.CurrentPath; public bool SkipValidation { get; } = context.SkipValidation; diff --git a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs index ed72eb275..d0e9af357 100644 --- a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs @@ -88,7 +88,7 @@ private void ExtractImageUrl(ParserContext context) return; } - var includeFrom = context.Path.Directory!.FullName; + var includeFrom = context.CurrentPath.Directory!.FullName; if (imageUrl.StartsWith('/')) includeFrom = context.Parser.SourcePath.FullName; diff --git a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs index da258b790..20541577b 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -22,7 +22,7 @@ public class IncludeBlock(DirectiveBlockParser parser, ParserContext context) : { public override string Directive => "include"; - public Func? GetDocumentationFile { get; } = context.GetDocumentationFile; + public Func GetDocumentationFile { get; } = context.GetDocumentationFile; public ConfigurationFile Configuration { get; } = context.Configuration; @@ -65,7 +65,7 @@ private void ExtractInclusionPath(ParserContext context) return; } - var includeFrom = context.Path.Directory!.FullName; + var includeFrom = context.CurrentPath.Directory!.FullName; if (includePath.StartsWith('/')) includeFrom = DocumentationSourcePath.FullName; @@ -89,7 +89,7 @@ private void ExtractInclusionPath(ParserContext context) Found = false; } - if (file.FullName == context.Path.FullName) + if (file.FullName == context.CurrentPath.FullName) { this.EmitError($"{{include}} cyclical include detected `{IncludePath}` points to itself"); Found = false; diff --git a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs index e659b323e..7aabce523 100644 --- a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs @@ -14,7 +14,7 @@ public class SettingsBlock(DirectiveBlockParser parser, ParserContext context) : { public override string Directive => "settings"; - public Func? GetDocumentationFile { get; } = context.GetDocumentationFile; + public Func GetDocumentationFile { get; } = context.GetDocumentationFile; public ConfigurationFile Configuration { get; } = context.Configuration; @@ -22,7 +22,7 @@ public class SettingsBlock(DirectiveBlockParser parser, ParserContext context) : public IFileSystem FileSystem { get; } = context.Build.ReadFileSystem; - public IFileInfo IncludeFrom { get; } = context.Path; + public IFileInfo IncludeFrom { get; } = context.CurrentPath; public IDirectoryInfo DocumentationSourcePath { get; } = context.Parser.SourcePath; @@ -46,7 +46,7 @@ private void ExtractInclusionPath(ParserContext context) return; } - var includeFrom = context.Path.Directory!.FullName; + var includeFrom = context.CurrentPath.Directory!.FullName; if (includePath.StartsWith('/')) includeFrom = DocumentationSourcePath.FullName; diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 881f27cbc..b32b90406 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -55,6 +55,9 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; var context = processor.GetContext(); + if (context.CurrentUrlPath is not null) + link.SetData(nameof(context.CurrentUrlPath), context.CurrentUrlPath); + if (IsInCommentBlock(link) || context.SkipValidation) return match; @@ -183,7 +186,7 @@ private static (string url, string? anchor) SplitUrlAndAnchor(string fullUrl) private static string GetIncludeFromPath(string url, ParserContext context) => url.StartsWith('/') ? context.Parser.SourcePath.FullName - : context.Path.Directory!.FullName; + : context.CurrentPath.Directory!.FullName; private static void ValidateInternalUrl(InlineProcessor processor, string url, string includeFrom, LinkInline link, ParserContext context) { @@ -200,7 +203,7 @@ private static void ProcessLinkText(InlineProcessor processor, LinkInline link, if (link.FirstChild != null && string.IsNullOrEmpty(anchor)) return; - var markdown = context.GetDocumentationFile?.Invoke(file) as MarkdownFile; + var markdown = context.GetDocumentationFile(file) as MarkdownFile; if (markdown == null && link.FirstChild == null) { @@ -225,10 +228,10 @@ private static void ProcessLinkText(InlineProcessor processor, LinkInline link, private static IFileInfo ResolveFile(ParserContext context, string url) => string.IsNullOrWhiteSpace(url) - ? context.Path + ? context.CurrentPath : url.StartsWith('/') ? context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Build.SourcePath.FullName, url.TrimStart('/'))) - : context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Path.Directory!.FullName, url)); + : context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.CurrentPath.Directory!.FullName, url)); private static void ValidateAnchor(InlineProcessor processor, MarkdownFile markdown, string anchor, LinkInline link) { diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index fbc257b91..73590f75c 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -24,7 +24,7 @@ namespace Elastic.Markdown.Myst; public class MarkdownParser( IDirectoryInfo sourcePath, BuildContext context, - Func? getDocumentationFile, + Func getDocumentationFile, ConfigurationFile configuration, ICrossLinkResolver linksResolver ) @@ -94,20 +94,16 @@ public static MarkdownPipeline Pipeline public Task MinimalParseAsync(IFileInfo path, Cancel ctx) { - var context = new ParserContext(this, path, null, Context, Configuration, LinksResolver) + var context = new ParserContext(this, path, null, Context, Configuration, LinksResolver, getDocumentationFile) { - SkipValidation = true, - GetDocumentationFile = getDocumentationFile + SkipValidation = true }; return ParseAsync(path, context, MinimalPipeline, ctx); } public Task ParseAsync(IFileInfo path, YamlFrontMatter? matter, Cancel ctx) { - var context = new ParserContext(this, path, matter, Context, Configuration, LinksResolver) - { - GetDocumentationFile = getDocumentationFile - }; + var context = new ParserContext(this, path, matter, Context, Configuration, LinksResolver, getDocumentationFile); return ParseAsync(path, context, Pipeline, ctx); } @@ -135,10 +131,7 @@ private static async Task ParseAsync( public MarkdownDocument Parse(string yaml, IFileInfo parent, YamlFrontMatter? matter) { - var context = new ParserContext(this, parent, matter, Context, Configuration, LinksResolver) - { - GetDocumentationFile = getDocumentationFile - }; + var context = new ParserContext(this, parent, matter, Context, Configuration, LinksResolver, getDocumentationFile); var markdownDocument = Markdig.Markdown.Parse(yaml, Pipeline, context); return markdownDocument; } diff --git a/src/Elastic.Markdown/Myst/ParserContext.cs b/src/Elastic.Markdown/Myst/ParserContext.cs index 313c64aa0..de825deba 100644 --- a/src/Elastic.Markdown/Myst/ParserContext.cs +++ b/src/Elastic.Markdown/Myst/ParserContext.cs @@ -32,14 +32,20 @@ public ParserContext( YamlFrontMatter? frontMatter, BuildContext context, ConfigurationFile configuration, - ICrossLinkResolver linksResolver) + ICrossLinkResolver linksResolver, + Func getDocumentationFile + ) { Parser = markdownParser; - Path = path; FrontMatter = frontMatter; Build = context; Configuration = configuration; LinksResolver = linksResolver; + CurrentPath = path; + CurrentPathRelative = Path.GetRelativePath(markdownParser.SourcePath.FullName, CurrentPath.FullName); + GetDocumentationFile = getDocumentationFile; + if (getDocumentationFile(CurrentPath) is MarkdownFile md) + CurrentUrlPath = md.Url; if (frontMatter?.Properties is not { Count: > 0 }) Substitutions = configuration.Substitutions; @@ -69,11 +75,13 @@ public ParserContext( public ConfigurationFile Configuration { get; } public ICrossLinkResolver LinksResolver { get; } public MarkdownParser Parser { get; } - public IFileInfo Path { get; } + public IFileInfo CurrentPath { get; } + public string CurrentPathRelative { get; } + public string? CurrentUrlPath { get; } public YamlFrontMatter? FrontMatter { get; } public BuildContext Build { get; } public bool SkipValidation { get; init; } - public Func? GetDocumentationFile { get; init; } + public Func GetDocumentationFile { get; init; } public IReadOnlyDictionary Substitutions { get; } public IReadOnlyDictionary ContextSubstitutions { get; } diff --git a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs index fd9280d78..4b3099054 100644 --- a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs +++ b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs @@ -16,6 +16,15 @@ protected override void Write(HtmlRenderer renderer, LinkInline link) { if (renderer.EnableHtmlForInline && !link.IsImage && link.Url?.StartsWith('/') == true) { + // ReSharper disable once UnusedVariable + if (link.GetData(nameof(ParserContext.CurrentUrlPath)) is not string currentUrl) + { + + } + else + { + + } _ = renderer.Write(" Date: Wed, 26 Feb 2025 10:13:40 +0100 Subject: [PATCH 2/8] Ensure CurrentUrl is set correctly for snippets too --- .../Myst/Directives/DirectiveHtmlRenderer.cs | 9 ++++----- src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs | 3 +++ .../Myst/InlineParsers/DiagnosticLinkInlineParser.cs | 3 +-- src/Elastic.Markdown/Myst/MarkdownParser.cs | 6 ++++++ src/Elastic.Markdown/Myst/ParserContext.cs | 11 +++++++---- .../Myst/Renderers/HtmxLinkInlineRenderer.cs | 9 +++------ 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index ae2a2315b..f09c4a8cf 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -224,11 +224,10 @@ private static void WriteIncludeBlock(HtmlRenderer renderer, IncludeBlock block) if (!block.Found || block.IncludePath is null) return; - var parser = new MarkdownParser( - block.DocumentationSourcePath, block.Build, block.GetDocumentationFile, - block.Configuration, block.LinksResolver); - var file = block.FileSystem.FileInfo.New(block.IncludePath); - var document = parser.ParseAsync(file, block.FrontMatter, default).GetAwaiter().GetResult(); + var parser = new MarkdownParser(block.DocumentationSourcePath, block.Build, block.GetDocumentationFile, block.Configuration, block.LinksResolver); + var snippet = block.FileSystem.FileInfo.New(block.IncludePath); + var parentPath = block.ParentMarkdownFile!; + var document = parser.ParseSnippetAsync(snippet, parentPath, block.FrontMatter, default).GetAwaiter().GetResult(); var html = document.ToHtml(MarkdownParser.Pipeline); _ = renderer.Write(html); } diff --git a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs index 20541577b..94f43b39e 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -34,6 +34,7 @@ public class IncludeBlock(DirectiveBlockParser parser, ParserContext context) : public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter; + public IFileInfo? ParentMarkdownFile { get; private set; } public string? IncludePath { get; private set; } public string? IncludePathFromSourceDirectory { get; private set; } @@ -58,6 +59,8 @@ public override void FinalizeAndValidate(ParserContext context) private void ExtractInclusionPath(ParserContext context) { + ParentMarkdownFile = context.CurrentPath; + var includePath = Arguments; if (string.IsNullOrWhiteSpace(includePath)) { diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index b32b90406..45e0d2693 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -55,8 +55,7 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; var context = processor.GetContext(); - if (context.CurrentUrlPath is not null) - link.SetData(nameof(context.CurrentUrlPath), context.CurrentUrlPath); + link.SetData(nameof(context.CurrentUrlPath), context.CurrentUrlPath); if (IsInCommentBlock(link) || context.SkipValidation) return match; diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index 73590f75c..6f6d00733 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -107,6 +107,12 @@ public Task ParseAsync(IFileInfo path, YamlFrontMatter? matter return ParseAsync(path, context, Pipeline, ctx); } + public Task ParseSnippetAsync(IFileInfo path, IFileInfo parentPath, YamlFrontMatter? matter, Cancel ctx) + { + var context = new ParserContext(this, path, matter, Context, Configuration, LinksResolver, getDocumentationFile, parentPath); + return ParseAsync(path, context, Pipeline, ctx); + } + private static async Task ParseAsync( IFileInfo path, MarkdownParserContext context, diff --git a/src/Elastic.Markdown/Myst/ParserContext.cs b/src/Elastic.Markdown/Myst/ParserContext.cs index de825deba..ed7a81171 100644 --- a/src/Elastic.Markdown/Myst/ParserContext.cs +++ b/src/Elastic.Markdown/Myst/ParserContext.cs @@ -33,7 +33,8 @@ public ParserContext( BuildContext context, ConfigurationFile configuration, ICrossLinkResolver linksResolver, - Func getDocumentationFile + Func getDocumentationFile, + IFileInfo? parentPath = null ) { Parser = markdownParser; @@ -44,8 +45,10 @@ public ParserContext( CurrentPath = path; CurrentPathRelative = Path.GetRelativePath(markdownParser.SourcePath.FullName, CurrentPath.FullName); GetDocumentationFile = getDocumentationFile; - if (getDocumentationFile(CurrentPath) is MarkdownFile md) - CurrentUrlPath = md.Url; + + CurrentUrlPath = getDocumentationFile(parentPath ?? CurrentPath) is MarkdownFile md + ? md.Url + : throw new Exception($"Unable to find documentation file for {(parentPath ?? CurrentPath).FullName}"); if (frontMatter?.Properties is not { Count: > 0 }) Substitutions = configuration.Substitutions; @@ -77,7 +80,7 @@ public ParserContext( public MarkdownParser Parser { get; } public IFileInfo CurrentPath { get; } public string CurrentPathRelative { get; } - public string? CurrentUrlPath { get; } + public string CurrentUrlPath { get; } public YamlFrontMatter? FrontMatter { get; } public BuildContext Build { get; } public bool SkipValidation { get; init; } diff --git a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs index 4b3099054..094bd432b 100644 --- a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs +++ b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs @@ -19,12 +19,10 @@ protected override void Write(HtmlRenderer renderer, LinkInline link) // ReSharper disable once UnusedVariable if (link.GetData(nameof(ParserContext.CurrentUrlPath)) is not string currentUrl) { - + base.Write(renderer, link); + return; } - else - { - } _ = renderer.Write(""); } else - { base.Write(renderer, link); - } } } From 58ade462d373a47042066afff4feb8c0a7324336 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 26 Feb 2025 10:56:06 +0100 Subject: [PATCH 3/8] Refactor arguments to parsercontext to state object --- .editorconfig | 2 + src/Elastic.Markdown/BuildContext.cs | 16 ++-- src/Elastic.Markdown/IO/DocumentationSet.cs | 4 +- .../Myst/Directives/DirectiveHtmlRenderer.cs | 6 +- .../Myst/Directives/ImageBlock.cs | 2 +- .../Myst/Directives/IncludeBlock.cs | 6 +- .../Myst/Directives/SettingsBlock.cs | 4 +- .../DiagnosticLinkInlineParser.cs | 4 +- src/Elastic.Markdown/Myst/MarkdownParser.cs | 54 +++++++++--- src/Elastic.Markdown/Myst/ParserContext.cs | 88 ++++++++++--------- src/docs-builder/Http/DocumentationWebHost.cs | 6 +- 11 files changed, 114 insertions(+), 78 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0e45b376c..420311719 100644 --- a/.editorconfig +++ b/.editorconfig @@ -168,6 +168,7 @@ csharp_new_line_before_else = true csharp_new_line_before_catch = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = true + # just a suggestion do to our JSON tests that use anonymous types to # represent json quite a bit (makes copy paste easier). csharp_new_line_before_members_in_anonymous_types = true @@ -184,6 +185,7 @@ csharp_space_between_method_call_parameter_list_parentheses = false #Wrap csharp_preserve_single_line_statements = false csharp_preserve_single_line_blocks = true +resharper_wrap_object_and_collection_initializer_style = chop_always # Resharper resharper_csharp_braces_for_lock=required_for_multiline diff --git a/src/Elastic.Markdown/BuildContext.cs b/src/Elastic.Markdown/BuildContext.cs index 74b4788c6..2983097d4 100644 --- a/src/Elastic.Markdown/BuildContext.cs +++ b/src/Elastic.Markdown/BuildContext.cs @@ -15,8 +15,8 @@ public record BuildContext public IFileSystem ReadFileSystem { get; } public IFileSystem WriteFileSystem { get; } - public IDirectoryInfo SourcePath { get; } - public IDirectoryInfo OutputPath { get; } + public IDirectoryInfo DocumentationSourceDirectory { get; } + public IDirectoryInfo DocumentationOutputDirectory { get; } public IFileInfo ConfigurationPath { get; } @@ -56,17 +56,17 @@ public BuildContext(DiagnosticsCollector collector, IFileSystem readFileSystem, ? ReadFileSystem.DirectoryInfo.New(source) : ReadFileSystem.DirectoryInfo.New(Path.Combine(Paths.Root.FullName)); - (SourcePath, ConfigurationPath) = FindDocsFolderFromRoot(rootFolder); + (DocumentationSourceDirectory, ConfigurationPath) = FindDocsFolderFromRoot(rootFolder); - OutputPath = !string.IsNullOrWhiteSpace(output) + DocumentationOutputDirectory = !string.IsNullOrWhiteSpace(output) ? WriteFileSystem.DirectoryInfo.New(output) : WriteFileSystem.DirectoryInfo.New(Path.Combine(Paths.Root.FullName, ".artifacts/docs/html")); - if (ConfigurationPath.FullName != SourcePath.FullName) - SourcePath = ConfigurationPath.Directory!; + if (ConfigurationPath.FullName != DocumentationSourceDirectory.FullName) + DocumentationSourceDirectory = ConfigurationPath.Directory!; - Git = GitCheckoutInformation.Create(SourcePath, ReadFileSystem); - Configuration = new ConfigurationFile(ConfigurationPath, SourcePath, this); + Git = GitCheckoutInformation.Create(DocumentationSourceDirectory, ReadFileSystem); + Configuration = new ConfigurationFile(ConfigurationPath, DocumentationSourceDirectory, this); } public ConfigurationFile Configuration { get; set; } diff --git a/src/Elastic.Markdown/IO/DocumentationSet.cs b/src/Elastic.Markdown/IO/DocumentationSet.cs index 1ee5e863d..bec1b5d73 100644 --- a/src/Elastic.Markdown/IO/DocumentationSet.cs +++ b/src/Elastic.Markdown/IO/DocumentationSet.cs @@ -37,8 +37,8 @@ public class DocumentationSet public DocumentationSet(BuildContext context, ILoggerFactory logger, ICrossLinkResolver? linkResolver = null) { Context = context; - SourcePath = context.SourcePath; - OutputPath = context.OutputPath; + SourcePath = context.DocumentationSourceDirectory; + OutputPath = context.DocumentationOutputDirectory; RelativeSourcePath = Path.GetRelativePath(Paths.Root.FullName, SourcePath.FullName); LinkResolver = linkResolver ?? new CrossLinkResolver(new ConfigurationCrossLinkFetcher(context.Configuration, logger)); diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index f09c4a8cf..f36095d76 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -224,7 +224,7 @@ private static void WriteIncludeBlock(HtmlRenderer renderer, IncludeBlock block) if (!block.Found || block.IncludePath is null) return; - var parser = new MarkdownParser(block.DocumentationSourcePath, block.Build, block.GetDocumentationFile, block.Configuration, block.LinksResolver); + var parser = new MarkdownParser(block.DocumentationSourceDirectory, block.Build, block.GetDocumentationFile, block.Configuration, block.LinksResolver); var snippet = block.FileSystem.FileInfo.New(block.IncludePath); var parentPath = block.ParentMarkdownFile!; var document = parser.ParseSnippetAsync(snippet, parentPath, block.FrontMatter, default).GetAwaiter().GetResult(); @@ -238,7 +238,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc return; var parser = new MarkdownParser( - block.DocumentationSourcePath, block.Build, block.GetDocumentationFile, block.Configuration + block.DocumentationSourceDirectory, block.Build, block.GetDocumentationFile, block.Configuration , block.LinksResolver ); @@ -266,7 +266,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc SettingsCollection = settings, RenderMarkdown = s => { - var document = parser.Parse(s, block.IncludeFrom, block.FrontMatter); + var document = parser.ParseEmbeddedMarkdown(s, block.IncludeFrom, block.FrontMatter); var html = document.ToHtml(MarkdownParser.Pipeline); return html; } diff --git a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs index d0e9af357..73670ddb9 100644 --- a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs @@ -90,7 +90,7 @@ private void ExtractImageUrl(ParserContext context) var includeFrom = context.CurrentPath.Directory!.FullName; if (imageUrl.StartsWith('/')) - includeFrom = context.Parser.SourcePath.FullName; + includeFrom = context.Build.DocumentationSourceDirectory.FullName; ImageUrl = imageUrl; var imagePath = Path.Combine(includeFrom, imageUrl.TrimStart('/')); diff --git a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs index 94f43b39e..0f83c35ee 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -30,7 +30,7 @@ public class IncludeBlock(DirectiveBlockParser parser, ParserContext context) : public IFileSystem FileSystem { get; } = context.Build.ReadFileSystem; - public IDirectoryInfo DocumentationSourcePath { get; } = context.Parser.SourcePath; + public IDirectoryInfo DocumentationSourceDirectory { get; } = context.Build.DocumentationSourceDirectory; public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter; @@ -70,10 +70,10 @@ private void ExtractInclusionPath(ParserContext context) var includeFrom = context.CurrentPath.Directory!.FullName; if (includePath.StartsWith('/')) - includeFrom = DocumentationSourcePath.FullName; + includeFrom = DocumentationSourceDirectory.FullName; IncludePath = Path.Combine(includeFrom, includePath.TrimStart('/')); - IncludePathFromSourceDirectory = Path.GetRelativePath(DocumentationSourcePath.FullName, IncludePath); + IncludePathFromSourceDirectory = Path.GetRelativePath(DocumentationSourceDirectory.FullName, IncludePath); if (FileSystem.File.Exists(IncludePath)) Found = true; else diff --git a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs index 7aabce523..2325a85f3 100644 --- a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs @@ -24,7 +24,7 @@ public class SettingsBlock(DirectiveBlockParser parser, ParserContext context) : public IFileInfo IncludeFrom { get; } = context.CurrentPath; - public IDirectoryInfo DocumentationSourcePath { get; } = context.Parser.SourcePath; + public IDirectoryInfo DocumentationSourceDirectory { get; } = context.Build.DocumentationSourceDirectory; public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter; @@ -48,7 +48,7 @@ private void ExtractInclusionPath(ParserContext context) var includeFrom = context.CurrentPath.Directory!.FullName; if (includePath.StartsWith('/')) - includeFrom = DocumentationSourcePath.FullName; + includeFrom = DocumentationSourceDirectory.FullName; IncludePath = Path.Combine(includeFrom, includePath.TrimStart('/')); if (FileSystem.File.Exists(IncludePath)) diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 45e0d2693..f0ca95ebb 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -184,7 +184,7 @@ private static (string url, string? anchor) SplitUrlAndAnchor(string fullUrl) private static string GetIncludeFromPath(string url, ParserContext context) => url.StartsWith('/') - ? context.Parser.SourcePath.FullName + ? context.Build.DocumentationSourceDirectory.FullName : context.CurrentPath.Directory!.FullName; private static void ValidateInternalUrl(InlineProcessor processor, string url, string includeFrom, LinkInline link, ParserContext context) @@ -229,7 +229,7 @@ private static IFileInfo ResolveFile(ParserContext context, string url) => string.IsNullOrWhiteSpace(url) ? context.CurrentPath : url.StartsWith('/') - ? context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Build.SourcePath.FullName, url.TrimStart('/'))) + ? context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Build.DocumentationSourceDirectory.FullName, url.TrimStart('/'))) : context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.CurrentPath.Directory!.FullName, url)); private static void ValidateAnchor(InlineProcessor processor, MarkdownFile markdown, string anchor, LinkInline link) diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index 6f6d00733..736773e0d 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -23,15 +23,15 @@ namespace Elastic.Markdown.Myst; public class MarkdownParser( IDirectoryInfo sourcePath, - BuildContext context, + BuildContext build, Func getDocumentationFile, ConfigurationFile configuration, ICrossLinkResolver linksResolver - ) +) { public IDirectoryInfo SourcePath { get; } = sourcePath; - private BuildContext Context { get; } = context; + private BuildContext Build { get; } = build; private ICrossLinkResolver LinksResolver { get; } = linksResolver; @@ -53,12 +53,12 @@ private static MarkdownPipeline MinimalPipeline _ = builder.BlockParsers.TryRemove(); MinimalPipelineCached = builder.Build(); return MinimalPipelineCached; - } } // ReSharper disable once InconsistentNaming private static MarkdownPipeline? PipelineCached; + public static MarkdownPipeline Pipeline { get @@ -94,25 +94,59 @@ public static MarkdownPipeline Pipeline public Task MinimalParseAsync(IFileInfo path, Cancel ctx) { - var context = new ParserContext(this, path, null, Context, Configuration, LinksResolver, getDocumentationFile) + var state = new ParserState(Build) { + SourcePath = path, + YamlFrontMatter = null, + DocumentationFileLookup = getDocumentationFile, + CrossLinkResolver = LinksResolver, SkipValidation = true }; + var context = new ParserContext(state); return ParseAsync(path, context, MinimalPipeline, ctx); } public Task ParseAsync(IFileInfo path, YamlFrontMatter? matter, Cancel ctx) { - var context = new ParserContext(this, path, matter, Context, Configuration, LinksResolver, getDocumentationFile); + var state = new ParserState(Build) + { + SourcePath = path, + YamlFrontMatter = matter, + DocumentationFileLookup = getDocumentationFile, + CrossLinkResolver = LinksResolver + }; + var context = new ParserContext(state); return ParseAsync(path, context, Pipeline, ctx); } public Task ParseSnippetAsync(IFileInfo path, IFileInfo parentPath, YamlFrontMatter? matter, Cancel ctx) { - var context = new ParserContext(this, path, matter, Context, Configuration, LinksResolver, getDocumentationFile, parentPath); + var state = new ParserState(Build) + { + SourcePath = path, + YamlFrontMatter = matter, + DocumentationFileLookup = getDocumentationFile, + CrossLinkResolver = LinksResolver, + ParentMarkdownPath = parentPath + }; + var context = new ParserContext(state); return ParseAsync(path, context, Pipeline, ctx); } + public MarkdownDocument ParseEmbeddedMarkdown(string markdown, IFileInfo path, YamlFrontMatter? matter) + { + var state = new ParserState(Build) + { + SourcePath = path, + YamlFrontMatter = matter, + DocumentationFileLookup = getDocumentationFile, + CrossLinkResolver = LinksResolver + }; + var context = new ParserContext(state); + var markdownDocument = Markdig.Markdown.Parse(markdown, Pipeline, context); + return markdownDocument; + } + private static async Task ParseAsync( IFileInfo path, MarkdownParserContext context, @@ -135,10 +169,4 @@ private static async Task ParseAsync( } } - public MarkdownDocument Parse(string yaml, IFileInfo parent, YamlFrontMatter? matter) - { - var context = new ParserContext(this, parent, matter, Context, Configuration, LinksResolver, getDocumentationFile); - var markdownDocument = Markdig.Markdown.Parse(yaml, Pipeline, context); - return markdownDocument; - } } diff --git a/src/Elastic.Markdown/Myst/ParserContext.cs b/src/Elastic.Markdown/Myst/ParserContext.cs index ed7a81171..c182a92f8 100644 --- a/src/Elastic.Markdown/Myst/ParserContext.cs +++ b/src/Elastic.Markdown/Myst/ParserContext.cs @@ -24,41 +24,61 @@ processor.Context as ParserContext ?? throw new InvalidOperationException($"Provided context is not a {nameof(ParserContext)}"); } +public record ParserState(BuildContext Build) +{ + public ConfigurationFile Configuration { get; } = Build.Configuration; + + public required IFileInfo SourcePath { get; init; } + public required YamlFrontMatter? YamlFrontMatter { get; init; } + public required ICrossLinkResolver CrossLinkResolver { get; init; } + public required Func DocumentationFileLookup { get; init; } + + public IFileInfo? ParentMarkdownPath { get; set; } + public bool SkipValidation { get; set; } +} + public class ParserContext : MarkdownParserContext { - public ParserContext( - MarkdownParser markdownParser, - IFileInfo path, - YamlFrontMatter? frontMatter, - BuildContext context, - ConfigurationFile configuration, - ICrossLinkResolver linksResolver, - Func getDocumentationFile, - IFileInfo? parentPath = null - ) + public ConfigurationFile Configuration { get; } + public ICrossLinkResolver LinksResolver { get; } + public IFileInfo CurrentPath { get; } + public string CurrentPathRelative { get; } + public string CurrentUrlPath { get; } + public YamlFrontMatter? FrontMatter { get; } + public BuildContext Build { get; } + public bool SkipValidation { get; } + public Func GetDocumentationFile { get; } + public IReadOnlyDictionary Substitutions { get; } + public IReadOnlyDictionary ContextSubstitutions { get; } + + public ParserContext(ParserState state) { - Parser = markdownParser; - FrontMatter = frontMatter; - Build = context; - Configuration = configuration; - LinksResolver = linksResolver; - CurrentPath = path; - CurrentPathRelative = Path.GetRelativePath(markdownParser.SourcePath.FullName, CurrentPath.FullName); - GetDocumentationFile = getDocumentationFile; - - CurrentUrlPath = getDocumentationFile(parentPath ?? CurrentPath) is MarkdownFile md + Build = state.Build; + Configuration = state.Configuration; + FrontMatter = state.YamlFrontMatter; + SkipValidation = state.SkipValidation; + + LinksResolver = state.CrossLinkResolver; + CurrentPath = state.SourcePath; + CurrentPathRelative = Path.GetRelativePath(Build.DocumentationSourceDirectory.FullName, CurrentPath.FullName); + GetDocumentationFile = state.DocumentationFileLookup; + var parentPath = state.ParentMarkdownPath; + + CurrentUrlPath = GetDocumentationFile(parentPath ?? CurrentPath) is MarkdownFile md ? md.Url - : throw new Exception($"Unable to find documentation file for {(parentPath ?? CurrentPath).FullName}"); + : SkipValidation + ? string.Empty + : throw new Exception($"Unable to find documentation file for {(parentPath ?? CurrentPath).FullName}"); - if (frontMatter?.Properties is not { Count: > 0 }) - Substitutions = configuration.Substitutions; + if (FrontMatter?.Properties is not { Count: > 0 }) + Substitutions = Configuration.Substitutions; else { - var subs = new Dictionary(configuration.Substitutions); - foreach (var (k, value) in frontMatter.Properties) + var subs = new Dictionary(Configuration.Substitutions); + foreach (var (k, value) in FrontMatter.Properties) { var key = k.ToLowerInvariant(); - if (configuration.Substitutions.TryGetValue(key, out _)) + if (Configuration.Substitutions.TryGetValue(key, out _)) this.EmitError($"{{{key}}} can not be redeclared in front matter as its a global substitution"); else subs[key] = value; @@ -69,23 +89,9 @@ public ParserContext( var contextSubs = new Dictionary(); - if (frontMatter?.Title is { } title) + if (FrontMatter?.Title is { } title) contextSubs["context.page_title"] = title; ContextSubstitutions = contextSubs; } - - public ConfigurationFile Configuration { get; } - public ICrossLinkResolver LinksResolver { get; } - public MarkdownParser Parser { get; } - public IFileInfo CurrentPath { get; } - public string CurrentPathRelative { get; } - public string CurrentUrlPath { get; } - public YamlFrontMatter? FrontMatter { get; } - public BuildContext Build { get; } - public bool SkipValidation { get; init; } - public Func GetDocumentationFile { get; init; } - public IReadOnlyDictionary Substitutions { get; } - public IReadOnlyDictionary ContextSubstitutions { get; } - } diff --git a/src/docs-builder/Http/DocumentationWebHost.cs b/src/docs-builder/Http/DocumentationWebHost.cs index 49d273e86..eacec3eac 100644 --- a/src/docs-builder/Http/DocumentationWebHost.cs +++ b/src/docs-builder/Http/DocumentationWebHost.cs @@ -41,11 +41,11 @@ public DocumentationWebHost(string? path, int port, ILoggerFactory logger, IFile _ = builder.Services .AddAotLiveReload(s => { - s.FolderToMonitor = _context.SourcePath.FullName; + s.FolderToMonitor = _context.DocumentationSourceDirectory.FullName; s.ClientFileExtensions = ".md,.yml"; }) .AddSingleton(_ => - new ReloadableGeneratorState(_context.SourcePath, null, _context, logger) + new ReloadableGeneratorState(_context.DocumentationSourceDirectory, null, _context, logger) ) .AddHostedService(); @@ -133,7 +133,7 @@ public sealed class EmbeddedOrPhysicalFileProvider : IFileProvider, IDisposable public EmbeddedOrPhysicalFileProvider(BuildContext context) { - var documentationStaticFiles = Path.Combine(context.SourcePath.FullName, "_static"); + var documentationStaticFiles = Path.Combine(context.DocumentationSourceDirectory.FullName, "_static"); #if DEBUG // this attempts to serve files directly from their source rather than the embedded resources during development. // this allows us to change js/css files without restarting the webserver From 730b4961c8a0798a7deb95c385bbd1e94bafe4eb Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 26 Feb 2025 11:13:43 +0100 Subject: [PATCH 4/8] remove interim properties on directives, use context or build directly --- src/Elastic.Markdown/IO/MarkdownFile.cs | 6 ++-- .../Myst/Directives/DirectiveHtmlRenderer.cs | 26 ++++++++++------- .../Myst/Directives/IncludeBlock.cs | 28 ++++++------------- .../Myst/Directives/SettingsBlock.cs | 22 +++------------ src/Elastic.Markdown/Myst/ParserContext.cs | 2 -- 5 files changed, 32 insertions(+), 52 deletions(-) diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 5bac78c60..78ad37f4f 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -230,9 +230,9 @@ public static List GetAnchors( .Where(i => i.Found) .Select(i => { - var path = i.IncludePathFromSourceDirectory; - if (path is null - || !set.FlatMappedFiles.TryGetValue(path, out var file) + var relativePath = i.IncludePathRelativeToSource; + if (relativePath is null + || !set.FlatMappedFiles.TryGetValue(relativePath, out var file) || file is not SnippetFile snippet) return null; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index f36095d76..39279e648 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -202,8 +202,8 @@ private static void WriteLiteralIncludeBlock(HtmlRenderer renderer, IncludeBlock if (!block.Found || block.IncludePath is null) return; - var file = block.FileSystem.FileInfo.New(block.IncludePath); - var content = block.FileSystem.File.ReadAllText(file.FullName); + var file = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); + var content = block.Build.ReadFileSystem.File.ReadAllText(file.FullName); if (string.IsNullOrEmpty(block.Language)) _ = renderer.Write(content); else @@ -224,10 +224,16 @@ private static void WriteIncludeBlock(HtmlRenderer renderer, IncludeBlock block) if (!block.Found || block.IncludePath is null) return; - var parser = new MarkdownParser(block.DocumentationSourceDirectory, block.Build, block.GetDocumentationFile, block.Configuration, block.LinksResolver); - var snippet = block.FileSystem.FileInfo.New(block.IncludePath); - var parentPath = block.ParentMarkdownFile!; - var document = parser.ParseSnippetAsync(snippet, parentPath, block.FrontMatter, default).GetAwaiter().GetResult(); + var parser = new MarkdownParser( + block.Build.DocumentationSourceDirectory, + block.Build, + block.Context.GetDocumentationFile, + block.Build.Configuration, + block.Context.LinksResolver + ); + var snippet = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); + var parentPath = block.Context.CurrentPath; + var document = parser.ParseSnippetAsync(snippet, parentPath, block.Context.FrontMatter, default).GetAwaiter().GetResult(); var html = document.ToHtml(MarkdownParser.Pipeline); _ = renderer.Write(html); } @@ -238,11 +244,11 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc return; var parser = new MarkdownParser( - block.DocumentationSourceDirectory, block.Build, block.GetDocumentationFile, block.Configuration - , block.LinksResolver + block.Build.DocumentationSourceDirectory, block.Build, block.Context.GetDocumentationFile, block.Build.Configuration + , block.Context.LinksResolver ); - var file = block.FileSystem.FileInfo.New(block.IncludePath); + var file = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); YamlSettings? settings; try @@ -266,7 +272,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc SettingsCollection = settings, RenderMarkdown = s => { - var document = parser.ParseEmbeddedMarkdown(s, block.IncludeFrom, block.FrontMatter); + var document = parser.ParseEmbeddedMarkdown(s, block.IncludeFrom, block.Context.FrontMatter); var html = document.ToHtml(MarkdownParser.Pipeline); return html; } diff --git a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs index 0f83c35ee..740f94b2e 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -1,6 +1,7 @@ // 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 System.IO.Abstractions; using Elastic.Markdown.CrossLinks; using Elastic.Markdown.Diagnostics; @@ -22,21 +23,11 @@ public class IncludeBlock(DirectiveBlockParser parser, ParserContext context) : { public override string Directive => "include"; - public Func GetDocumentationFile { get; } = context.GetDocumentationFile; - - public ConfigurationFile Configuration { get; } = context.Configuration; - - public ICrossLinkResolver LinksResolver { get; } = context.LinksResolver; - - public IFileSystem FileSystem { get; } = context.Build.ReadFileSystem; - - public IDirectoryInfo DocumentationSourceDirectory { get; } = context.Build.DocumentationSourceDirectory; + public ParserContext Context { get; } = context; - public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter; - - public IFileInfo? ParentMarkdownFile { get; private set; } public string? IncludePath { get; private set; } - public string? IncludePathFromSourceDirectory { get; private set; } + + public string? IncludePathRelativeToSource { get; private set; } public bool Found { get; private set; } @@ -59,8 +50,6 @@ public override void FinalizeAndValidate(ParserContext context) private void ExtractInclusionPath(ParserContext context) { - ParentMarkdownFile = context.CurrentPath; - var includePath = Arguments; if (string.IsNullOrWhiteSpace(includePath)) { @@ -70,11 +59,12 @@ private void ExtractInclusionPath(ParserContext context) var includeFrom = context.CurrentPath.Directory!.FullName; if (includePath.StartsWith('/')) - includeFrom = DocumentationSourceDirectory.FullName; + includeFrom = Build.DocumentationSourceDirectory.FullName; IncludePath = Path.Combine(includeFrom, includePath.TrimStart('/')); - IncludePathFromSourceDirectory = Path.GetRelativePath(DocumentationSourceDirectory.FullName, IncludePath); - if (FileSystem.File.Exists(IncludePath)) + IncludePathRelativeToSource = Path.GetRelativePath(Build.DocumentationSourceDirectory.FullName, IncludePath); + + if (Build.ReadFileSystem.File.Exists(IncludePath)) Found = true; else this.EmitError($"`{IncludePath}` does not exist."); @@ -84,7 +74,7 @@ private void ExtractInclusionPath(ParserContext context) if (Literal) return; - var file = FileSystem.FileInfo.New(IncludePath); + var file = Build.ReadFileSystem.FileInfo.New(IncludePath); if (file.Directory != null && file.Directory.FullName.IndexOf("_snippets", StringComparison.Ordinal) < 0) { diff --git a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs index 2325a85f3..94a62c752 100644 --- a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs @@ -2,11 +2,7 @@ // 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 System.IO.Abstractions; -using Elastic.Markdown.CrossLinks; using Elastic.Markdown.Diagnostics; -using Elastic.Markdown.IO; -using Elastic.Markdown.IO.Configuration; -using Elastic.Markdown.Myst.FrontMatter; namespace Elastic.Markdown.Myst.Directives; @@ -14,22 +10,12 @@ public class SettingsBlock(DirectiveBlockParser parser, ParserContext context) : { public override string Directive => "settings"; - public Func GetDocumentationFile { get; } = context.GetDocumentationFile; + public ParserContext Context { get; } = context; - public ConfigurationFile Configuration { get; } = context.Configuration; - - public ICrossLinkResolver LinksResolver { get; } = context.LinksResolver; - - public IFileSystem FileSystem { get; } = context.Build.ReadFileSystem; + public string? IncludePath { get; private set; } public IFileInfo IncludeFrom { get; } = context.CurrentPath; - public IDirectoryInfo DocumentationSourceDirectory { get; } = context.Build.DocumentationSourceDirectory; - - public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter; - - public string? IncludePath { get; private set; } - public bool Found { get; private set; } @@ -48,10 +34,10 @@ private void ExtractInclusionPath(ParserContext context) var includeFrom = context.CurrentPath.Directory!.FullName; if (includePath.StartsWith('/')) - includeFrom = DocumentationSourceDirectory.FullName; + includeFrom = Build.DocumentationSourceDirectory.FullName; IncludePath = Path.Combine(includeFrom, includePath.TrimStart('/')); - if (FileSystem.File.Exists(IncludePath)) + if (Build.ReadFileSystem.File.Exists(IncludePath)) Found = true; else this.EmitError($"`{IncludePath}` does not exist."); diff --git a/src/Elastic.Markdown/Myst/ParserContext.cs b/src/Elastic.Markdown/Myst/ParserContext.cs index c182a92f8..2e2e9be1c 100644 --- a/src/Elastic.Markdown/Myst/ParserContext.cs +++ b/src/Elastic.Markdown/Myst/ParserContext.cs @@ -42,7 +42,6 @@ public class ParserContext : MarkdownParserContext public ConfigurationFile Configuration { get; } public ICrossLinkResolver LinksResolver { get; } public IFileInfo CurrentPath { get; } - public string CurrentPathRelative { get; } public string CurrentUrlPath { get; } public YamlFrontMatter? FrontMatter { get; } public BuildContext Build { get; } @@ -60,7 +59,6 @@ public ParserContext(ParserState state) LinksResolver = state.CrossLinkResolver; CurrentPath = state.SourcePath; - CurrentPathRelative = Path.GetRelativePath(Build.DocumentationSourceDirectory.FullName, CurrentPath.FullName); GetDocumentationFile = state.DocumentationFileLookup; var parentPath = state.ParentMarkdownPath; From ec65c36e50a2d1a98ae709c8c4f2022bf7253fce Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 26 Feb 2025 11:15:40 +0100 Subject: [PATCH 5/8] normalize property names between ParserContext and ParserState --- .../ProcessorDiagnosticExtensions.cs | 12 +++---- .../Myst/CodeBlocks/EnhancedCodeBlock.cs | 2 +- .../CodeBlocks/EnhancedCodeBlockParser.cs | 2 +- .../Myst/Directives/DirectiveBlock.cs | 2 +- .../Myst/Directives/DirectiveHtmlRenderer.cs | 14 ++++---- .../Myst/Directives/ImageBlock.cs | 2 +- .../Myst/Directives/IncludeBlock.cs | 4 +-- .../Myst/Directives/SettingsBlock.cs | 4 +-- .../DiagnosticLinkInlineParser.cs | 10 +++--- src/Elastic.Markdown/Myst/MarkdownParser.cs | 8 ++--- src/Elastic.Markdown/Myst/ParserContext.cs | 32 +++++++++---------- 11 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs b/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs index 577e55146..e337a8262 100644 --- a/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs +++ b/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs @@ -20,7 +20,7 @@ public static void EmitError(this InlineProcessor processor, int line, int colum var d = new Diagnostic { Severity = Severity.Error, - File = processor.GetContext().CurrentPath.FullName, + File = processor.GetContext().MarkdownSourcePath.FullName, Column = column, Line = line, Message = message, @@ -38,7 +38,7 @@ public static void EmitWarning(this InlineProcessor processor, int line, int col var d = new Diagnostic { Severity = Severity.Warning, - File = processor.GetContext().CurrentPath.FullName, + File = processor.GetContext().MarkdownSourcePath.FullName, Column = column, Line = line, Message = message, @@ -54,7 +54,7 @@ public static void EmitError(this ParserContext context, string message, Excepti var d = new Diagnostic { Severity = Severity.Error, - File = context.CurrentPath.FullName, + File = context.MarkdownSourcePath.FullName, Message = message + (e != null ? Environment.NewLine + e : string.Empty), }; context.Build.Collector.Channel.Write(d); @@ -67,7 +67,7 @@ public static void EmitWarning(this ParserContext context, int line, int column, var d = new Diagnostic { Severity = Severity.Warning, - File = context.CurrentPath.FullName, + File = context.MarkdownSourcePath.FullName, Column = column, Line = line, Message = message, @@ -146,7 +146,7 @@ public static void EmitError(this InlineProcessor processor, LinkInline inline, var d = new Diagnostic { Severity = Severity.Error, - File = processor.GetContext().CurrentPath.FullName, + File = processor.GetContext().MarkdownSourcePath.FullName, Column = column, Line = line, Message = message, @@ -169,7 +169,7 @@ public static void EmitWarning(this InlineProcessor processor, LinkInline inline var d = new Diagnostic { Severity = Severity.Warning, - File = processor.GetContext().CurrentPath.FullName, + File = processor.GetContext().MarkdownSourcePath.FullName, Column = column, Line = line, Message = message, diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs index 4df649031..d3522f666 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs @@ -21,7 +21,7 @@ public class EnhancedCodeBlock(BlockParser parser, ParserContext context) { public BuildContext Build { get; } = context.Build; - public IFileInfo CurrentFile { get; } = context.CurrentPath; + public IFileInfo CurrentFile { get; } = context.MarkdownSourcePath; public bool SkipValidation { get; } = context.SkipValidation; diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs index 82e376420..48647d504 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs @@ -147,7 +147,7 @@ private static void ProcessCallOuts(StringLineGroup lines, string language, Enha var span = line.Slice.AsSpan(); - if (span.ReplaceSubstitutions(context.FrontMatter?.Properties, out var replacement)) + if (span.ReplaceSubstitutions(context.YamlFrontMatter?.Properties, out var replacement)) { var s = new StringSlice(replacement); lines.Lines[index] = new StringLine(ref s); diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs index 870f3b6f7..e03398882 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs @@ -43,7 +43,7 @@ public abstract class DirectiveBlock( public BuildContext Build { get; } = context.Build; - public IFileInfo CurrentFile { get; } = context.CurrentPath; + public IFileInfo CurrentFile { get; } = context.MarkdownSourcePath; public bool SkipValidation { get; } = context.SkipValidation; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 39279e648..11598d89f 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -227,13 +227,13 @@ private static void WriteIncludeBlock(HtmlRenderer renderer, IncludeBlock block) var parser = new MarkdownParser( block.Build.DocumentationSourceDirectory, block.Build, - block.Context.GetDocumentationFile, + block.Context.DocumentationFileLookup, block.Build.Configuration, - block.Context.LinksResolver + block.Context.CrossLinkResolver ); var snippet = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); - var parentPath = block.Context.CurrentPath; - var document = parser.ParseSnippetAsync(snippet, parentPath, block.Context.FrontMatter, default).GetAwaiter().GetResult(); + var parentPath = block.Context.MarkdownSourcePath; + var document = parser.ParseSnippetAsync(snippet, parentPath, block.Context.YamlFrontMatter, default).GetAwaiter().GetResult(); var html = document.ToHtml(MarkdownParser.Pipeline); _ = renderer.Write(html); } @@ -244,8 +244,8 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc return; var parser = new MarkdownParser( - block.Build.DocumentationSourceDirectory, block.Build, block.Context.GetDocumentationFile, block.Build.Configuration - , block.Context.LinksResolver + block.Build.DocumentationSourceDirectory, block.Build, block.Context.DocumentationFileLookup, block.Build.Configuration + , block.Context.CrossLinkResolver ); var file = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); @@ -272,7 +272,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc SettingsCollection = settings, RenderMarkdown = s => { - var document = parser.ParseEmbeddedMarkdown(s, block.IncludeFrom, block.Context.FrontMatter); + var document = parser.ParseEmbeddedMarkdown(s, block.IncludeFrom, block.Context.YamlFrontMatter); var html = document.ToHtml(MarkdownParser.Pipeline); return html; } diff --git a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs index 73670ddb9..8e132642a 100644 --- a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs @@ -88,7 +88,7 @@ private void ExtractImageUrl(ParserContext context) return; } - var includeFrom = context.CurrentPath.Directory!.FullName; + var includeFrom = context.MarkdownSourcePath.Directory!.FullName; if (imageUrl.StartsWith('/')) includeFrom = context.Build.DocumentationSourceDirectory.FullName; diff --git a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs index 740f94b2e..45552e332 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -57,7 +57,7 @@ private void ExtractInclusionPath(ParserContext context) return; } - var includeFrom = context.CurrentPath.Directory!.FullName; + var includeFrom = context.MarkdownSourcePath.Directory!.FullName; if (includePath.StartsWith('/')) includeFrom = Build.DocumentationSourceDirectory.FullName; @@ -82,7 +82,7 @@ private void ExtractInclusionPath(ParserContext context) Found = false; } - if (file.FullName == context.CurrentPath.FullName) + if (file.FullName == context.MarkdownSourcePath.FullName) { this.EmitError($"{{include}} cyclical include detected `{IncludePath}` points to itself"); Found = false; diff --git a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs index 94a62c752..1cf37ed29 100644 --- a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs @@ -14,7 +14,7 @@ public class SettingsBlock(DirectiveBlockParser parser, ParserContext context) : public string? IncludePath { get; private set; } - public IFileInfo IncludeFrom { get; } = context.CurrentPath; + public IFileInfo IncludeFrom { get; } = context.MarkdownSourcePath; public bool Found { get; private set; } @@ -32,7 +32,7 @@ private void ExtractInclusionPath(ParserContext context) return; } - var includeFrom = context.CurrentPath.Directory!.FullName; + var includeFrom = context.MarkdownSourcePath.Directory!.FullName; if (includePath.StartsWith('/')) includeFrom = Build.DocumentationSourceDirectory.FullName; diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index f0ca95ebb..a451c2007 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -162,7 +162,7 @@ private static void ProcessCrossLink(LinkInline link, InlineProcessor processor, if (url != null) context.Build.Collector.EmitCrossLink(url); - if (context.LinksResolver.TryResolve(s => processor.EmitError(link, s), uri, out var resolvedUri)) + if (context.CrossLinkResolver.TryResolve(s => processor.EmitError(link, s), uri, out var resolvedUri)) link.Url = resolvedUri.ToString(); } @@ -185,7 +185,7 @@ private static (string url, string? anchor) SplitUrlAndAnchor(string fullUrl) private static string GetIncludeFromPath(string url, ParserContext context) => url.StartsWith('/') ? context.Build.DocumentationSourceDirectory.FullName - : context.CurrentPath.Directory!.FullName; + : context.MarkdownSourcePath.Directory!.FullName; private static void ValidateInternalUrl(InlineProcessor processor, string url, string includeFrom, LinkInline link, ParserContext context) { @@ -202,7 +202,7 @@ private static void ProcessLinkText(InlineProcessor processor, LinkInline link, if (link.FirstChild != null && string.IsNullOrEmpty(anchor)) return; - var markdown = context.GetDocumentationFile(file) as MarkdownFile; + var markdown = context.DocumentationFileLookup(file) as MarkdownFile; if (markdown == null && link.FirstChild == null) { @@ -227,10 +227,10 @@ private static void ProcessLinkText(InlineProcessor processor, LinkInline link, private static IFileInfo ResolveFile(ParserContext context, string url) => string.IsNullOrWhiteSpace(url) - ? context.CurrentPath + ? context.MarkdownSourcePath : url.StartsWith('/') ? context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Build.DocumentationSourceDirectory.FullName, url.TrimStart('/'))) - : context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.CurrentPath.Directory!.FullName, url)); + : context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.MarkdownSourcePath.Directory!.FullName, url)); private static void ValidateAnchor(InlineProcessor processor, MarkdownFile markdown, string anchor, LinkInline link) { diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index 736773e0d..a2a3b5242 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -96,7 +96,7 @@ public Task MinimalParseAsync(IFileInfo path, Cancel ctx) { var state = new ParserState(Build) { - SourcePath = path, + MarkdownSourcePath = path, YamlFrontMatter = null, DocumentationFileLookup = getDocumentationFile, CrossLinkResolver = LinksResolver, @@ -110,7 +110,7 @@ public Task ParseAsync(IFileInfo path, YamlFrontMatter? matter { var state = new ParserState(Build) { - SourcePath = path, + MarkdownSourcePath = path, YamlFrontMatter = matter, DocumentationFileLookup = getDocumentationFile, CrossLinkResolver = LinksResolver @@ -123,7 +123,7 @@ public Task ParseSnippetAsync(IFileInfo path, IFileInfo parent { var state = new ParserState(Build) { - SourcePath = path, + MarkdownSourcePath = path, YamlFrontMatter = matter, DocumentationFileLookup = getDocumentationFile, CrossLinkResolver = LinksResolver, @@ -137,7 +137,7 @@ public MarkdownDocument ParseEmbeddedMarkdown(string markdown, IFileInfo path, Y { var state = new ParserState(Build) { - SourcePath = path, + MarkdownSourcePath = path, YamlFrontMatter = matter, DocumentationFileLookup = getDocumentationFile, CrossLinkResolver = LinksResolver diff --git a/src/Elastic.Markdown/Myst/ParserContext.cs b/src/Elastic.Markdown/Myst/ParserContext.cs index 2e2e9be1c..e5764a2b1 100644 --- a/src/Elastic.Markdown/Myst/ParserContext.cs +++ b/src/Elastic.Markdown/Myst/ParserContext.cs @@ -28,25 +28,25 @@ public record ParserState(BuildContext Build) { public ConfigurationFile Configuration { get; } = Build.Configuration; - public required IFileInfo SourcePath { get; init; } + public required IFileInfo MarkdownSourcePath { get; init; } public required YamlFrontMatter? YamlFrontMatter { get; init; } public required ICrossLinkResolver CrossLinkResolver { get; init; } public required Func DocumentationFileLookup { get; init; } - public IFileInfo? ParentMarkdownPath { get; set; } - public bool SkipValidation { get; set; } + public IFileInfo? ParentMarkdownPath { get; init; } + public bool SkipValidation { get; init; } } public class ParserContext : MarkdownParserContext { public ConfigurationFile Configuration { get; } - public ICrossLinkResolver LinksResolver { get; } - public IFileInfo CurrentPath { get; } + public ICrossLinkResolver CrossLinkResolver { get; } + public IFileInfo MarkdownSourcePath { get; } public string CurrentUrlPath { get; } - public YamlFrontMatter? FrontMatter { get; } + public YamlFrontMatter? YamlFrontMatter { get; } public BuildContext Build { get; } public bool SkipValidation { get; } - public Func GetDocumentationFile { get; } + public Func DocumentationFileLookup { get; } public IReadOnlyDictionary Substitutions { get; } public IReadOnlyDictionary ContextSubstitutions { get; } @@ -54,26 +54,26 @@ public ParserContext(ParserState state) { Build = state.Build; Configuration = state.Configuration; - FrontMatter = state.YamlFrontMatter; + YamlFrontMatter = state.YamlFrontMatter; SkipValidation = state.SkipValidation; - LinksResolver = state.CrossLinkResolver; - CurrentPath = state.SourcePath; - GetDocumentationFile = state.DocumentationFileLookup; + CrossLinkResolver = state.CrossLinkResolver; + MarkdownSourcePath = state.MarkdownSourcePath; + DocumentationFileLookup = state.DocumentationFileLookup; var parentPath = state.ParentMarkdownPath; - CurrentUrlPath = GetDocumentationFile(parentPath ?? CurrentPath) is MarkdownFile md + CurrentUrlPath = DocumentationFileLookup(parentPath ?? MarkdownSourcePath) is MarkdownFile md ? md.Url : SkipValidation ? string.Empty - : throw new Exception($"Unable to find documentation file for {(parentPath ?? CurrentPath).FullName}"); + : throw new Exception($"Unable to find documentation file for {(parentPath ?? MarkdownSourcePath).FullName}"); - if (FrontMatter?.Properties is not { Count: > 0 }) + if (YamlFrontMatter?.Properties is not { Count: > 0 }) Substitutions = Configuration.Substitutions; else { var subs = new Dictionary(Configuration.Substitutions); - foreach (var (k, value) in FrontMatter.Properties) + foreach (var (k, value) in YamlFrontMatter.Properties) { var key = k.ToLowerInvariant(); if (Configuration.Substitutions.TryGetValue(key, out _)) @@ -87,7 +87,7 @@ public ParserContext(ParserState state) var contextSubs = new Dictionary(); - if (FrontMatter?.Title is { } title) + if (YamlFrontMatter?.Title is { } title) contextSubs["context.page_title"] = title; ContextSubstitutions = contextSubs; From dfd96c51da49b5ec61d69ee5f85de412b53696cb Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 26 Feb 2025 11:35:32 +0100 Subject: [PATCH 6/8] normalize passing resolvers around --- src/Elastic.Markdown.Refactor/Move.cs | 4 +- .../DocumentationGenerator.cs | 16 +- src/Elastic.Markdown/IO/DocumentationSet.cs | 89 +++++------ src/Elastic.Markdown/IO/MarkdownFile.cs | 15 +- .../IO/State/LinkReference.cs | 6 +- .../Myst/Directives/DirectiveHtmlRenderer.cs | 13 +- src/Elastic.Markdown/Myst/MarkdownParser.cs | 142 ++++++++---------- src/Elastic.Markdown/Myst/ParserContext.cs | 19 ++- src/Elastic.Markdown/Slices/HtmlWriter.cs | 6 +- .../Http/ReloadGeneratorService.cs | 2 +- .../Directives/DirectiveBaseTests.cs | 2 +- .../Inline/InlneBaseTests.cs | 2 +- 12 files changed, 154 insertions(+), 162 deletions(-) diff --git a/src/Elastic.Markdown.Refactor/Move.cs b/src/Elastic.Markdown.Refactor/Move.cs index 59ca49584..a20b031e5 100644 --- a/src/Elastic.Markdown.Refactor/Move.cs +++ b/src/Elastic.Markdown.Refactor/Move.cs @@ -254,9 +254,9 @@ string targetPath { var relativeSource = Path.GetRelativePath(currentDir, sourcePath); var relativeSourceWithDotSlash = Path.Combine(".", relativeSource); - var relativeToDocsFolder = Path.GetRelativePath(documentationSet.SourcePath.FullName, sourcePath); + var relativeToDocsFolder = Path.GetRelativePath(documentationSet.SourceDirectory.FullName, sourcePath); var absolutStyleSource = $"/{relativeToDocsFolder}"; - var relativeToDocsFolderTarget = Path.GetRelativePath(documentationSet.SourcePath.FullName, targetPath); + var relativeToDocsFolderTarget = Path.GetRelativePath(documentationSet.SourceDirectory.FullName, targetPath); var absoluteStyleTarget = $"/{relativeToDocsFolderTarget}"; return ( relativeSource, diff --git a/src/Elastic.Markdown/DocumentationGenerator.cs b/src/Elastic.Markdown/DocumentationGenerator.cs index c88b7d34d..833391926 100644 --- a/src/Elastic.Markdown/DocumentationGenerator.cs +++ b/src/Elastic.Markdown/DocumentationGenerator.cs @@ -38,18 +38,18 @@ public DocumentationGenerator( ) { _conversionCollector = conversionCollector; - _readFileSystem = docSet.Context.ReadFileSystem; - _writeFileSystem = docSet.Context.WriteFileSystem; + _readFileSystem = docSet.Build.ReadFileSystem; + _writeFileSystem = docSet.Build.WriteFileSystem; _logger = logger.CreateLogger(nameof(DocumentationGenerator)); DocumentationSet = docSet; - Context = docSet.Context; + Context = docSet.Build; Resolver = docSet.LinkResolver; HtmlWriter = new HtmlWriter(DocumentationSet, _writeFileSystem); _logger.LogInformation("Created documentation set for: {DocumentationSetName}", DocumentationSet.Name); - _logger.LogInformation("Source directory: {SourcePath} Exists: {SourcePathExists}", docSet.SourcePath, docSet.SourcePath.Exists); - _logger.LogInformation("Output directory: {OutputPath} Exists: {OutputPathExists}", docSet.OutputPath, docSet.OutputPath.Exists); + _logger.LogInformation("Source directory: {SourcePath} Exists: {SourcePathExists}", docSet.SourceDirectory, docSet.SourceDirectory.Exists); + _logger.LogInformation("Output directory: {OutputPath} Exists: {OutputPathExists}", docSet.OutputDirectory, docSet.OutputDirectory.Exists); } public GenerationState? GetPreviousGenerationState() @@ -181,7 +181,7 @@ private async Task ProcessFile(HashSet offendingFiles, DocumentationFile private IFileInfo OutputFile(string relativePath) { - var outputFile = _writeFileSystem.FileInfo.New(Path.Combine(DocumentationSet.OutputPath.FullName, relativePath)); + var outputFile = _writeFileSystem.FileInfo.New(Path.Combine(DocumentationSet.OutputDirectory.FullName, relativePath)); return outputFile; } @@ -227,7 +227,7 @@ private async Task GenerateLinkReference(Cancel ctx) var state = LinkReference.Create(DocumentationSet); var bytes = JsonSerializer.SerializeToUtf8Bytes(state, SourceGenerationContext.Default.LinkReference); - await DocumentationSet.OutputPath.FileSystem.File.WriteAllBytesAsync(file.FullName, bytes, ctx); + await DocumentationSet.OutputDirectory.FileSystem.File.WriteAllBytesAsync(file.FullName, bytes, ctx); } private async Task GenerateDocumentationState(Cancel ctx) @@ -242,7 +242,7 @@ private async Task GenerateDocumentationState(Cancel ctx) Git = Context.Git }; var bytes = JsonSerializer.SerializeToUtf8Bytes(state, SourceGenerationContext.Default.GenerationState); - await DocumentationSet.OutputPath.FileSystem.File.WriteAllBytesAsync(stateFile.FullName, bytes, ctx); + await DocumentationSet.OutputDirectory.FileSystem.File.WriteAllBytesAsync(stateFile.FullName, bytes, ctx); } private async Task CopyFileFsAware(DocumentationFile file, IFileInfo outputFile, Cancel ctx) diff --git a/src/Elastic.Markdown/IO/DocumentationSet.cs b/src/Elastic.Markdown/IO/DocumentationSet.cs index bec1b5d73..a2ab7c96e 100644 --- a/src/Elastic.Markdown/IO/DocumentationSet.cs +++ b/src/Elastic.Markdown/IO/DocumentationSet.cs @@ -16,13 +16,13 @@ namespace Elastic.Markdown.IO; public class DocumentationSet { - public BuildContext Context { get; } + public BuildContext Build { get; } public string Name { get; } public IFileInfo OutputStateFile { get; } public IFileInfo LinkReferenceFile { get; } - public IDirectoryInfo SourcePath { get; } - public IDirectoryInfo OutputPath { get; } + public IDirectoryInfo SourceDirectory { get; } + public IDirectoryInfo OutputDirectory { get; } public string RelativeSourcePath { get; } @@ -30,42 +30,47 @@ public class DocumentationSet public ConfigurationFile Configuration { get; } - public MarkdownParser MarkdownParser { get; } + private MarkdownParser MarkdownParser { get; } public ICrossLinkResolver LinkResolver { get; } - public DocumentationSet(BuildContext context, ILoggerFactory logger, ICrossLinkResolver? linkResolver = null) + public DocumentationSet(BuildContext build, ILoggerFactory logger, ICrossLinkResolver? linkResolver = null) { - Context = context; - SourcePath = context.DocumentationSourceDirectory; - OutputPath = context.DocumentationOutputDirectory; - RelativeSourcePath = Path.GetRelativePath(Paths.Root.FullName, SourcePath.FullName); + Build = build; + SourceDirectory = build.DocumentationSourceDirectory; + OutputDirectory = build.DocumentationOutputDirectory; + RelativeSourcePath = Path.GetRelativePath(Paths.Root.FullName, SourceDirectory.FullName); LinkResolver = - linkResolver ?? new CrossLinkResolver(new ConfigurationCrossLinkFetcher(context.Configuration, logger)); - Configuration = context.Configuration; + linkResolver ?? new CrossLinkResolver(new ConfigurationCrossLinkFetcher(build.Configuration, logger)); + Configuration = build.Configuration; - MarkdownParser = new MarkdownParser(SourcePath, context, GetMarkdownFile, context.Configuration, LinkResolver); + var resolver = new ParserResolvers + { + CrossLinkResolver = LinkResolver, + DocumentationFileLookup = DocumentationFileLookup + }; + MarkdownParser = new MarkdownParser(build, resolver); - Name = SourcePath.FullName; - OutputStateFile = OutputPath.FileSystem.FileInfo.New(Path.Combine(OutputPath.FullName, ".doc.state")); - LinkReferenceFile = OutputPath.FileSystem.FileInfo.New(Path.Combine(OutputPath.FullName, "links.json")); + Name = SourceDirectory.FullName; + OutputStateFile = OutputDirectory.FileSystem.FileInfo.New(Path.Combine(OutputDirectory.FullName, ".doc.state")); + LinkReferenceFile = OutputDirectory.FileSystem.FileInfo.New(Path.Combine(OutputDirectory.FullName, "links.json")); - Files = [.. context.ReadFileSystem.Directory - .EnumerateFiles(SourcePath.FullName, "*.*", SearchOption.AllDirectories) - .Select(f => context.ReadFileSystem.FileInfo.New(f)) + Files = [.. build.ReadFileSystem.Directory + .EnumerateFiles(SourceDirectory.FullName, "*.*", SearchOption.AllDirectories) + .Select(f => build.ReadFileSystem.FileInfo.New(f)) .Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden) && !f.Attributes.HasFlag(FileAttributes.System)) .Where(f => !f.Directory!.Attributes.HasFlag(FileAttributes.Hidden) && !f.Directory!.Attributes.HasFlag(FileAttributes.System)) // skip hidden folders - .Where(f => !Path.GetRelativePath(SourcePath.FullName, f.FullName).StartsWith('.')) + .Where(f => !Path.GetRelativePath(SourceDirectory.FullName, f.FullName).StartsWith('.')) .Select(file => file.Extension switch { - ".jpg" => new ImageFile(file, SourcePath, "image/jpeg"), - ".jpeg" => new ImageFile(file, SourcePath, "image/jpeg"), - ".gif" => new ImageFile(file, SourcePath, "image/gif"), - ".svg" => new ImageFile(file, SourcePath, "image/svg+xml"), - ".png" => new ImageFile(file, SourcePath), - ".md" => CreateMarkDownFile(file, context), - _ => new StaticFile(file, SourcePath) + ".jpg" => new ImageFile(file, SourceDirectory, "image/jpeg"), + ".jpeg" => new ImageFile(file, SourceDirectory, "image/jpeg"), + ".gif" => new ImageFile(file, SourceDirectory, "image/gif"), + ".svg" => new ImageFile(file, SourceDirectory, "image/svg+xml"), + ".png" => new ImageFile(file, SourceDirectory), + ".md" => CreateMarkDownFile(file, build), + _ => new StaticFile(file, SourceDirectory) })]; LastWrite = Files.Max(f => f.SourceFile.LastWriteTimeUtc); @@ -77,7 +82,7 @@ public DocumentationSet(BuildContext context, ILoggerFactory logger, ICrossLinkR .ToDictionary(g => g.Key, g => g.ToArray()); var fileIndex = 0; - Tree = new DocumentationGroup(Context, Configuration.TableOfContents, FlatMappedFiles, folderFiles, ref fileIndex) + Tree = new DocumentationGroup(Build, Configuration.TableOfContents, FlatMappedFiles, folderFiles, ref fileIndex) { Parent = null }; @@ -86,7 +91,7 @@ public DocumentationSet(BuildContext context, ILoggerFactory logger, ICrossLinkR var excludedChildren = markdownFiles.Where(f => f.NavigationIndex == -1).ToArray(); foreach (var excludedChild in excludedChildren) - Context.EmitError(Context.ConfigurationPath, $"{excludedChild.RelativePath} is unreachable in the TOC because one of its parents matches exclusion glob"); + Build.EmitError(Build.ConfigurationPath, $"{excludedChild.RelativePath} is unreachable in the TOC because one of its parents matches exclusion glob"); MarkdownFiles = markdownFiles.Where(f => f.NavigationIndex > -1).ToDictionary(i => i.NavigationIndex, i => i).ToFrozenDictionary(); ValidateRedirectsExists(); @@ -114,14 +119,14 @@ void ValidateExists(string from, string to, IReadOnlyDictionary { if (!FlatMappedFiles.TryGetValue(to, out var file)) { - Context.EmitError(Configuration.SourceFile, $"Redirect {from} points to {to} which does not exist"); + Build.EmitError(Configuration.SourceFile, $"Redirect {from} points to {to} which does not exist"); return; } if (file is not MarkdownFile markdownFile) { - Context.EmitError(Configuration.SourceFile, $"Redirect {from} points to {to} which is not a markdown file"); + Build.EmitError(Configuration.SourceFile, $"Redirect {from} points to {to} which is not a markdown file"); return; } @@ -138,9 +143,9 @@ void ValidateExists(string from, string to, IReadOnlyDictionary public FrozenDictionary MarkdownFiles { get; } - public MarkdownFile? GetMarkdownFile(IFileInfo sourceFile) + public MarkdownFile? DocumentationFileLookup(IFileInfo sourceFile) { - var relativePath = Path.GetRelativePath(SourcePath.FullName, sourceFile.FullName); + var relativePath = Path.GetRelativePath(SourceDirectory.FullName, sourceFile.FullName); if (FlatMappedFiles.TryGetValue(relativePath, out var file) && file is MarkdownFile markdownFile) return markdownFile; return null; @@ -184,26 +189,26 @@ public async Task ResolveDirectoryTree(Cancel ctx) => private DocumentationFile CreateMarkDownFile(IFileInfo file, BuildContext context) { - var relativePath = Path.GetRelativePath(SourcePath.FullName, file.FullName); + var relativePath = Path.GetRelativePath(SourceDirectory.FullName, file.FullName); if (Configuration.Exclude.Any(g => g.IsMatch(relativePath))) - return new ExcludedFile(file, SourcePath); + return new ExcludedFile(file, SourceDirectory); // we ignore files in folders that start with an underscore if (relativePath.Contains("_snippets")) - return new SnippetFile(file, SourcePath); + return new SnippetFile(file, SourceDirectory); if (Configuration.Files.Contains(relativePath)) - return new MarkdownFile(file, SourcePath, MarkdownParser, context, this); + return new MarkdownFile(file, SourceDirectory, MarkdownParser, context, this); if (Configuration.Globs.Any(g => g.IsMatch(relativePath))) - return new MarkdownFile(file, SourcePath, MarkdownParser, context, this); + return new MarkdownFile(file, SourceDirectory, MarkdownParser, context, this); // we ignore files in folders that start with an underscore if (relativePath.IndexOf("/_", StringComparison.Ordinal) > 0 || relativePath.StartsWith('_')) - return new ExcludedFile(file, SourcePath); + return new ExcludedFile(file, SourceDirectory); context.EmitError(Configuration.SourceFile, $"Not linked in toc: {relativePath}"); - return new ExcludedFile(file, SourcePath); + return new ExcludedFile(file, SourceDirectory); } public DocumentationGroup Tree { get; } @@ -214,8 +219,8 @@ private DocumentationFile CreateMarkDownFile(IFileInfo file, BuildContext contex public void ClearOutputDirectory() { - if (OutputPath.Exists) - OutputPath.Delete(true); - OutputPath.Create(); + if (OutputDirectory.Exists) + OutputDirectory.Delete(true); + OutputDirectory.Create(); } } diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 78ad37f4f..9e4a79dfd 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -5,6 +5,7 @@ using System.IO.Abstractions; using Elastic.Markdown.Diagnostics; using Elastic.Markdown.Helpers; +using Elastic.Markdown.IO.Configuration; using Elastic.Markdown.IO.Navigation; using Elastic.Markdown.Myst; using Elastic.Markdown.Myst.Directives; @@ -27,23 +28,24 @@ public MarkdownFile( IFileInfo sourceFile, IDirectoryInfo rootPath, MarkdownParser parser, - BuildContext context, + BuildContext build, DocumentationSet set ) : base(sourceFile, rootPath) { FileName = sourceFile.Name; FilePath = sourceFile.FullName; - UrlPathPrefix = context.UrlPathPrefix; + UrlPathPrefix = build.UrlPathPrefix; MarkdownParser = parser; - Collector = context.Collector; - _configurationFile = context.Configuration.SourceFile; + Collector = build.Collector; + Configuration = build.Configuration; _set = set; } public string Id { get; } = Guid.NewGuid().ToString("N")[..8]; private DiagnosticsCollector Collector { get; } + private ConfigurationFile Configuration { get; } public DocumentationGroup? Parent { @@ -94,7 +96,6 @@ public string? NavigationTitle private bool _instructionsParsed; private DocumentationGroup? _parent; private string? _title; - private readonly IFileInfo _configurationFile; public MarkdownFile[] YieldParents() { @@ -141,7 +142,7 @@ private void ValidateAnchorRemapping() if (Anchors.Contains(v)) continue; - Collector.EmitError(_configurationFile.FullName, $"Bad anchor remap '{v}' does not exist in {RelativePath}"); + Collector.EmitError(Configuration.SourceFile.FullName, $"Bad anchor remap '{v}' does not exist in {RelativePath}"); } } @@ -164,7 +165,7 @@ public async Task ParseFullAsync(Cancel ctx) private IReadOnlyDictionary GetSubstitutions() { - var globalSubstitutions = MarkdownParser.Configuration.Substitutions; + var globalSubstitutions = Configuration.Substitutions; var fileSubstitutions = YamlFrontMatter?.Properties; if (fileSubstitutions is not { Count: >= 0 }) return globalSubstitutions; diff --git a/src/Elastic.Markdown/IO/State/LinkReference.cs b/src/Elastic.Markdown/IO/State/LinkReference.cs index f4d895a14..c5a0a6b14 100644 --- a/src/Elastic.Markdown/IO/State/LinkReference.cs +++ b/src/Elastic.Markdown/IO/State/LinkReference.cs @@ -67,7 +67,7 @@ public static LinkReference Deserialize(string json) => public static LinkReference Create(DocumentationSet set) { var redirects = set.Configuration.Redirects; - var crossLinks = set.Context.Collector.CrossLinks.ToHashSet().ToArray(); + var crossLinks = set.Build.Collector.CrossLinks.ToHashSet().ToArray(); var links = set.MarkdownFiles.Values .Select(m => (m.RelativePath, File: m)) .ToDictionary(k => k.RelativePath, v => @@ -79,8 +79,8 @@ public static LinkReference Create(DocumentationSet set) return new LinkReference { Redirects = redirects, - UrlPathPrefix = set.Context.UrlPathPrefix, - Origin = set.Context.Git, + UrlPathPrefix = set.Build.UrlPathPrefix, + Origin = set.Build.Git, Links = links, CrossLinks = crossLinks }; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 11598d89f..89b18c96a 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -224,13 +224,7 @@ private static void WriteIncludeBlock(HtmlRenderer renderer, IncludeBlock block) if (!block.Found || block.IncludePath is null) return; - var parser = new MarkdownParser( - block.Build.DocumentationSourceDirectory, - block.Build, - block.Context.DocumentationFileLookup, - block.Build.Configuration, - block.Context.CrossLinkResolver - ); + var parser = new MarkdownParser(block.Build, block.Context); var snippet = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); var parentPath = block.Context.MarkdownSourcePath; var document = parser.ParseSnippetAsync(snippet, parentPath, block.Context.YamlFrontMatter, default).GetAwaiter().GetResult(); @@ -243,10 +237,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc if (!block.Found || block.IncludePath is null) return; - var parser = new MarkdownParser( - block.Build.DocumentationSourceDirectory, block.Build, block.Context.DocumentationFileLookup, block.Build.Configuration - , block.Context.CrossLinkResolver - ); + var parser = new MarkdownParser(block.Build, block.Context); var file = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index a2a3b5242..67a7e9093 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -4,9 +4,6 @@ using System.IO.Abstractions; using Cysharp.IO; -using Elastic.Markdown.CrossLinks; -using Elastic.Markdown.IO; -using Elastic.Markdown.IO.Configuration; using Elastic.Markdown.Myst.CodeBlocks; using Elastic.Markdown.Myst.Comments; using Elastic.Markdown.Myst.Directives; @@ -21,76 +18,10 @@ namespace Elastic.Markdown.Myst; -public class MarkdownParser( - IDirectoryInfo sourcePath, - BuildContext build, - Func getDocumentationFile, - ConfigurationFile configuration, - ICrossLinkResolver linksResolver -) +public class MarkdownParser(BuildContext build, IParserResolvers resolvers) { - public IDirectoryInfo SourcePath { get; } = sourcePath; - private BuildContext Build { get; } = build; - - private ICrossLinkResolver LinksResolver { get; } = linksResolver; - - // ReSharper disable once InconsistentNaming - private static MarkdownPipeline? MinimalPipelineCached; - - private static MarkdownPipeline MinimalPipeline - { - get - { - if (MinimalPipelineCached is not null) - return MinimalPipelineCached; - var builder = new MarkdownPipelineBuilder() - .UseYamlFrontMatter() - .UseInlineAnchors() - .UseHeadingsWithSlugs() - .UseDirectives(); - - _ = builder.BlockParsers.TryRemove(); - MinimalPipelineCached = builder.Build(); - return MinimalPipelineCached; - } - } - - // ReSharper disable once InconsistentNaming - private static MarkdownPipeline? PipelineCached; - - public static MarkdownPipeline Pipeline - { - get - { - if (PipelineCached is not null) - return PipelineCached; - - var builder = new MarkdownPipelineBuilder() - .UseInlineAnchors() - .UsePreciseSourceLocation() - .UseDiagnosticLinks() - .UseHeadingsWithSlugs() - .UseEmphasisExtras(EmphasisExtraOptions.Default) - .UseSoftlineBreakAsHardlineBreak() - .UseSubstitution() - .UseComments() - .UseYamlFrontMatter() - .UseGridTables() - .UsePipeTables() - .UseDirectives() - .UseDefinitionLists() - .UseEnhancedCodeBlocks() - .UseHtmxLinkInlineRenderer() - .DisableHtml() - .UseHardBreaks(); - _ = builder.BlockParsers.TryRemove(); - PipelineCached = builder.Build(); - return PipelineCached; - } - } - - public ConfigurationFile Configuration { get; } = configuration; + private IParserResolvers Resolvers { get; } = resolvers; public Task MinimalParseAsync(IFileInfo path, Cancel ctx) { @@ -98,8 +29,8 @@ public Task MinimalParseAsync(IFileInfo path, Cancel ctx) { MarkdownSourcePath = path, YamlFrontMatter = null, - DocumentationFileLookup = getDocumentationFile, - CrossLinkResolver = LinksResolver, + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver, SkipValidation = true }; var context = new ParserContext(state); @@ -112,8 +43,8 @@ public Task ParseAsync(IFileInfo path, YamlFrontMatter? matter { MarkdownSourcePath = path, YamlFrontMatter = matter, - DocumentationFileLookup = getDocumentationFile, - CrossLinkResolver = LinksResolver + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver }; var context = new ParserContext(state); return ParseAsync(path, context, Pipeline, ctx); @@ -125,8 +56,8 @@ public Task ParseSnippetAsync(IFileInfo path, IFileInfo parent { MarkdownSourcePath = path, YamlFrontMatter = matter, - DocumentationFileLookup = getDocumentationFile, - CrossLinkResolver = LinksResolver, + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver, ParentMarkdownPath = parentPath }; var context = new ParserContext(state); @@ -139,8 +70,8 @@ public MarkdownDocument ParseEmbeddedMarkdown(string markdown, IFileInfo path, Y { MarkdownSourcePath = path, YamlFrontMatter = matter, - DocumentationFileLookup = getDocumentationFile, - CrossLinkResolver = LinksResolver + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver }; var context = new ParserContext(state); var markdownDocument = Markdig.Markdown.Parse(markdown, Pipeline, context); @@ -169,4 +100,57 @@ private static async Task ParseAsync( } } + // ReSharper disable once InconsistentNaming + private static MarkdownPipeline? MinimalPipelineCached; + private static MarkdownPipeline MinimalPipeline + { + get + { + if (MinimalPipelineCached is not null) + return MinimalPipelineCached; + var builder = new MarkdownPipelineBuilder() + .UseYamlFrontMatter() + .UseInlineAnchors() + .UseHeadingsWithSlugs() + .UseDirectives(); + + _ = builder.BlockParsers.TryRemove(); + MinimalPipelineCached = builder.Build(); + return MinimalPipelineCached; + } + } + + // ReSharper disable once InconsistentNaming + private static MarkdownPipeline? PipelineCached; + public static MarkdownPipeline Pipeline + { + get + { + if (PipelineCached is not null) + return PipelineCached; + + var builder = new MarkdownPipelineBuilder() + .UseInlineAnchors() + .UsePreciseSourceLocation() + .UseDiagnosticLinks() + .UseHeadingsWithSlugs() + .UseEmphasisExtras(EmphasisExtraOptions.Default) + .UseSoftlineBreakAsHardlineBreak() + .UseSubstitution() + .UseComments() + .UseYamlFrontMatter() + .UseGridTables() + .UsePipeTables() + .UseDirectives() + .UseDefinitionLists() + .UseEnhancedCodeBlocks() + .UseHtmxLinkInlineRenderer() + .DisableHtml() + .UseHardBreaks(); + _ = builder.BlockParsers.TryRemove(); + PipelineCached = builder.Build(); + return PipelineCached; + } + } + } diff --git a/src/Elastic.Markdown/Myst/ParserContext.cs b/src/Elastic.Markdown/Myst/ParserContext.cs index e5764a2b1..c025ea03c 100644 --- a/src/Elastic.Markdown/Myst/ParserContext.cs +++ b/src/Elastic.Markdown/Myst/ParserContext.cs @@ -24,20 +24,31 @@ processor.Context as ParserContext ?? throw new InvalidOperationException($"Provided context is not a {nameof(ParserContext)}"); } -public record ParserState(BuildContext Build) +public interface IParserResolvers +{ + ICrossLinkResolver CrossLinkResolver { get; } + Func DocumentationFileLookup { get; } +} + +public record ParserResolvers : IParserResolvers +{ + public required ICrossLinkResolver CrossLinkResolver { get; init; } + + public required Func DocumentationFileLookup { get; init; } +} + +public record ParserState(BuildContext Build) : ParserResolvers { public ConfigurationFile Configuration { get; } = Build.Configuration; public required IFileInfo MarkdownSourcePath { get; init; } public required YamlFrontMatter? YamlFrontMatter { get; init; } - public required ICrossLinkResolver CrossLinkResolver { get; init; } - public required Func DocumentationFileLookup { get; init; } public IFileInfo? ParentMarkdownPath { get; init; } public bool SkipValidation { get; init; } } -public class ParserContext : MarkdownParserContext +public class ParserContext : MarkdownParserContext, IParserResolvers { public ConfigurationFile Configuration { get; } public ICrossLinkResolver CrossLinkResolver { get; } diff --git a/src/Elastic.Markdown/Slices/HtmlWriter.cs b/src/Elastic.Markdown/Slices/HtmlWriter.cs index 988c74b27..c4e5a1d1e 100644 --- a/src/Elastic.Markdown/Slices/HtmlWriter.cs +++ b/src/Elastic.Markdown/Slices/HtmlWriter.cs @@ -39,8 +39,8 @@ public async Task RenderLayout(MarkdownFile markdown, MarkdownDocument d var previous = DocumentationSet.GetPrevious(markdown); var next = DocumentationSet.GetNext(markdown); - var remote = DocumentationSet.Context.Git.RepositoryName; - var branch = DocumentationSet.Context.Git.Branch; + var remote = DocumentationSet.Build.Git.RepositoryName; + var branch = DocumentationSet.Build.Git.Branch; var path = Path.Combine(DocumentationSet.RelativeSourcePath, markdown.RelativePath); var editUrl = $"https://github.com/elastic/{remote}/edit/{branch}/{path}"; @@ -58,7 +58,7 @@ public async Task RenderLayout(MarkdownFile markdown, MarkdownDocument d UrlPathPrefix = markdown.UrlPathPrefix, Applies = markdown.YamlFrontMatter?.AppliesTo, GithubEditUrl = editUrl, - AllowIndexing = DocumentationSet.Context.AllowIndexing && !markdown.Hidden + AllowIndexing = DocumentationSet.Build.AllowIndexing && !markdown.Hidden }); return await slice.RenderAsync(cancellationToken: ctx); } diff --git a/src/docs-builder/Http/ReloadGeneratorService.cs b/src/docs-builder/Http/ReloadGeneratorService.cs index 8045e8fec..689d4290c 100644 --- a/src/docs-builder/Http/ReloadGeneratorService.cs +++ b/src/docs-builder/Http/ReloadGeneratorService.cs @@ -22,7 +22,7 @@ public async Task StartAsync(Cancel cancellationToken) { await ReloadableGenerator.ReloadAsync(cancellationToken); - var watcher = new FileSystemWatcher(ReloadableGenerator.Generator.DocumentationSet.SourcePath.FullName) + var watcher = new FileSystemWatcher(ReloadableGenerator.Generator.DocumentationSet.SourceDirectory.FullName) { NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime diff --git a/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs b/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs index 0ff354cf9..f0421e934 100644 --- a/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs @@ -70,7 +70,7 @@ protected DirectiveTest(ITestOutputHelper output, [LanguageInjection("markdown") var context = new BuildContext(Collector, FileSystem); var linkResolver = new TestCrossLinkResolver(); Set = new DocumentationSet(context, logger, linkResolver); - File = Set.GetMarkdownFile(FileSystem.FileInfo.New("docs/index.md")) ?? throw new NullReferenceException(); + File = Set.DocumentationFileLookup(FileSystem.FileInfo.New("docs/index.md")) ?? throw new NullReferenceException(); Html = default!; //assigned later Document = default!; } diff --git a/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs index e3613cedd..2ef781cd7 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs @@ -114,7 +114,7 @@ protected InlineTest( }; var linkResolver = new TestCrossLinkResolver(); Set = new DocumentationSet(context, logger, linkResolver); - File = Set.GetMarkdownFile(FileSystem.FileInfo.New("docs/index.md")) ?? throw new NullReferenceException(); + File = Set.DocumentationFileLookup(FileSystem.FileInfo.New("docs/index.md")) ?? throw new NullReferenceException(); Html = default!; //assigned later Document = default!; } From 9308d5744ecb59133b19da2fbf6a9ffcf9c46cdf Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 26 Feb 2025 11:46:27 +0100 Subject: [PATCH 7/8] fix recursive dependency issue when calculating hashcode for markdown file because it held on to the complete Configuration --- .../IO/Configuration/ConfigurationFile.cs | 4 +--- src/Elastic.Markdown/IO/MarkdownFile.cs | 12 ++++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs b/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs index 46a1e75da..580b2e6ba 100644 --- a/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs +++ b/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs @@ -13,7 +13,6 @@ namespace Elastic.Markdown.IO.Configuration; public record ConfigurationFile : DocumentationFile { - private readonly IFileInfo _sourceFile; private readonly IDirectoryInfo _rootPath; private readonly BuildContext _context; private readonly int _depth; @@ -36,7 +35,6 @@ public record ConfigurationFile : DocumentationFile public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildContext context, int depth = 0, string parentPath = "") : base(sourceFile, rootPath) { - _sourceFile = sourceFile; _rootPath = rootPath; _context = context; _depth = depth; @@ -52,7 +50,7 @@ public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildCon var redirectFile = new RedirectFile(redirectFileInfo, _context); Redirects = redirectFile.Redirects; - var reader = new YamlStreamReader(_sourceFile, _context); + var reader = new YamlStreamReader(sourceFile, _context); try { foreach (var entry in reader.Read()) diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 9e4a79dfd..2f4d78520 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -24,6 +24,10 @@ public record MarkdownFile : DocumentationFile private readonly DocumentationSet _set; + private readonly IFileInfo _configurationFile; + + private readonly IReadOnlyDictionary _globalSubstitutions; + public MarkdownFile( IFileInfo sourceFile, IDirectoryInfo rootPath, @@ -38,14 +42,14 @@ DocumentationSet set UrlPathPrefix = build.UrlPathPrefix; MarkdownParser = parser; Collector = build.Collector; - Configuration = build.Configuration; + _configurationFile = build.Configuration.SourceFile; + _globalSubstitutions = build.Configuration.Substitutions; _set = set; } public string Id { get; } = Guid.NewGuid().ToString("N")[..8]; private DiagnosticsCollector Collector { get; } - private ConfigurationFile Configuration { get; } public DocumentationGroup? Parent { @@ -142,7 +146,7 @@ private void ValidateAnchorRemapping() if (Anchors.Contains(v)) continue; - Collector.EmitError(Configuration.SourceFile.FullName, $"Bad anchor remap '{v}' does not exist in {RelativePath}"); + Collector.EmitError(_configurationFile.FullName, $"Bad anchor remap '{v}' does not exist in {RelativePath}"); } } @@ -165,7 +169,7 @@ public async Task ParseFullAsync(Cancel ctx) private IReadOnlyDictionary GetSubstitutions() { - var globalSubstitutions = Configuration.Substitutions; + var globalSubstitutions = _globalSubstitutions; var fileSubstitutions = YamlFrontMatter?.Properties; if (fileSubstitutions is not { Count: >= 0 }) return globalSubstitutions; From 7f205d5c663f49139179b4fa5354cbf4ec1018c3 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 26 Feb 2025 11:47:59 +0100 Subject: [PATCH 8/8] cleanup namespaces --- src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs | 1 - src/Elastic.Markdown/IO/Configuration/RedirectFile.cs | 1 - src/Elastic.Markdown/IO/DocumentationSet.cs | 1 - src/Elastic.Markdown/IO/MarkdownFile.cs | 1 - src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs | 5 ----- src/docs-assembler/Links/LinkIndexLinkChecker.cs | 3 +-- tests/Elastic.Markdown.Tests/Directives/TabTests.cs | 2 -- tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs | 1 - 8 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs b/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs index 580b2e6ba..081252678 100644 --- a/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs +++ b/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs @@ -6,7 +6,6 @@ using DotNet.Globbing; using Elastic.Markdown.Diagnostics; using Elastic.Markdown.IO.State; -using YamlDotNet.Core; using YamlDotNet.RepresentationModel; namespace Elastic.Markdown.IO.Configuration; diff --git a/src/Elastic.Markdown/IO/Configuration/RedirectFile.cs b/src/Elastic.Markdown/IO/Configuration/RedirectFile.cs index 45d502ce5..1b84024a1 100644 --- a/src/Elastic.Markdown/IO/Configuration/RedirectFile.cs +++ b/src/Elastic.Markdown/IO/Configuration/RedirectFile.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information using System.IO.Abstractions; -using Elastic.Markdown.Diagnostics; using Elastic.Markdown.IO.State; using YamlDotNet.RepresentationModel; diff --git a/src/Elastic.Markdown/IO/DocumentationSet.cs b/src/Elastic.Markdown/IO/DocumentationSet.cs index a2ab7c96e..6d7313c9c 100644 --- a/src/Elastic.Markdown/IO/DocumentationSet.cs +++ b/src/Elastic.Markdown/IO/DocumentationSet.cs @@ -8,7 +8,6 @@ using Elastic.Markdown.Diagnostics; using Elastic.Markdown.IO.Configuration; using Elastic.Markdown.IO.Navigation; -using Elastic.Markdown.IO.State; using Elastic.Markdown.Myst; using Microsoft.Extensions.Logging; diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 2f4d78520..94c57e716 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -5,7 +5,6 @@ using System.IO.Abstractions; using Elastic.Markdown.Diagnostics; using Elastic.Markdown.Helpers; -using Elastic.Markdown.IO.Configuration; using Elastic.Markdown.IO.Navigation; using Elastic.Markdown.Myst; using Elastic.Markdown.Myst.Directives; diff --git a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs index 45552e332..9afddc444 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -2,12 +2,7 @@ // 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 System.IO.Abstractions; -using Elastic.Markdown.CrossLinks; using Elastic.Markdown.Diagnostics; -using Elastic.Markdown.IO; -using Elastic.Markdown.IO.Configuration; -using Elastic.Markdown.Myst.FrontMatter; namespace Elastic.Markdown.Myst.Directives; diff --git a/src/docs-assembler/Links/LinkIndexLinkChecker.cs b/src/docs-assembler/Links/LinkIndexLinkChecker.cs index 9048f14e4..7964ce201 100644 --- a/src/docs-assembler/Links/LinkIndexLinkChecker.cs +++ b/src/docs-assembler/Links/LinkIndexLinkChecker.cs @@ -2,14 +2,12 @@ // 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 System.Collections.Frozen; using Actions.Core.Services; using Elastic.Documentation.Tooling.Diagnostics.Console; using Elastic.Markdown.CrossLinks; using Elastic.Markdown.IO; using Elastic.Markdown.IO.State; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Documentation.Assembler.Links; @@ -36,6 +34,7 @@ Cancel ctx { var fetcher = new LinksIndexCrossLinkFetcher(logger); var resolver = new CrossLinkResolver(fetcher); + // ReSharper disable once RedundantAssignment var crossLinks = await resolver.FetchLinks(); if (string.IsNullOrEmpty(repository)) diff --git a/tests/Elastic.Markdown.Tests/Directives/TabTests.cs b/tests/Elastic.Markdown.Tests/Directives/TabTests.cs index 05fb0732b..2ef932f47 100644 --- a/tests/Elastic.Markdown.Tests/Directives/TabTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/TabTests.cs @@ -47,9 +47,7 @@ public void ParsesTabItems() var items = Block!.OfType().ToArray(); items.Should().NotBeNull().And.HaveCount(3); for (var i = 0; i < items.Length; i++) - { items[i].Index.Should().Be(i); - } } } diff --git a/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs b/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs index 3a3a229db..e11aadfcf 100644 --- a/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs +++ b/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs @@ -2,7 +2,6 @@ // 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 System.IO.Abstractions.TestingHelpers; -using Elastic.Markdown.Diagnostics; using Elastic.Markdown.IO; using FluentAssertions;