diff --git a/docs/testing/nested/index.md b/docs/testing/nested/index.md index 213abe12b..3c65e38ad 100644 --- a/docs/testing/nested/index.md +++ b/docs/testing/nested/index.md @@ -1,3 +1,12 @@ +--- +sub: + x: "Variable" +--- # Testing Nesting -The files in this directory are used for testing purposes. Do not edit these files unless you are working on tests. \ No newline at end of file +The files in this directory are used for testing purposes. Do not edit these files unless you are working on tests. + + +## Injecting a {{x}} is supported in headers. + +This should show up in the file's table of contents too. diff --git a/src/Elastic.Markdown/Helpers/Interpolation.cs b/src/Elastic.Markdown/Helpers/Interpolation.cs index 8ccc23156..9714ad0de 100644 --- a/src/Elastic.Markdown/Helpers/Interpolation.cs +++ b/src/Elastic.Markdown/Helpers/Interpolation.cs @@ -14,13 +14,17 @@ internal static partial class InterpolationRegex public static class Interpolation { - public static bool ReplaceSubstitutions(this ReadOnlySpan span, Dictionary? properties, out string? replacement) + public static bool ReplaceSubstitutions(this ReadOnlySpan span, IReadOnlyDictionary? properties, out string? replacement) { replacement = null; if (span.IndexOf("}}") < 0) return false; - var substitutions = properties ?? new(); + if (properties is null || properties.Count == 0) + return false; + + var substitutions = properties as Dictionary + ?? new Dictionary(properties, StringComparer.OrdinalIgnoreCase); if (substitutions.Count == 0) return false; diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 13cf948e6..89ef23efb 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -13,6 +13,7 @@ using Markdig; using Markdig.Extensions.Yaml; using Markdig.Syntax; +using YamlDotNet.Serialization; namespace Elastic.Markdown.IO; @@ -106,70 +107,54 @@ public async Task ParseFullAsync(Cancel ctx) return document; } - private void ReadDocumentInstructions(MarkdownDocument document) + private IReadOnlyDictionary GetSubstitutions() { + var globalSubstitutions = MarkdownParser.Configuration.Substitutions; + var fileSubstitutions = YamlFrontMatter?.Properties; + if (fileSubstitutions is not { Count: >= 0 }) + return globalSubstitutions; + + var allProperties = new Dictionary(fileSubstitutions); + foreach (var (key, value) in globalSubstitutions) + allProperties[key] = value; + return allProperties; + } + private void ReadDocumentInstructions(MarkdownDocument document) + { Title = document .FirstOrDefault(block => block is HeadingBlock { Level: 1 })? .GetData("header") as string; - if (document.FirstOrDefault() is YamlFrontMatterBlock yaml) - { - var raw = string.Join(Environment.NewLine, yaml.Lines.Lines); - YamlFrontMatter = ReadYamlFrontMatter(raw); - - // TODO remove when migration tool and our demo content sets are updated - var deprecatedTitle = YamlFrontMatter.Title; - if (!string.IsNullOrEmpty(deprecatedTitle)) - { - Collector.EmitWarning(FilePath, "'title' is no longer supported in yaml frontmatter please use a level 1 header instead."); - // TODO remove fallback once migration is over and we fully deprecate front matter titles - if (string.IsNullOrEmpty(Title)) - Title = deprecatedTitle; - } + YamlFrontMatter = ProcessYamlFrontMatter(document); + NavigationTitle = YamlFrontMatter.NavigationTitle; + var subs = GetSubstitutions(); - // set title on yaml front matter manually. - // frontmatter gets passed around as page information throughout - YamlFrontMatter.Title = Title; - - NavigationTitle = YamlFrontMatter.NavigationTitle; - if (!string.IsNullOrEmpty(NavigationTitle)) - { - var props = MarkdownParser.Configuration.Substitutions; - var properties = YamlFrontMatter.Properties; - if (properties is { Count: >= 0 } local) - { - var allProperties = new Dictionary(local); - foreach (var (key, value) in props) - allProperties[key] = value; - if (NavigationTitle.AsSpan().ReplaceSubstitutions(allProperties, out var replacement)) - NavigationTitle = replacement; - } - else - { - if (NavigationTitle.AsSpan().ReplaceSubstitutions(properties, out var replacement)) - NavigationTitle = replacement; - } - } + if (!string.IsNullOrEmpty(NavigationTitle)) + { + if (NavigationTitle.AsSpan().ReplaceSubstitutions(subs, out var replacement)) + NavigationTitle = replacement; } - else - YamlFrontMatter = new YamlFrontMatter { Title = Title }; if (string.IsNullOrEmpty(Title)) { Title = RelativePath; Collector.EmitWarning(FilePath, "Document has no title, using file name as title."); } + else if (Title.AsSpan().ReplaceSubstitutions(subs, out var replacement)) + Title = replacement; var contents = document .Descendants() .Where(block => block is { Level: >= 2 }) .Select(h => (h.GetData("header") as string, h.GetData("anchor") as string)) - .Select(h => new PageTocItem + .Select(h => { - Heading = h.Item1!.StripMarkdown(), - Slug = (h.Item2 ?? h.Item1).Slugify() + var header = h.Item1!.StripMarkdown(); + if (header.AsSpan().ReplaceSubstitutions(subs, out var replacement)) + header = replacement; + return new PageTocItem { Heading = header!, Slug = (h.Item2 ?? header).Slugify() }; }) .ToList(); @@ -192,6 +177,29 @@ private void ReadDocumentInstructions(MarkdownDocument document) _instructionsParsed = true; } + private YamlFrontMatter ProcessYamlFrontMatter(MarkdownDocument document) + { + if (document.FirstOrDefault() is not YamlFrontMatterBlock yaml) + return new YamlFrontMatter { Title = Title }; + + var raw = string.Join(Environment.NewLine, yaml.Lines.Lines); + var fm = ReadYamlFrontMatter(raw); + + // TODO remove when migration tool and our demo content sets are updated + var deprecatedTitle = fm.Title; + if (!string.IsNullOrEmpty(deprecatedTitle)) + { + Collector.EmitWarning(FilePath, "'title' is no longer supported in yaml frontmatter please use a level 1 header instead."); + // TODO remove fallback once migration is over and we fully deprecate front matter titles + if (string.IsNullOrEmpty(Title)) + Title = deprecatedTitle; + } + // set title on yaml front matter manually. + // frontmatter gets passed around as page information throughout + fm.Title = Title; + return fm; + } + private YamlFrontMatter ReadYamlFrontMatter(string raw) { try