Skip to content

Commit 019b974

Browse files
Support applies_to in additional directives/components
(Implements #1436)
1 parent b08b4cf commit 019b974

File tree

19 files changed

+213
-31
lines changed

19 files changed

+213
-31
lines changed

docs/syntax/applies.md

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ The `applies_to` metadata supports an [exhaustive list of keys](#key).
2323
When you write or edit documentation, only specify the keys that apply to that content.
2424
Each key accepts values with the following syntax:
2525

26-
```
26+
```yaml
2727
<key>: <lifecycle> [version]
2828
```
2929
3030
Where:
3131
32-
- The [lifecycle](#lifecycle) is mandatory
33-
- The [version](#version) is optional
34-
- You can specify multiple states by separating them with a comma. For example: `stack: preview 9.1, ga 9.4`
32+
* The [lifecycle](#lifecycle) is mandatory
33+
* The [version](#version) is optional
34+
* You can specify multiple states by separating them with a comma. For example: `stack: preview 9.1, ga 9.4`
3535

3636
:::{note}
3737
**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.
4141

4242
Versioned products require a `version` tag to be used with the `lifecycle` tag. See [Syntax](#syntax):
4343

44-
```
44+
```yaml
4545
applies_to:
4646
stack: preview 9.1, ga 9.4
4747
deployment:
@@ -50,7 +50,7 @@ applies_to:
5050

5151
Unversioned products use `lifecycle` tags without a version:
5252

53-
```
53+
```yaml
5454
applies_to:
5555
serverless:
5656
elasticsearch: beta
@@ -72,7 +72,6 @@ applies_to:
7272
:::{include} /_snippets/applies_to-version.md
7373
:::
7474

75-
7675
## Examples
7776

7877
### Lifecycle examples
@@ -173,6 +172,56 @@ Property {preview}`<version>`
173172
: definition body
174173
```
175174

175+
## Directive property
176+
177+
### Tab-item directive
178+
179+
::::{tab-set}
180+
:::{tab-item}
181+
:applies_to: stack: beta 9.0
182+
This is where the content for tab #1 goes.
183+
:::
184+
:::{tab-item}
185+
:applies_to: stack: ga 9.1
186+
This is where the content for tab #2 goes.
187+
:::
188+
:::{tab-item}
189+
:applies_to: serverless: ga
190+
This is where the content for tab #3 goes.
191+
:::
192+
:::{tab-item} Titled Tab
193+
This is where the content for tab #4 goes.
194+
:::
195+
::::
196+
197+
### Admonition directive
198+
199+
:::{tip}
200+
:applies_to: eck: all
201+
This is a tip.
202+
:::
203+
204+
:::{admonition} This is my callout
205+
:applies_to: eck: all
206+
It can *span* multiple lines and supports inline formatting.
207+
:::
208+
209+
:::{admonition}
210+
:applies_to: stack: ga 9.0
211+
In {{stack}} 9.0, dense vector fields are always indexed as `int8_hnsw`.
212+
:::
213+
214+
### Dropdown directive
215+
216+
:::{dropdown} Rules are failing due to number of alerts
217+
:applies_to: stack: all
218+
Dropdown content
219+
:::
220+
:::{dropdown} Rules are failing due to number of alerts
221+
:applies_to: stack: ga 9.0
222+
Dropdown content
223+
:::
224+
176225
## Structured model
177226

178227
![Applies To Model](images/applies.png)
@@ -215,7 +264,6 @@ applies_to:
215264
---
216265
```
217266

218-
219267
## Look and feel
220268

221269
### Block
@@ -242,15 +290,15 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam
242290
sit amet auctor odio. Donec ac placerat nunc. {applies_to}`stack: preview` Aenean scelerisque viverra lectus
243291
nec dignissim. Vestibulum ut felis nec massa auctor placerat. Maecenas vel dictum.
244292

245-
- {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.
246-
- {applies_to}`observability: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam.
247-
- {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.
293+
* {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.
294+
* {applies_to}`observability: preview` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ut libero diam.
295+
* {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.
248296

249297
#### Stack
250298

251299
| `applies_to` | result |
252300
|--------------------------------------------|--------------------------------------|
253-
| `` {applies_to}`stack: ` `` | {applies_to}`stack: ` |
301+
| `` {applies_to}`stack: ` `` | {applies_to}`stack:` |
254302
| `` {applies_to}`stack: preview` `` | {applies_to}`stack: preview` |
255303
| `` {applies_to}`stack: preview 8.18` `` | {applies_to}`stack: preview 8.18` |
256304
| `` {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
281329

282330
| `applies_to` | result |
283331
|-------------------------------------------------|-------------------------------------------|
284-
| `` {applies_to}`serverless: ` `` | {applies_to}`serverless: ` |
332+
| `` {applies_to}`serverless: ` `` | {applies_to}`serverless:` |
285333
| `` {applies_to}`serverless: preview` `` | {applies_to}`serverless: preview` |
286334
| `` {applies_to}`serverless: ga` `` | {applies_to}`serverless: ga` |
287335
| `` {applies_to}`serverless: beta` `` | {applies_to}`serverless: beta` |

src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public AdmonitionBlock(DirectiveBlockParser parser, string admonition, ParserCon
3333

3434
public override void FinalizeAndValidate(ParserContext context)
3535
{
36+
// Call the DirectiveBlock's FinalizeAndValidate
37+
// for setup common to all the directive blocks
38+
base.FinalizeAndValidate(context);
39+
3640
CrossReferenceName = Prop("name");
3741
DropdownOpen = TryPropBool("open");
3842
if (DropdownOpen.HasValue)

src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
@using Elastic.Markdown.Myst.Components
2+
13
@inherits RazorSlice<Elastic.Markdown.Myst.Directives.Admonition.AdmonitionViewModel>
24
<div class="admonition @Model.Directive @Model.Classes" id="@Model.CrossReferenceName" open="@Model.Open">
35
<div class="admonition-title">
4-
5-
@{
6+
7+
@{
68
switch (Model.Directive)
79
{
810
case "tip":
@@ -33,6 +35,9 @@
3335
}
3436
}
3537
<span>@Model.Title</span>
38+
<span class="blah applies applies-inline" style="margin-left: auto;">
39+
@RenderPartialAsync(ApplicableToComponent.Create(Model.ApplicableToViewModel))
40+
</span>
3641
</div>
3742
<div class="admonition-content">@Model.RenderBlock()</div>
3843
</div>

src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionViewModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using Elastic.Markdown.Myst.Components;
6+
57
namespace Elastic.Markdown.Myst.Directives.Admonition;
68

79
public class AdmonitionViewModel : DirectiveViewModel
@@ -11,4 +13,5 @@ public class AdmonitionViewModel : DirectiveViewModel
1113
public required string? CrossReferenceName { get; init; }
1214
public required string? Classes { get; init; }
1315
public required string? Open { get; init; }
16+
public required ApplicableToViewModel? ApplicableToViewModel { get; init; }
1417
}

src/Elastic.Markdown/Myst/Directives/Diagram/DiagramBlock.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public class DiagramBlock(DirectiveBlockParser parser, ParserContext context) :
2727

2828
public override void FinalizeAndValidate(ParserContext context)
2929
{
30+
// Call the DirectiveBlock's FinalizeAndValidate
31+
// for setup common to all the directive blocks
32+
base.FinalizeAndValidate(context);
33+
3034
// Extract diagram type from arguments or default to "mermaid"
3135
DiagramType = !string.IsNullOrWhiteSpace(Arguments) ? Arguments.ToLowerInvariant() : "mermaid";
3236

src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
// See the license.txt file in the project root for more information.
88

99
using System.IO.Abstractions;
10+
using Elastic.Documentation.AppliesTo;
1011
using Elastic.Documentation.Configuration;
1112
using Markdig.Helpers;
1213
using Markdig.Syntax;
14+
using Elastic.Markdown.Myst.Components;
15+
using Elastic.Markdown.Diagnostics;
1316

1417
namespace Elastic.Markdown.Myst.Directives;
1518

@@ -95,11 +98,55 @@ public abstract class DirectiveBlock(
9598
/// <inheritdoc />
9699
public int ClosingFencedCharCount { get; set; }
97100

101+
/// <summary>
102+
/// Applicability of this directive, in terms of product and version.
103+
/// </summary>
104+
public ApplicableToViewModel? ApplicableToViewModel { get; set; }
105+
98106
/// <summary>
99107
/// Allows blocks to finalize setting properties once fully parsed
100108
/// </summary>
101109
/// <param name="context"></param>
102-
public abstract void FinalizeAndValidate(ParserContext context);
110+
public virtual void FinalizeAndValidate(ParserContext context)
111+
{
112+
//context.Build.VersionsConfiguration;
113+
var applicableTo = ParseApplicableTo(context);
114+
if (applicableTo != null)
115+
{
116+
ApplicableToViewModel = new ApplicableToViewModel
117+
{
118+
Inline = true,
119+
AppliesTo = applicableTo,
120+
VersionsConfig = context.Build.VersionsConfiguration
121+
};
122+
}
123+
}
124+
125+
private ApplicableTo? ParseApplicableTo(ParserContext processor)
126+
{
127+
if (Properties is null)
128+
return null;
129+
var appliesTo = Prop("applies_to");
130+
if (string.IsNullOrWhiteSpace(appliesTo))
131+
return null;
132+
133+
try
134+
{
135+
var applicableTo = YamlSerialization.Deserialize<ApplicableTo>(appliesTo);
136+
if (applicableTo.Diagnostics is null)
137+
return applicableTo;
138+
foreach (var (severity, message) in applicableTo.Diagnostics)
139+
processor.EmitError(message);
140+
applicableTo.Diagnostics = null;
141+
return applicableTo;
142+
}
143+
catch (Exception e)
144+
{
145+
processor.EmitError($"Unable to parse applies_to role: {{{Directive}}}{appliesTo}", e);
146+
}
147+
148+
return null;
149+
}
103150

104151
internal void AddProperty(string key, string value)
105152
{

src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,12 @@ public override BlockState TryContinue(BlockProcessor processor, Block block)
201201
if (block is not DirectiveBlock directiveBlock)
202202
return base.TryContinue(processor, block);
203203

204-
var tokens = line.ToString().Split(':', 3, RemoveEmptyEntries | TrimEntries);
204+
var tokens = line.ToString().Split(':', 2, RemoveEmptyEntries | TrimEntries);
205205
if (tokens.Length < 1)
206206
return base.TryContinue(processor, block);
207207

208208
var name = tokens[0];
209-
var data = tokens.Length > 1 ? string.Join(":", tokens[1..]) : string.Empty;
209+
var data = tokens.Length > 1 ? tokens[1] : string.Empty;
210210
directiveBlock.AddProperty(name, data);
211211

212212
return BlockState.Continue;

src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ private static void WriteAdmonition(HtmlRenderer renderer, AdmonitionBlock block
212212
CrossReferenceName = block.CrossReferenceName,
213213
Classes = block.Classes,
214214
Title = block.Title,
215-
Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null
215+
Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null,
216+
ApplicableToViewModel = block.ApplicableToViewModel,
216217
});
217218
RenderRazorSlice(slice, renderer);
218219
}
@@ -226,7 +227,8 @@ private static void WriteDropdown(HtmlRenderer renderer, DropdownBlock block)
226227
CrossReferenceName = block.CrossReferenceName,
227228
Classes = block.Classes,
228229
Title = block.Title,
229-
Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null
230+
Open = block.DropdownOpen.GetValueOrDefault() ? "open" : null,
231+
ApplicableToViewModel = block.ApplicableToViewModel,
230232
});
231233
RenderRazorSlice(slice, renderer);
232234
}
@@ -246,7 +248,8 @@ private static void WriteTabItem(HtmlRenderer renderer, TabItemBlock block)
246248
Title = block.Title,
247249
TabSetIndex = block.TabSetIndex,
248250
SyncKey = block.SyncKey,
249-
TabSetGroupKey = block.TabSetGroupKey
251+
TabSetGroupKey = block.TabSetGroupKey,
252+
ApplicableToViewModel = block.ApplicableToViewModel,
250253
});
251254
RenderRazorSlice(slice, renderer);
252255
}

src/Elastic.Markdown/Myst/Directives/Dropdown/DropdownView.cshtml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
@using Elastic.Markdown.Myst.Components
2+
13
@inherits RazorSlice<Elastic.Markdown.Myst.Directives.Admonition.AdmonitionViewModel>
4+
25
<details class="dropdown @Model.Classes" id="@Model.CrossReferenceName" open="@Model.Open">
36
<summary class="dropdown-title">
4-
<span class="sd-summary-text">@Model.Title</span>
7+
<span>
8+
@if (Model.ApplicableToViewModel != null)
9+
{
10+
<span class="applies applies-inline">
11+
@RenderPartialAsync(ApplicableToComponent.Create(Model.ApplicableToViewModel))
12+
&nbsp
13+
</span>
14+
}
15+
<span class="sd-summary-text">@Model.Title</span>
16+
</span>
517
<svg
618
xmlns="http://www.w3.org/2000/svg"
719
fill="none"

src/Elastic.Markdown/Myst/Directives/Image/ImageBlock.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ public class ImageBlock(DirectiveBlockParser parser, ParserContext context)
7272

7373
public override void FinalizeAndValidate(ParserContext context)
7474
{
75+
// Call the DirectiveBlock's FinalizeAndValidate
76+
// for setup common to all the directive blocks
77+
base.FinalizeAndValidate(context);
78+
7579
Label = Prop("label", "name");
7680
Alt = Prop("alt")?.ReplaceSubstitutions(context) ?? string.Empty;
7781
// Use Alt as Title if no explicit Title is provided

0 commit comments

Comments
 (0)