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.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/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/Diagnostics/ProcessorDiagnosticExtensions.cs b/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs index 2f92b6f2f..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().Path.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().Path.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.Path.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.Path.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().Path.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().Path.FullName, + File = processor.GetContext().MarkdownSourcePath.FullName, Column = column, Line = line, Message = message, 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/Configuration/ConfigurationFile.cs b/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs index 46a1e75da..081252678 100644 --- a/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs +++ b/src/Elastic.Markdown/IO/Configuration/ConfigurationFile.cs @@ -6,14 +6,12 @@ using DotNet.Globbing; using Elastic.Markdown.Diagnostics; using Elastic.Markdown.IO.State; -using YamlDotNet.Core; using YamlDotNet.RepresentationModel; 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 +34,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 +49,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/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 1ee5e863d..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; @@ -16,13 +15,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 +29,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.SourcePath; - OutputPath = context.OutputPath; - 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 +81,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 +90,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 +118,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 +142,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 +188,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 +218,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 5bac78c60..94c57e716 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -23,21 +23,26 @@ public record MarkdownFile : DocumentationFile private readonly DocumentationSet _set; + private readonly IFileInfo _configurationFile; + + private readonly IReadOnlyDictionary _globalSubstitutions; + 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; + _configurationFile = build.Configuration.SourceFile; + _globalSubstitutions = build.Configuration.Substitutions; _set = set; } @@ -94,7 +99,6 @@ public string? NavigationTitle private bool _instructionsParsed; private DocumentationGroup? _parent; private string? _title; - private readonly IFileInfo _configurationFile; public MarkdownFile[] YieldParents() { @@ -164,7 +168,7 @@ public async Task ParseFullAsync(Cancel ctx) private IReadOnlyDictionary GetSubstitutions() { - var globalSubstitutions = MarkdownParser.Configuration.Substitutions; + var globalSubstitutions = _globalSubstitutions; var fileSubstitutions = YamlFrontMatter?.Properties; if (fileSubstitutions is not { Count: >= 0 }) return globalSubstitutions; @@ -230,9 +234,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/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/CodeBlocks/EnhancedCodeBlock.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs index 54971b050..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.Path; + 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 ca142dcc8..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.Path; + 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 ae2a2315b..89b18c96a 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,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.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(); var html = document.ToHtml(MarkdownParser.Pipeline); _ = renderer.Write(html); } @@ -238,12 +237,9 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc 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.Build, block.Context); - var file = block.FileSystem.FileInfo.New(block.IncludePath); + var file = block.Build.ReadFileSystem.FileInfo.New(block.IncludePath); YamlSettings? settings; try @@ -267,7 +263,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.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 ed72eb275..8e132642a 100644 --- a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs @@ -88,9 +88,9 @@ private void ExtractImageUrl(ParserContext context) return; } - var includeFrom = context.Path.Directory!.FullName; + var includeFrom = context.MarkdownSourcePath.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 da258b790..9afddc444 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -1,12 +1,8 @@ // 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; -using Elastic.Markdown.IO; -using Elastic.Markdown.IO.Configuration; -using Elastic.Markdown.Myst.FrontMatter; namespace Elastic.Markdown.Myst.Directives; @@ -22,20 +18,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 DocumentationSourcePath { get; } = context.Parser.SourcePath; - - public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter; + public ParserContext Context { get; } = context; public string? IncludePath { get; private set; } - public string? IncludePathFromSourceDirectory { get; private set; } + + public string? IncludePathRelativeToSource { get; private set; } public bool Found { get; private set; } @@ -65,13 +52,14 @@ private void ExtractInclusionPath(ParserContext context) return; } - var includeFrom = context.Path.Directory!.FullName; + var includeFrom = context.MarkdownSourcePath.Directory!.FullName; if (includePath.StartsWith('/')) - includeFrom = DocumentationSourcePath.FullName; + includeFrom = Build.DocumentationSourceDirectory.FullName; IncludePath = Path.Combine(includeFrom, includePath.TrimStart('/')); - IncludePathFromSourceDirectory = Path.GetRelativePath(DocumentationSourcePath.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."); @@ -81,7 +69,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) { @@ -89,7 +77,7 @@ private void ExtractInclusionPath(ParserContext context) Found = false; } - if (file.FullName == context.Path.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 e659b323e..1cf37ed29 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 ConfigurationFile Configuration { get; } = context.Configuration; - - public ICrossLinkResolver LinksResolver { get; } = context.LinksResolver; - - public IFileSystem FileSystem { get; } = context.Build.ReadFileSystem; - - public IFileInfo IncludeFrom { get; } = context.Path; - - public IDirectoryInfo DocumentationSourcePath { get; } = context.Parser.SourcePath; - - public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter; + public ParserContext Context { get; } = context; public string? IncludePath { get; private set; } + public IFileInfo IncludeFrom { get; } = context.MarkdownSourcePath; + public bool Found { get; private set; } @@ -46,12 +32,12 @@ private void ExtractInclusionPath(ParserContext context) return; } - var includeFrom = context.Path.Directory!.FullName; + var includeFrom = context.MarkdownSourcePath.Directory!.FullName; if (includePath.StartsWith('/')) - includeFrom = DocumentationSourcePath.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/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 881f27cbc..a451c2007 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -55,6 +55,8 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) return match; var context = processor.GetContext(); + link.SetData(nameof(context.CurrentUrlPath), context.CurrentUrlPath); + if (IsInCommentBlock(link) || context.SkipValidation) return match; @@ -160,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(); } @@ -182,8 +184,8 @@ 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.Build.DocumentationSourceDirectory.FullName + : context.MarkdownSourcePath.Directory!.FullName; private static void ValidateInternalUrl(InlineProcessor processor, string url, string includeFrom, LinkInline link, ParserContext context) { @@ -200,7 +202,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.DocumentationFileLookup(file) as MarkdownFile; if (markdown == null && link.FirstChild == null) { @@ -225,10 +227,10 @@ private static void ProcessLinkText(InlineProcessor processor, LinkInline link, private static IFileInfo ResolveFile(ParserContext context, string url) => string.IsNullOrWhiteSpace(url) - ? context.Path + ? context.MarkdownSourcePath : 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.Build.DocumentationSourceDirectory.FullName, url.TrimStart('/'))) + : 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 fbc257b91..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,23 +18,90 @@ namespace Elastic.Markdown.Myst; -public class MarkdownParser( - IDirectoryInfo sourcePath, - BuildContext context, - Func? getDocumentationFile, - ConfigurationFile configuration, - ICrossLinkResolver linksResolver - ) +public class MarkdownParser(BuildContext build, IParserResolvers resolvers) { - public IDirectoryInfo SourcePath { get; } = sourcePath; + private BuildContext Build { get; } = build; + private IParserResolvers Resolvers { get; } = resolvers; - private BuildContext Context { get; } = context; + public Task MinimalParseAsync(IFileInfo path, Cancel ctx) + { + var state = new ParserState(Build) + { + MarkdownSourcePath = path, + YamlFrontMatter = null, + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver, + SkipValidation = true + }; + var context = new ParserContext(state); + return ParseAsync(path, context, MinimalPipeline, ctx); + } - private ICrossLinkResolver LinksResolver { get; } = linksResolver; + public Task ParseAsync(IFileInfo path, YamlFrontMatter? matter, Cancel ctx) + { + var state = new ParserState(Build) + { + MarkdownSourcePath = path, + YamlFrontMatter = matter, + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver + }; + var context = new ParserContext(state); + return ParseAsync(path, context, Pipeline, ctx); + } + + public Task ParseSnippetAsync(IFileInfo path, IFileInfo parentPath, YamlFrontMatter? matter, Cancel ctx) + { + var state = new ParserState(Build) + { + MarkdownSourcePath = path, + YamlFrontMatter = matter, + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver, + 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) + { + MarkdownSourcePath = path, + YamlFrontMatter = matter, + DocumentationFileLookup = Resolvers.DocumentationFileLookup, + CrossLinkResolver = Resolvers.CrossLinkResolver + }; + var context = new ParserContext(state); + var markdownDocument = Markdig.Markdown.Parse(markdown, Pipeline, context); + return markdownDocument; + } + + private static async Task ParseAsync( + IFileInfo path, + MarkdownParserContext context, + MarkdownPipeline pipeline, + Cancel ctx) + { + if (path.FileSystem is FileSystem) + { + //real IO optimize through UTF8 stream reader. + await using var streamReader = new Utf8StreamReader(path.FullName, fileOpenMode: FileOpenMode.Throughput); + var inputMarkdown = await streamReader.AsTextReader().ReadToEndAsync(ctx); + var markdownDocument = Markdig.Markdown.Parse(inputMarkdown, pipeline, context); + return markdownDocument; + } + else + { + var inputMarkdown = await path.FileSystem.File.ReadAllTextAsync(path.FullName, ctx); + var markdownDocument = Markdig.Markdown.Parse(inputMarkdown, pipeline, context); + return markdownDocument; + } + } // ReSharper disable once InconsistentNaming private static MarkdownPipeline? MinimalPipelineCached; - private static MarkdownPipeline MinimalPipeline { get @@ -53,7 +117,6 @@ private static MarkdownPipeline MinimalPipeline _ = builder.BlockParsers.TryRemove(); MinimalPipelineCached = builder.Build(); return MinimalPipelineCached; - } } @@ -90,56 +153,4 @@ public static MarkdownPipeline Pipeline } } - public ConfigurationFile Configuration { get; } = configuration; - - public Task MinimalParseAsync(IFileInfo path, Cancel ctx) - { - var context = new ParserContext(this, path, null, Context, Configuration, LinksResolver) - { - SkipValidation = true, - GetDocumentationFile = getDocumentationFile - }; - 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 - }; - return ParseAsync(path, context, Pipeline, ctx); - } - - private static async Task ParseAsync( - IFileInfo path, - MarkdownParserContext context, - MarkdownPipeline pipeline, - Cancel ctx) - { - if (path.FileSystem is FileSystem) - { - //real IO optimize through UTF8 stream reader. - await using var streamReader = new Utf8StreamReader(path.FullName, fileOpenMode: FileOpenMode.Throughput); - var inputMarkdown = await streamReader.AsTextReader().ReadToEndAsync(ctx); - var markdownDocument = Markdig.Markdown.Parse(inputMarkdown, pipeline, context); - return markdownDocument; - } - else - { - var inputMarkdown = await path.FileSystem.File.ReadAllTextAsync(path.FullName, ctx); - var markdownDocument = Markdig.Markdown.Parse(inputMarkdown, pipeline, context); - return markdownDocument; - } - } - - public MarkdownDocument Parse(string yaml, IFileInfo parent, YamlFrontMatter? matter) - { - var context = new ParserContext(this, parent, matter, Context, Configuration, LinksResolver) - { - GetDocumentationFile = 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..c025ea03c 100644 --- a/src/Elastic.Markdown/Myst/ParserContext.cs +++ b/src/Elastic.Markdown/Myst/ParserContext.cs @@ -24,32 +24,70 @@ processor.Context as ParserContext ?? throw new InvalidOperationException($"Provided context is not a {nameof(ParserContext)}"); } -public class ParserContext : MarkdownParserContext +public interface IParserResolvers { - public ParserContext( - MarkdownParser markdownParser, - IFileInfo path, - YamlFrontMatter? frontMatter, - BuildContext context, - ConfigurationFile configuration, - ICrossLinkResolver linksResolver) + 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 IFileInfo? ParentMarkdownPath { get; init; } + public bool SkipValidation { get; init; } +} + +public class ParserContext : MarkdownParserContext, IParserResolvers +{ + public ConfigurationFile Configuration { get; } + public ICrossLinkResolver CrossLinkResolver { get; } + public IFileInfo MarkdownSourcePath { get; } + public string CurrentUrlPath { get; } + public YamlFrontMatter? YamlFrontMatter { get; } + public BuildContext Build { get; } + public bool SkipValidation { get; } + public Func DocumentationFileLookup { get; } + public IReadOnlyDictionary Substitutions { get; } + public IReadOnlyDictionary ContextSubstitutions { get; } + + public ParserContext(ParserState state) { - Parser = markdownParser; - Path = path; - FrontMatter = frontMatter; - Build = context; - Configuration = configuration; - LinksResolver = linksResolver; - - if (frontMatter?.Properties is not { Count: > 0 }) - Substitutions = configuration.Substitutions; + Build = state.Build; + Configuration = state.Configuration; + YamlFrontMatter = state.YamlFrontMatter; + SkipValidation = state.SkipValidation; + + CrossLinkResolver = state.CrossLinkResolver; + MarkdownSourcePath = state.MarkdownSourcePath; + DocumentationFileLookup = state.DocumentationFileLookup; + var parentPath = state.ParentMarkdownPath; + + CurrentUrlPath = DocumentationFileLookup(parentPath ?? MarkdownSourcePath) is MarkdownFile md + ? md.Url + : SkipValidation + ? string.Empty + : throw new Exception($"Unable to find documentation file for {(parentPath ?? MarkdownSourcePath).FullName}"); + + if (YamlFrontMatter?.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 YamlFrontMatter.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; @@ -60,21 +98,9 @@ public ParserContext( var contextSubs = new Dictionary(); - if (frontMatter?.Title is { } title) + if (YamlFrontMatter?.Title is { } title) contextSubs["context.page_title"] = title; ContextSubstitutions = contextSubs; } - - public ConfigurationFile Configuration { get; } - public ICrossLinkResolver LinksResolver { get; } - public MarkdownParser Parser { get; } - public IFileInfo Path { 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/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs index fd9280d78..094bd432b 100644 --- a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs +++ b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs @@ -16,6 +16,13 @@ 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) + { + base.Write(renderer, link); + return; + } + _ = renderer.Write(""); } else - { base.Write(renderer, link); - } } } 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-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/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 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/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/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!; } 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;