From 019b9743411ddcbcceda95e83da5fd4394e23c5d Mon Sep 17 00:00:00 2001 From: Siamak Poursabahian Date: Wed, 27 Aug 2025 15:06:38 -0700 Subject: [PATCH 1/3] Support `applies_to` in additional directives/components (Implements #1436) --- docs/syntax/applies.md | 74 +++++++++++++++---- .../Directives/Admonition/AdmonitionBlock.cs | 4 + .../Admonition/AdmonitionView.cshtml | 9 ++- .../Admonition/AdmonitionViewModel.cs | 3 + .../Myst/Directives/Diagram/DiagramBlock.cs | 4 + .../Myst/Directives/DirectiveBlock.cs | 49 +++++++++++- .../Myst/Directives/DirectiveBlockParser.cs | 4 +- .../Myst/Directives/DirectiveHtmlRenderer.cs | 9 ++- .../Directives/Dropdown/DropdownView.cshtml | 14 +++- .../Myst/Directives/Image/ImageBlock.cs | 4 + .../Directives/Image/ImageCarouselBlock.cs | 4 + .../Myst/Directives/Include/IncludeBlock.cs | 4 + .../Myst/Directives/Mermaid/MermaidBlock.cs | 7 +- .../Myst/Directives/Settings/SettingsBlock.cs | 9 ++- .../Myst/Directives/Stepper/StepperBlock.cs | 11 ++- .../Myst/Directives/Tabs/TabItemView.cshtml | 15 +++- .../Myst/Directives/Tabs/TabItemViewModel.cs | 3 + .../Myst/Directives/Tabs/TabSetBlock.cs | 13 +++- .../Myst/Directives/Version/VersionBlock.cs | 4 + 19 files changed, 213 insertions(+), 31 deletions(-) diff --git a/docs/syntax/applies.md b/docs/syntax/applies.md index e193229a7..1db7bea90 100644 --- a/docs/syntax/applies.md +++ b/docs/syntax/applies.md @@ -23,15 +23,15 @@ The `applies_to` metadata supports an [exhaustive list of keys](#key). When you write or edit documentation, only specify the keys that apply to that content. Each key accepts values with the following syntax: -``` +```yaml : [version] ``` Where: -- The [lifecycle](#lifecycle) is mandatory -- The [version](#version) is optional -- You can specify multiple states by separating them with a comma. For example: `stack: preview 9.1, ga 9.4` +* The [lifecycle](#lifecycle) is mandatory +* The [version](#version) is optional +* You can specify multiple states by separating them with a comma. For example: `stack: preview 9.1, ga 9.4` :::{note} **Automatic Version Sorting**: When you specify multiple versions for the same product, the build system automatically sorts them in descending order (highest version first) regardless of the order you write them in the source file. For example, `stack: ga 8.18.6, ga 9.1.2, ga 8.19.2, ga 9.0.6` will be displayed as `stack: ga 9.1.2, ga 9.0.6, ga 8.19.2, ga 8.18.6`. Items without versions (like `ga` without a version or `all`) are sorted last. @@ -41,7 +41,7 @@ Note that a key without any value doesn't show any badge in the output. Versioned products require a `version` tag to be used with the `lifecycle` tag. See [Syntax](#syntax): -``` +```yaml applies_to: stack: preview 9.1, ga 9.4 deployment: @@ -50,7 +50,7 @@ applies_to: Unversioned products use `lifecycle` tags without a version: -``` +```yaml applies_to: serverless: elasticsearch: beta @@ -72,7 +72,6 @@ applies_to: :::{include} /_snippets/applies_to-version.md ::: - ## Examples ### Lifecycle examples @@ -173,6 +172,56 @@ Property {preview}`` : definition body ``` +## Directive property + +### Tab-item directive + +::::{tab-set} +:::{tab-item} +:applies_to: stack: beta 9.0 +This is where the content for tab #1 goes. +::: +:::{tab-item} +:applies_to: stack: ga 9.1 +This is where the content for tab #2 goes. +::: +:::{tab-item} +:applies_to: serverless: ga +This is where the content for tab #3 goes. +::: +:::{tab-item} Titled Tab +This is where the content for tab #4 goes. +::: +:::: + +### Admonition directive + +:::{tip} +:applies_to: eck: all +This is a tip. +::: + +:::{admonition} This is my callout +:applies_to: eck: all +It can *span* multiple lines and supports inline formatting. +::: + +:::{admonition} +:applies_to: stack: ga 9.0 +In {{stack}} 9.0, dense vector fields are always indexed as `int8_hnsw`. +::: + +### Dropdown directive + +:::{dropdown} Rules are failing due to number of alerts +:applies_to: stack: all +Dropdown content +::: +:::{dropdown} Rules are failing due to number of alerts +:applies_to: stack: ga 9.0 +Dropdown content +::: + ## Structured model ![Applies To Model](images/applies.png) @@ -215,7 +264,6 @@ applies_to: --- ``` - ## Look and feel ### Block @@ -242,15 +290,15 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam sit amet auctor odio. Donec ac placerat nunc. {applies_to}`stack: preview` Aenean scelerisque viverra lectus nec dignissim. Vestibulum ut felis nec massa auctor placerat. Maecenas vel dictum. -- {applies_to}`elasticsearch: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam. Mauris sed eleifend erat, sit amet auctor odio. Donec ac placerat nunc. -- {applies_to}`observability: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam. -- {applies_to}`security: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam. Mauris sed eleifend erat, sit amet auctor odio. Donec ac placerat nunc. Aenean scelerisque viverra lectus nec dignissim. +* {applies_to}`elasticsearch: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam. Mauris sed eleifend erat, sit amet auctor odio. Donec ac placerat nunc. +* {applies_to}`observability: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam. +* {applies_to}`security: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam. Mauris sed eleifend erat, sit amet auctor odio. Donec ac placerat nunc. Aenean scelerisque viverra lectus nec dignissim. #### Stack | `applies_to` | result | |--------------------------------------------|--------------------------------------| -| `` {applies_to}`stack: ` `` | {applies_to}`stack: ` | +| `` {applies_to}`stack: ` `` | {applies_to}`stack:` | | `` {applies_to}`stack: preview` `` | {applies_to}`stack: preview` | | `` {applies_to}`stack: preview 8.18` `` | {applies_to}`stack: preview 8.18` | | `` {applies_to}`stack: preview 9.0` `` | {applies_to}`stack: preview 9.0` | @@ -281,7 +329,7 @@ nec dignissim. Vestibulum ut felis nec massa auctor placerat. Maecenas vel dictu | `applies_to` | result | |-------------------------------------------------|-------------------------------------------| -| `` {applies_to}`serverless: ` `` | {applies_to}`serverless: ` | +| `` {applies_to}`serverless: ` `` | {applies_to}`serverless:` | | `` {applies_to}`serverless: preview` `` | {applies_to}`serverless: preview` | | `` {applies_to}`serverless: ga` `` | {applies_to}`serverless: ga` | | `` {applies_to}`serverless: beta` `` | {applies_to}`serverless: beta` | diff --git a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs index ba676590f..d3a06a431 100644 --- a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs @@ -33,6 +33,10 @@ public AdmonitionBlock(DirectiveBlockParser parser, string admonition, ParserCon public override void FinalizeAndValidate(ParserContext context) { + // Call the DirectiveBlock's FinalizeAndValidate + // for setup common to all the directive blocks + base.FinalizeAndValidate(context); + CrossReferenceName = Prop("name"); DropdownOpen = TryPropBool("open"); if (DropdownOpen.HasValue) diff --git a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml index 517a86fb5..f1383bde5 100644 --- a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml +++ b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml @@ -1,8 +1,10 @@ +@using Elastic.Markdown.Myst.Components + @inherits RazorSlice
- - @{ + + @{ switch (Model.Directive) { case "tip": @@ -33,6 +35,9 @@ } } @Model.Title + + @RenderPartialAsync(ApplicableToComponent.Create(Model.ApplicableToViewModel)) +
@Model.RenderBlock()
diff --git a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionViewModel.cs index 51da8b32a..0a6f48273 100644 --- a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionViewModel.cs +++ b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionViewModel.cs @@ -2,6 +2,8 @@ // 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 Elastic.Markdown.Myst.Components; + namespace Elastic.Markdown.Myst.Directives.Admonition; public class AdmonitionViewModel : DirectiveViewModel @@ -11,4 +13,5 @@ public class AdmonitionViewModel : DirectiveViewModel public required string? CrossReferenceName { get; init; } public required string? Classes { get; init; } public required string? Open { get; init; } + public required ApplicableToViewModel? ApplicableToViewModel { get; init; } } diff --git a/src/Elastic.Markdown/Myst/Directives/Diagram/DiagramBlock.cs b/src/Elastic.Markdown/Myst/Directives/Diagram/DiagramBlock.cs index f9a7c75fb..ef2ab169f 100644 --- a/src/Elastic.Markdown/Myst/Directives/Diagram/DiagramBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/Diagram/DiagramBlock.cs @@ -27,6 +27,10 @@ public class DiagramBlock(DirectiveBlockParser parser, ParserContext context) : public override void FinalizeAndValidate(ParserContext context) { + // Call the DirectiveBlock's FinalizeAndValidate + // for setup common to all the directive blocks + base.FinalizeAndValidate(context); + // Extract diagram type from arguments or default to "mermaid" DiagramType = !string.IsNullOrWhiteSpace(Arguments) ? Arguments.ToLowerInvariant() : "mermaid"; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs index 4cda54a91..91c01b3ce 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs @@ -7,9 +7,12 @@ // See the license.txt file in the project root for more information. using System.IO.Abstractions; +using Elastic.Documentation.AppliesTo; using Elastic.Documentation.Configuration; using Markdig.Helpers; using Markdig.Syntax; +using Elastic.Markdown.Myst.Components; +using Elastic.Markdown.Diagnostics; namespace Elastic.Markdown.Myst.Directives; @@ -95,11 +98,55 @@ public abstract class DirectiveBlock( /// public int ClosingFencedCharCount { get; set; } + /// + /// Applicability of this directive, in terms of product and version. + /// + public ApplicableToViewModel? ApplicableToViewModel { get; set; } + /// /// Allows blocks to finalize setting properties once fully parsed /// /// - public abstract void FinalizeAndValidate(ParserContext context); + public virtual void FinalizeAndValidate(ParserContext context) + { + //context.Build.VersionsConfiguration; + var applicableTo = ParseApplicableTo(context); + if (applicableTo != null) + { + ApplicableToViewModel = new ApplicableToViewModel + { + Inline = true, + AppliesTo = applicableTo, + VersionsConfig = context.Build.VersionsConfiguration + }; + } + } + + private ApplicableTo? ParseApplicableTo(ParserContext processor) + { + if (Properties is null) + return null; + var appliesTo = Prop("applies_to"); + if (string.IsNullOrWhiteSpace(appliesTo)) + return null; + + try + { + var applicableTo = YamlSerialization.Deserialize(appliesTo); + if (applicableTo.Diagnostics is null) + return applicableTo; + foreach (var (severity, message) in applicableTo.Diagnostics) + processor.EmitError(message); + applicableTo.Diagnostics = null; + return applicableTo; + } + catch (Exception e) + { + processor.EmitError($"Unable to parse applies_to role: {{{Directive}}}{appliesTo}", e); + } + + return null; + } internal void AddProperty(string key, string value) { diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs index f53825fc0..4260bbafe 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs @@ -201,12 +201,12 @@ public override BlockState TryContinue(BlockProcessor processor, Block block) if (block is not DirectiveBlock directiveBlock) return base.TryContinue(processor, block); - var tokens = line.ToString().Split(':', 3, RemoveEmptyEntries | TrimEntries); + var tokens = line.ToString().Split(':', 2, 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; + var data = tokens.Length > 1 ? tokens[1] : string.Empty; directiveBlock.AddProperty(name, data); return BlockState.Continue; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 1e890eb6b..00a8df6b4 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -212,7 +212,8 @@ private static void WriteAdmonition(HtmlRenderer renderer, AdmonitionBlock block CrossReferenceName = block.CrossReferenceName, Classes = block.Classes, Title = block.Title, - Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null + Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null, + ApplicableToViewModel = block.ApplicableToViewModel, }); RenderRazorSlice(slice, renderer); } @@ -226,7 +227,8 @@ private static void WriteDropdown(HtmlRenderer renderer, DropdownBlock block) CrossReferenceName = block.CrossReferenceName, Classes = block.Classes, Title = block.Title, - Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null + Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null, + ApplicableToViewModel = block.ApplicableToViewModel, }); RenderRazorSlice(slice, renderer); } @@ -246,7 +248,8 @@ private static void WriteTabItem(HtmlRenderer renderer, TabItemBlock block) Title = block.Title, TabSetIndex = block.TabSetIndex, SyncKey = block.SyncKey, - TabSetGroupKey = block.TabSetGroupKey + TabSetGroupKey = block.TabSetGroupKey, + ApplicableToViewModel = block.ApplicableToViewModel, }); RenderRazorSlice(slice, renderer); } diff --git a/src/Elastic.Markdown/Myst/Directives/Dropdown/DropdownView.cshtml b/src/Elastic.Markdown/Myst/Directives/Dropdown/DropdownView.cshtml index bdf9f6dc8..eb00e2f91 100644 --- a/src/Elastic.Markdown/Myst/Directives/Dropdown/DropdownView.cshtml +++ b/src/Elastic.Markdown/Myst/Directives/Dropdown/DropdownView.cshtml @@ -1,7 +1,19 @@ +@using Elastic.Markdown.Myst.Components + @inherits RazorSlice +