diff --git a/.editorconfig b/.editorconfig index c4395e55..be408627 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,7 @@ spelling_exclusion_path = "./build/exclusion.dic" [*.{fs,fsx,cs,vb}] file_header_template=Licensed to Elasticsearch B.V under one or more agreements.\nElasticsearch B.V licenses this file to you under the Apache 2.0 License.\nSee the LICENSE file in the project root for more information +max_line_length = 160 [*.{fs,fsx}] indent_style = space diff --git a/src/Elastic.Markdown/Myst/Directives/AdmonitionBlock.cs b/src/Elastic.Markdown/Myst/Directives/AdmonitionBlock.cs index 092c37e0..60d8eea3 100644 --- a/src/Elastic.Markdown/Myst/Directives/AdmonitionBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/AdmonitionBlock.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information namespace Elastic.Markdown.Myst.Directives; -public class AdmonitionBlock( - DirectiveBlockParser parser, - string admonition, - Dictionary properties, - ParserContext context) - : DirectiveBlock(parser, properties, context) +public class DropdownBlock(DirectiveBlockParser parser, ParserContext context) : AdmonitionBlock(parser, "admonition", context); + +public class AdmonitionBlock(DirectiveBlockParser parser, string admonition, ParserContext context) + : DirectiveBlock(parser, context) { public string Admonition => admonition == "admonition" ? Classes?.Trim() ?? "note" : admonition; @@ -33,7 +31,7 @@ public string Title public override void FinalizeAndValidate(ParserContext context) { - CrossReferenceName = Properties.GetValueOrDefault("name"); + CrossReferenceName = Prop("name"); DropdownOpen = TryPropBool("open"); if (DropdownOpen.HasValue) Classes = "dropdown"; @@ -41,5 +39,3 @@ public override void FinalizeAndValidate(ParserContext context) } -public class DropdownBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : AdmonitionBlock(parser, "admonition", properties, context); diff --git a/src/Elastic.Markdown/Myst/Directives/AppliesBlock.cs b/src/Elastic.Markdown/Myst/Directives/AppliesBlock.cs index df909dbb..d3274c51 100644 --- a/src/Elastic.Markdown/Myst/Directives/AppliesBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/AppliesBlock.cs @@ -8,8 +8,7 @@ namespace Elastic.Markdown.Myst.Directives; -public class AppliesBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : DirectiveBlock(parser, properties, context) +public class AppliesBlock(DirectiveBlockParser parser, ParserContext context) : DirectiveBlock(parser, context) { public override string Directive => "mermaid"; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs index 4b2bf517..edaf2a1a 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs @@ -32,16 +32,14 @@ public interface IBlockExtension : IBlock /// Initializes a new instance of the class. /// /// The parser used to create this block. -/// /// public abstract class DirectiveBlock( DirectiveBlockParser parser, - Dictionary properties, - ParserContext context - ) + ParserContext context) : ContainerBlock(parser), IFencedBlock, IBlockExtension { - protected IReadOnlyDictionary Properties { get; } = properties; + private Dictionary? _properties; + protected IReadOnlyDictionary? Properties => _properties; public BuildContext Build { get; } = context.Build; @@ -97,8 +95,15 @@ ParserContext context /// public abstract void FinalizeAndValidate(ParserContext context); - protected bool PropBool(params string[] keys) - { + internal void AddProperty(string key, string value) + { + _properties ??= new Dictionary(); + _properties[key] = value; + } + + protected bool PropBool(params string[] keys) + { + if (Properties is null) return false; var value = Prop(keys); if (string.IsNullOrEmpty(value)) return keys.Any(k => Properties.ContainsKey(k)); @@ -108,6 +113,7 @@ protected bool PropBool(params string[] keys) protected bool? TryPropBool(params string[] keys) { + if (Properties is null) return null; var value = Prop(keys); if (string.IsNullOrEmpty(value)) return keys.Any(k => Properties.ContainsKey(k)) ? true : null; @@ -118,6 +124,7 @@ protected bool PropBool(params string[] keys) protected string? Prop(params string[] keys) { + if (Properties is null) return null; foreach (var key in keys) { if (Properties.TryGetValue(key, out var value)) diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs index 4b5a2727..e7f2f48b 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs @@ -28,8 +28,6 @@ public DirectiveBlockParser() InfoPrefix = null; } - private Dictionary _admonitionData = new(); - private readonly string[] _admonitions = [ "important", "warning", "note", "tip" ]; private readonly string[] _versionBlocks = [ "versionadded", "versionchanged", "versionremoved", "deprecated" ]; @@ -67,7 +65,6 @@ public DirectiveBlockParser() protected override DirectiveBlock CreateFencedBlock(BlockProcessor processor) { - _admonitionData = new Dictionary(); var info = processor.Line.AsSpan(); if (processor.Context is not ParserContext context) @@ -76,57 +73,57 @@ protected override DirectiveBlock CreateFencedBlock(BlockProcessor processor) // TODO alternate lookup .NET 9 var directive = info.ToString().Trim(['{', '}', '`']); if (_unsupportedBlocks.TryGetValue(directive, out var issueId)) - return new UnsupportedDirectiveBlock(this, directive, _admonitionData, issueId, context); + return new UnsupportedDirectiveBlock(this, directive, issueId, context); if (info.IndexOf("{tab-set}") > 0) - return new TabSetBlock(this, _admonitionData, context); + return new TabSetBlock(this, context); if (info.IndexOf("{tab-item}") > 0) - return new TabItemBlock(this, _admonitionData, context); + return new TabItemBlock(this, context); if (info.IndexOf("{dropdown}") > 0) - return new DropdownBlock(this, _admonitionData, context); + return new DropdownBlock(this, context); if (info.IndexOf("{image}") > 0) - return new ImageBlock(this, _admonitionData, context); + return new ImageBlock(this, context); if (info.IndexOf("{figure}") > 0) - return new FigureBlock(this, _admonitionData, context); + return new FigureBlock(this, context); if (info.IndexOf("{figure-md}") > 0) - return new FigureBlock(this, _admonitionData, context); + return new FigureBlock(this, context); // this is currently listed as unsupported // leaving the parsing in until we are confident we don't want this // for dev-docs if (info.IndexOf("{mermaid}") > 0) - return new MermaidBlock(this, _admonitionData, context); + return new MermaidBlock(this, context); if (info.IndexOf("{include}") > 0) - return new IncludeBlock(this, _admonitionData, context); + return new IncludeBlock(this, context); if (info.IndexOf("{literalinclude}") > 0) - return new LiteralIncludeBlock(this, _admonitionData, context); + return new LiteralIncludeBlock(this, context); if (info.IndexOf("{applies}") > 0) - return new AppliesBlock(this, _admonitionData, context); + return new AppliesBlock(this, context); if (info.IndexOf("{settings}") > 0) - return new SettingsBlock(this, _admonitionData, context); + return new SettingsBlock(this, context); foreach (var admonition in _admonitions) { if (info.IndexOf($"{{{admonition}}}") > 0) - return new AdmonitionBlock(this, admonition, _admonitionData, context); + return new AdmonitionBlock(this, admonition, context); } foreach (var version in _versionBlocks) { if (info.IndexOf($"{{{version}}}") > 0) - return new VersionBlock(this, version, _admonitionData, context); + return new VersionBlock(this, version, context); } - return new UnknownDirectiveBlock(this, info.ToString(), _admonitionData, context); + return new UnknownDirectiveBlock(this, info.ToString(), context); } public override bool Close(BlockProcessor processor, Block block) @@ -139,7 +136,7 @@ public override bool Close(BlockProcessor processor, Block block) public override BlockState TryOpen(BlockProcessor processor) { - if (processor.Context is not ParserContext context) + if (processor.Context is not ParserContext) throw new Exception("Expected parser context to be of type ParserContext"); // We expect no indentation for a fenced code block. @@ -164,19 +161,21 @@ public override BlockState TryContinue(BlockProcessor processor, Block block) { var line = processor.Line.AsSpan(); - // TODO only parse this if no content proceeds it (and not in a code fence) - if (line.StartsWith(":")) - { - var tokens = line.ToString().Split(':', 3, RemoveEmptyEntries | TrimEntries); - if (tokens.Length < 1) - return base.TryContinue(processor, block); - - var name = tokens[0]; - var data = tokens.Length > 1 ? string.Join(":", tokens[1..]) : string.Empty; - _admonitionData[name] = data; - return BlockState.Continue; - } + if (!line.StartsWith(":")) + return base.TryContinue(processor, block); + + if (block is not DirectiveBlock directiveBlock) + return base.TryContinue(processor, block); + + var tokens = line.ToString().Split(':', 3, RemoveEmptyEntries | TrimEntries); + if (tokens.Length < 1) + return base.TryContinue(processor, block); + + var name = tokens[0]; + var data = tokens.Length > 1 ? string.Join(":", tokens[1..]) : string.Empty; + directiveBlock.AddProperty(name, data); + + return BlockState.Continue; - return base.TryContinue(processor, block); } } diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 4e68d17b..75e4412f 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -162,18 +162,6 @@ private void WriteDropdown(HtmlRenderer renderer, DropdownBlock block) RenderRazorSlice(slice, renderer, block); } - private void WriteCode(HtmlRenderer renderer, EnhancedCodeBlock block) - { - var slice = Code.Create(new CodeViewModel - { - CrossReferenceName = string.Empty,// block.CrossReferenceName, - Language = block.Language, - Caption = string.Empty - }); - //RenderRazorSliceRawContent(slice, renderer, block); - } - - private void WriteTabSet(HtmlRenderer renderer, TabSetBlock block) { var slice = TabSet.Create(new TabSetViewModel()); @@ -232,7 +220,7 @@ private void WriteIncludeBlock(HtmlRenderer renderer, IncludeBlock block) block.Configuration); var file = block.FileSystem.FileInfo.New(block.IncludePath); var document = parser.ParseAsync(file, block.FrontMatter, default).GetAwaiter().GetResult(); - var html = document.ToHtml(parser.Pipeline); + var html = document.ToHtml(MarkdownParser.Pipeline); renderer.Write(html); //var slice = Include.Create(new IncludeViewModel { Html = html }); //RenderRazorSlice(slice, renderer, block); @@ -270,7 +258,7 @@ private void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock block) RenderMarkdown = s => { var document = parser.Parse(s, block.IncludeFrom, block.FrontMatter); - var html = document.ToHtml(parser.Pipeline); + 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 6fc805cd..ed72eb27 100644 --- a/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/ImageBlock.cs @@ -6,8 +6,10 @@ namespace Elastic.Markdown.Myst.Directives; -public class ImageBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : DirectiveBlock(parser, properties, context) +public class FigureBlock(DirectiveBlockParser parser, ParserContext context) : ImageBlock(parser, context); + +public class ImageBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) { public override string Directive => "image"; @@ -100,5 +102,3 @@ private void ExtractImageUrl(ParserContext context) } -public class FigureBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : ImageBlock(parser, properties, context); diff --git a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs index b55e8201..677a9eb0 100644 --- a/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs @@ -8,8 +8,15 @@ namespace Elastic.Markdown.Myst.Directives; -public class IncludeBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : DirectiveBlock(parser, properties, context) +public class LiteralIncludeBlock : IncludeBlock +{ + public LiteralIncludeBlock(DirectiveBlockParser parser, ParserContext context) : base(parser, context) => + Literal = true; + + public override string Directive => "literalinclude"; +} + +public class IncludeBlock(DirectiveBlockParser parser, ParserContext context) : DirectiveBlock(parser, context) { public override string Directive => "include"; @@ -65,13 +72,3 @@ private void ExtractInclusionPath(ParserContext context) this.EmitError($"`{IncludePath}` does not exist."); } } - - -public class LiteralIncludeBlock : IncludeBlock -{ - public LiteralIncludeBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : base(parser, properties, context) => Literal = true; - - public override string Directive => "literalinclude"; - -} diff --git a/src/Elastic.Markdown/Myst/Directives/MermaidBlock.cs b/src/Elastic.Markdown/Myst/Directives/MermaidBlock.cs index 7452bde8..92ff6531 100644 --- a/src/Elastic.Markdown/Myst/Directives/MermaidBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/MermaidBlock.cs @@ -3,8 +3,7 @@ // See the LICENSE file in the project root for more information namespace Elastic.Markdown.Myst.Directives; -public class MermaidBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : DirectiveBlock(parser, properties, context) +public class MermaidBlock(DirectiveBlockParser parser, ParserContext context) : DirectiveBlock(parser, context) { public override string Directive => "mermaid"; diff --git a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs index 6b9c36de..9ea1442b 100644 --- a/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/SettingsBlock.cs @@ -8,8 +8,7 @@ namespace Elastic.Markdown.Myst.Directives; -public class SettingsBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : DirectiveBlock(parser, properties, context) +public class SettingsBlock(DirectiveBlockParser parser, ParserContext context) : DirectiveBlock(parser, context) { public override string Directive => "settings"; @@ -32,10 +31,7 @@ public class SettingsBlock(DirectiveBlockParser parser, Dictionary ExtractInclusionPath(context); private void ExtractInclusionPath(ParserContext context) { diff --git a/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs b/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs index 75db2fd3..3b3fcb4c 100644 --- a/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs @@ -6,8 +6,8 @@ namespace Elastic.Markdown.Myst.Directives; -public class TabSetBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : DirectiveBlock(parser, properties, context) +public class TabSetBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) { public override string Directive => "tab-set"; @@ -23,8 +23,8 @@ public int FindIndex() return _index; } } -public class TabItemBlock(DirectiveBlockParser parser, Dictionary properties, ParserContext context) - : DirectiveBlock(parser, properties, context) +public class TabItemBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) { public override string Directive => "tab-item"; diff --git a/src/Elastic.Markdown/Myst/Directives/UnknownDirectiveBlock.cs b/src/Elastic.Markdown/Myst/Directives/UnknownDirectiveBlock.cs index 72a78e28..db669741 100644 --- a/src/Elastic.Markdown/Myst/Directives/UnknownDirectiveBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/UnknownDirectiveBlock.cs @@ -4,12 +4,8 @@ namespace Elastic.Markdown.Myst.Directives; -public class UnknownDirectiveBlock( - DirectiveBlockParser parser, - string directive, - Dictionary properties, - ParserContext context) - : DirectiveBlock(parser, properties, context) +public class UnknownDirectiveBlock(DirectiveBlockParser parser, string directive, ParserContext context) + : DirectiveBlock(parser, context) { public override string Directive => directive; diff --git a/src/Elastic.Markdown/Myst/Directives/UnsupportedDirectiveBlock.cs b/src/Elastic.Markdown/Myst/Directives/UnsupportedDirectiveBlock.cs index 6f3345c7..14ba8809 100644 --- a/src/Elastic.Markdown/Myst/Directives/UnsupportedDirectiveBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/UnsupportedDirectiveBlock.cs @@ -6,13 +6,8 @@ namespace Elastic.Markdown.Myst.Directives; -public class UnsupportedDirectiveBlock( - DirectiveBlockParser parser, - string directive, - Dictionary properties, - int issueId, - ParserContext context) - : DirectiveBlock(parser, properties, context) +public class UnsupportedDirectiveBlock(DirectiveBlockParser parser, string directive, int issueId, ParserContext context) + : DirectiveBlock(parser, context) { public override string Directive => directive; diff --git a/src/Elastic.Markdown/Myst/Directives/VersionBlock.cs b/src/Elastic.Markdown/Myst/Directives/VersionBlock.cs index e03feaa8..0bb3b084 100644 --- a/src/Elastic.Markdown/Myst/Directives/VersionBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/VersionBlock.cs @@ -8,12 +8,8 @@ namespace Elastic.Markdown.Myst.Directives; -public class VersionBlock( - DirectiveBlockParser parser, - string directive, - Dictionary properties, - ParserContext context) - : DirectiveBlock(parser, properties, context) +public class VersionBlock(DirectiveBlockParser parser, string directive, ParserContext context) + : DirectiveBlock(parser, context) { public override string Directive => directive; public string Class => directive.Replace("version", ""); diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index aed329a9..3381963e 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -27,8 +27,7 @@ public class MarkdownParser( private BuildContext Context { get; } = context; - //TODO directive properties are stateful, rewrite this so we can cache builders - public MarkdownPipeline MinimalPipeline => + public static MarkdownPipeline MinimalPipeline { get; } = new MarkdownPipelineBuilder() .UseDiagnosticLinks() .UseYamlFrontMatter() @@ -36,7 +35,7 @@ public class MarkdownParser( .UseSubstitution() .Build(); - public MarkdownPipeline Pipeline => + public static MarkdownPipeline Pipeline { get; } = new MarkdownPipelineBuilder() .EnableTrackTrivia() .UsePreciseSourceLocation() diff --git a/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs b/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs index ab5e2af1..356b9d08 100644 --- a/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs @@ -10,12 +10,12 @@ namespace Elastic.Markdown.Tests.Directives; public abstract class AdmonitionUnsupportedTests(ITestOutputHelper output, string directive) : DirectiveTest(output, - $$""" - ```{{{directive}}} - This is an attention block - ``` - A regular paragraph. - """ +$$""" + ```{{{directive}}} + This is an attention block + ``` + A regular paragraph. + """ ) { [Fact]