diff --git a/docs/syntax/substitutions.md b/docs/syntax/substitutions.md
index d7bd928d1..45adc889f 100644
--- a/docs/syntax/substitutions.md
+++ b/docs/syntax/substitutions.md
@@ -3,6 +3,7 @@ sub:
frontmatter_key: "Front Matter Value"
a-key-with-dashes: "A key with dashes"
version: 7.17.0
+ hello-world: "Hello world!"
---
# Substitutions
@@ -26,7 +27,7 @@ Doing so will result in a build error.
To use the variables in your files, surround them in curly brackets (`{{variable}}`).
-## Example
+### Example
Here are some variable substitutions:
@@ -36,6 +37,97 @@ Here are some variable substitutions:
| {{a-key-with-dashes}} | Front Matter |
| {{a-global-variable}} | `docset.yml` |
+## Mutations
+
+Substitutions can be mutated using a chain of operators seperated by a pipe (`|`).
+
+````markdown
+`{{hello-world | trim | lc | tc}}`
+````
+
+Will trim, lowercase and finally titlecase the contents of the 'hello-world' variable.
+
+### Operators
+
+
+| Operator | Purpose |
+|----------|----------------------------------------------------|
+| `lc` | LowerCase, |
+| `uc` | UpperCase, |
+| `tc` | TitleCase, capitalizes all words, |
+| `c` | Capitalize the first letter, |
+| `kc` | Convert to KebabCase, |
+| `sc` | Convert to SnakeCase, |
+| `cc` | Convert to CamelCase, |
+| `pc` | Convert to PascalCase, |
+| `trim` | Trim common non word characters from start and end |
+
+For variables declaring a semantic version or `Major.Minor` the following operations are also exposed
+
+| Operator | Purpose |
+|----------|------------------------------------------|
+| `M` | Display only the major component |
+| `M.x` | Display major component followed by '.x' |
+| `M.M` | Display only the major and the minor |
+| `M+1` | The next major version |
+| `M.M+1` | The next minor version |
+
+### Example
+
+Given the following frontmatter:
+
+```yaml
+---
+sub:
+ hello-world: "Hello world!"
+---
+```
+
+::::{tab-set}
+
+:::{tab-item} Output
+
+* Lowercase: {{hello-world | lc}}
+* Uppercase: {{hello-world | uc}}
+* TitleCase: {{hello-world | tc}}
+* kebab-case: {{hello-world | kc}}
+* camelCase: {{hello-world | tc | cc}}
+* PascalCase: {{hello-world | pc}}
+* SnakeCase: {{hello-world | sc}}
+* CapitalCase (chained): {{hello-world | lc | c}}
+* Trim: {{hello-world | trim}}
+* M.x: {{version.stack | M.x }}
+* M.M: {{version.stack | M.M }}
+* M: {{version.stack | M }}
+* M+1: {{version.stack | M+1 }}
+* M+1 | M.M: {{version.stack | M+1 | M.M }}
+* M.M+1: {{version.stack | M.M+1 }}
+
+:::
+
+:::{tab-item} Markdown
+
+````markdown
+* Lowercase: {{hello-world | lc}}
+* Uppercase: {{hello-world | uc}}
+* TitleCase: {{hello-world | tc}}
+* kebab-case: {{hello-world | kc}}
+* camelCase: {{hello-world | tc | cc}}
+* PascalCase: {{hello-world | pc}}
+* SnakeCase: {{hello-world | sc}}
+* CapitalCase (chained): {{hello-world | lc | c}}
+* Trim: {{hello-world | trim}}
+* M.x: {{version.stack | M.x }}
+* M.M: {{version.stack | M.M }}
+* M: {{version.stack | M }}
+* M+1: {{version.stack | M+1 }}
+* M+1 | M.M: {{version.stack | M+1 | M.M }}
+* M.M+1: {{version.stack | M.M+1 }}
+````
+:::
+
+::::
+
## Code blocks
Substitutions are supported in code blocks but are disabled by default. Enable substitutions by adding `subs=true` to the code block.
diff --git a/docs/syntax/version-variables.md b/docs/syntax/version-variables.md
index 87cc0cf07..da9a63401 100644
--- a/docs/syntax/version-variables.md
+++ b/docs/syntax/version-variables.md
@@ -10,14 +10,21 @@ Besides the current version, the following suffixes are available:
| Version substitution | result | purpose |
|--------------------------------------|-----------------------------------|-----------------------------------------|
-| `{{versions.stack}}` | {{version.stack}} | Current version |
-| `{{versions.stack.major_minor}}` | {{version.stack.major_minor}} | Current `MAJOR.MINOR` |
-| `{{versions.stack.major_x}}` | {{version.stack.major_x}} | Current `MAJOR.X` |
-| `{{versions.stack.major_component}}` | {{version.stack.major_component}} | Current major component |
-| `{{versions.stack.next_major}}` | {{version.stack.next_major}} | The next major version |
-| `{{versions.stack.next_minor}}` | {{version.stack.next_minor}} | The next minor version |
-| `{{versions.stack.base}}` | {{version.stack.base}} | The first version on the new doc system |
+| `{{version.stack}}` | {{version.stack}} | Current version |
+| `{{version.stack.base}}` | {{version.stack.base}} | The first version on the new doc system |
+## Formatting
+
+Using specialized [mutation operators](substitutions.md#mutations) versions
+can be printed in any kind of ways.
+
+
+| Version substitution | result |
+|------------------------|-----------|
+| `{{version.stack| M.M}}` | {{version.stack|M.M}} |
+| `{{version.stack.base | M }}` | {{version.stack.base | M }} |
+| `{{version.stack | M+1 | M }}` | {{version.stack | M+1 | M }} |
+| `{{version.stack.base | M.M+1 }}` | {{version.stack.base | M.M+1 }} |
## Available versioning schemes.
diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs
index 24cbaa5b8..da4cb39de 100644
--- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs
+++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs
@@ -163,27 +163,11 @@ public ConfigurationFile(IDocumentationContext context, VersionsConfiguration ve
foreach (var (id, system) in versionsConfig.VersioningSystems)
{
var name = id.ToStringFast(true);
- var current = system.Current;
var key = $"version.{name}";
_substitutions[key] = system.Current;
key = $"version.{name}.base";
_substitutions[key] = system.Base;
-
- key = $"version.{name}.major_minor";
- _substitutions[key] = $"{current.Major:N0}.{current.Minor:N0}";
-
- key = $"version.{name}.major_x";
- _substitutions[key] = $"{current.Major:N0}.x";
-
- key = $"version.{name}.major_component";
- _substitutions[key] = $"{current.Major:N0}";
-
- key = $"version.{name}.next_minor";
- _substitutions[key] = new SemVersion(current.Major, current.Minor + 1, current.Patch, current.Prerelease, current.Metadata);
-
- key = $"version.{name}.next_major";
- _substitutions[key] = new SemVersion(current.Major + 1, current.Minor, current.Patch, current.Prerelease, current.Metadata);
}
var toc = new TableOfContentsConfiguration(this, sourceFile, ScopeDirectory, _context, 0, "");
diff --git a/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs b/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs
index 24e43c434..abcefd00d 100644
--- a/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs
+++ b/src/Elastic.Markdown/Diagnostics/ProcessorDiagnosticExtensions.cs
@@ -14,32 +14,24 @@ public static class ProcessorDiagnosticExtensions
{
private static string CreateExceptionMessage(string message, Exception? e) => message + (e != null ? Environment.NewLine + e : string.Empty);
- public static void EmitError(this InlineProcessor processor, int line, int column, int length, string message)
- {
- var context = processor.GetContext();
- if (context.SkipValidation)
- return;
- var d = new Diagnostic
- {
- Severity = Severity.Error,
- File = processor.GetContext().MarkdownSourcePath.FullName,
- Column = column,
- Line = line,
- Message = message,
- Length = length
- };
- context.Build.Collector.Write(d);
- }
+ public static void EmitError(this InlineProcessor processor, int line, int column, int length, string message) =>
+ processor.Emit(Severity.Error, line, column, length, message);
- public static void EmitWarning(this InlineProcessor processor, int line, int column, int length, string message)
+ public static void EmitWarning(this InlineProcessor processor, int line, int column, int length, string message) =>
+ processor.Emit(Severity.Warning, line, column, length, message);
+
+ public static void EmitHint(this InlineProcessor processor, int line, int column, int length, string message) =>
+ processor.Emit(Severity.Hint, line, column, length, message);
+
+ public static void Emit(this InlineProcessor processor, Severity severity, int line, int column, int length, string message)
{
var context = processor.GetContext();
if (context.SkipValidation)
return;
var d = new Diagnostic
{
- Severity = Severity.Warning,
+ Severity = severity,
File = processor.GetContext().MarkdownSourcePath.FullName,
Column = column,
Line = line,
diff --git a/src/Elastic.Markdown/DocumentationGenerator.cs b/src/Elastic.Markdown/DocumentationGenerator.cs
index 3a9bd9eed..b956771ea 100644
--- a/src/Elastic.Markdown/DocumentationGenerator.cs
+++ b/src/Elastic.Markdown/DocumentationGenerator.cs
@@ -180,7 +180,13 @@ private void HintUnusedSubstitutionKeys()
{
var definedKeys = new HashSet The following should be subbed: Hello World!
not a comment
not a {{valid-key}}
- not a {substitution}
+
+ """
diff --git a/tests/authoring/Inline/Substitutions.fs b/tests/authoring/Inline/Substitutions.fs
index ed75ba22b..1a7679952 100644
--- a/tests/authoring/Inline/Substitutions.fs
+++ b/tests/authoring/Inline/Substitutions.fs
@@ -46,6 +46,7 @@ The following should be subbed: {{hello-world}}
not a comment
not a {{valid-key}}
not a {substitution}
+The following should be subbed too: {{ hello-world }}
"""
[