diff --git a/docs/source/docset.yml b/docs/source/docset.yml index b192cbf85..fa1bce394 100644 --- a/docs/source/docset.yml +++ b/docs/source/docset.yml @@ -17,6 +17,7 @@ external_hosts: - palletsprojects.com - google.com - checkvist.com + - commonmark.org exclude: - '_*.md' subs: @@ -64,6 +65,7 @@ toc: - folder: syntax children: - file: index.md + - file: md-extensions.md - file: admonitions.md - file: automated_settings.md - file: code.md diff --git a/docs/source/syntax/_snippets/reusable-snippet.md b/docs/source/syntax/_snippets/reusable-snippet.md index 12bee436d..561874d9e 100644 --- a/docs/source/syntax/_snippets/reusable-snippet.md +++ b/docs/source/syntax/_snippets/reusable-snippet.md @@ -1,5 +1,5 @@ This is a snippet included on "{{page_title}}". -```{tip} +:::{tip} This is a snippet -``` +::: diff --git a/docs/source/syntax/admonitions.md b/docs/source/syntax/admonitions.md index 3f480eff7..14f6855fd 100644 --- a/docs/source/syntax/admonitions.md +++ b/docs/source/syntax/admonitions.md @@ -39,9 +39,9 @@ This is a warning. ::: ``` -```{warning} +:::{warning} This is a warning. -``` +::: ### Tip @@ -53,9 +53,9 @@ This is a tip. ::: ``` -```{tip} +:::{tip} This is a tip. -``` +::: ### Important @@ -67,15 +67,15 @@ This is an important notice. ::: ``` -```{important} +:::{important} This is an important notice. -``` +::: ## Collapsible admonitions -```{tip} +:::{tip} Also see [dropdowns](./dropdowns.md). -``` +::: Use `:open: ` to make an admonition collapsible. @@ -89,13 +89,13 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i ::: ``` -```{note} +:::{note} :open: Longer content can be collapsed to take less space. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. -``` +::: --- diff --git a/docs/source/syntax/automated_settings.md b/docs/source/syntax/automated_settings.md index 19a5ea51c..5826a3b63 100644 --- a/docs/source/syntax/automated_settings.md +++ b/docs/source/syntax/automated_settings.md @@ -32,5 +32,5 @@ groups: _Everything below this line is auto-generated._ -```{settings} /syntax/kibana-alerting-action-settings.yml -``` \ No newline at end of file +:::{settings} /syntax/kibana-alerting-action-settings.yml +::: \ No newline at end of file diff --git a/docs/source/syntax/code.md b/docs/source/syntax/code.md index 49584a142..444bcc9fe 100644 --- a/docs/source/syntax/code.md +++ b/docs/source/syntax/code.md @@ -74,9 +74,9 @@ var apiKey = new ApiKey(""); // Set up the api key var client = new ElasticsearchClient("", apiKey); ``` -```{note} +:::{note} the comments have the follow code to be hoisted as a callout. -``` +::: ````markdown ```csharp diff --git a/docs/source/syntax/conditionals.md b/docs/source/syntax/conditionals.md index 1c3d1af44..b3abfeaa2 100644 --- a/docs/source/syntax/conditionals.md +++ b/docs/source/syntax/conditionals.md @@ -2,6 +2,6 @@ title: Conditionals --- -```{warning} +:::{warning} This feature is not currently supported in Elastic Docs V3. -``` \ No newline at end of file +::: \ No newline at end of file diff --git a/docs/source/syntax/dropdowns.md b/docs/source/syntax/dropdowns.md index 54b6d9172..08dbee1ec 100644 --- a/docs/source/syntax/dropdowns.md +++ b/docs/source/syntax/dropdowns.md @@ -27,10 +27,10 @@ Dropdown content ::: ``` -```{dropdown} Dropdown Title +:::{dropdown} Dropdown Title :open: Dropdown content -``` +::: ## Asciidoc syntax diff --git a/docs/source/syntax/example_blocks.md b/docs/source/syntax/example_blocks.md index 317778282..a0158dc47 100644 --- a/docs/source/syntax/example_blocks.md +++ b/docs/source/syntax/example_blocks.md @@ -2,6 +2,6 @@ title: Example blocks --- -```{warning} +:::{warning} This feature is not currently supported in Elastic Docs V3. -``` \ No newline at end of file +::: \ No newline at end of file diff --git a/docs/source/syntax/images.md b/docs/source/syntax/images.md index 6f0de88f7..21aed7eca 100644 --- a/docs/source/syntax/images.md +++ b/docs/source/syntax/images.md @@ -12,9 +12,9 @@ Images can be referenced from the top-level `_static` dir or a local image dir. Screenshots are images displayed with a box-shadow. -```{warning} +:::{warning} This feature is not currently supported in Elastic Docs V3. -``` +::: ## Block-level images @@ -33,10 +33,10 @@ Or, use the `image` directive. ::: ``` -```{image} /_static/img/observability.png +:::{image} /_static/img/observability.png :alt: Elasticsearch :width: 250px -``` +::: ## Inline images diff --git a/docs/source/syntax/md-extensions.md b/docs/source/syntax/md-extensions.md new file mode 100644 index 000000000..7a1616a4e --- /dev/null +++ b/docs/source/syntax/md-extensions.md @@ -0,0 +1,72 @@ +--- +title: Markdown Syntax Extensions +title_navigation: Markdown Syntax +--- + +## Syntax + +The new documentation fully supports [CommonMark](https://commonmark.org/) Markdown syntax. + +On top of this widely accepted feature set we have various extensions points. + +## Directives + +Directives are our main extension point over markdown and follows the following syntax + + +```markdown +:::{EXTENSION} ARGUMENTS +:OPTION: VALUE + +Nested content that will be parsed as markdown +::: +``` + +- `EXTENSION` is the name of the directive e.g [`note`](admonitions.md#note) +- `ARGUMENT` some directives take a main argumnt e.g [`:::{include} _snippets/include.md`](file_inclusion.md) +- `OPTION` and `VALUE` can be used to further customize a given directive. + +The usage of `:::` allows the nested markdown to be syntax highlighted properly by editors and web viewers. + +Our (directives) are always wrapped in brackets `{ }`. + +### Nesting Directives + +You can increase the leading semicolons to include nested directives. Here the `{note}` wraps a `{hint}`. + +```markdown +::::{note} My note +:::{hint} My hint +Content displayed in the hint admonition +::: +Content displayed in the note admonition +:::: +``` + +## Literal directives + +For best editor compatability it is recommended to use triple backticks for content that needs to be displayed literally + +````markdown +```js +const x = 1; +``` +```` + +Many markdown editors support syntax highlighting for embedded code blocks. + +## Github Flavored Markdown + +We support some of GitHub flavor markdown extensions these are highlighted in green here: https://github.github.com/gfm/ + +Supported: + +* GFM Tables [](tables.md#github-flavored-markdown-gfm-table) +* Strikethrough ~~as seen here~~ + +Not supported: + +* Task lists +* Auto links (http://www.elastic.co) +* Using a subset of html + diff --git a/docs/source/syntax/passthrough.md b/docs/source/syntax/passthrough.md index 61b645186..e6cd42902 100644 --- a/docs/source/syntax/passthrough.md +++ b/docs/source/syntax/passthrough.md @@ -2,6 +2,6 @@ title: Passthrough blocks --- -```{warning} +:::{warning} This feature is not currently supported in Elastic Docs V3. -``` \ No newline at end of file +::: \ No newline at end of file diff --git a/docs/source/syntax/sidebars.md b/docs/source/syntax/sidebars.md index 5d1410332..7afe152de 100644 --- a/docs/source/syntax/sidebars.md +++ b/docs/source/syntax/sidebars.md @@ -2,6 +2,6 @@ title: Sidebars --- -```{warning} +:::{warning} This feature is not currently supported in Elastic Docs V3. -``` \ No newline at end of file +::: \ No newline at end of file diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs index 56369e4f0..f7c8e8604 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs @@ -34,7 +34,7 @@ public DirectiveBlockParser() private readonly string[] _codeBlocks = ["code", "code-block", "sourcecode"]; - private readonly FrozenDictionary _unsupportedBlocks = new Dictionary + private static readonly FrozenDictionary UnsupportedBlocks = new Dictionary { { "bibliography", 5 }, { "blockquote", 6 }, @@ -63,6 +63,9 @@ public DirectiveBlockParser() { "seealso", 3 } }.ToFrozenDictionary(); + private static readonly FrozenDictionary.AlternateLookup> UnsupportedLookup = + UnsupportedBlocks.GetAlternateLookup>(); + protected override DirectiveBlock CreateFencedBlock(BlockProcessor processor) { var info = processor.Line.AsSpan(); @@ -70,10 +73,9 @@ protected override DirectiveBlock CreateFencedBlock(BlockProcessor processor) if (processor.Context is not ParserContext context) throw new Exception("Expected parser context to be of type ParserContext"); - // TODO alternate lookup .NET 9 - var directive = info.ToString().Trim(['{', '}', '`']); - if (_unsupportedBlocks.TryGetValue(directive, out var issueId)) - return new UnsupportedDirectiveBlock(this, directive, issueId, context); + var directive = info.Trim(['{', '}', '`', ':']); + if (UnsupportedLookup.TryGetValue(directive, out var issueId)) + return new UnsupportedDirectiveBlock(this, directive.ToString(), issueId, context); if (info.IndexOf("{tab-set}") > 0) return new TabSetBlock(this, context); diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 69fed5b07..c52ecb48d 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -108,8 +108,11 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) if (link.FirstChild == null || !string.IsNullOrEmpty(anchor)) { - var file = string.IsNullOrWhiteSpace(url) ? context.Path - : context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Build.SourcePath.FullName, url.TrimStart('/'))); + var file = string.IsNullOrWhiteSpace(url) + ? context.Path + : url.StartsWith('/') + ? context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Build.SourcePath.FullName, url.TrimStart('/'))) + : context.Build.ReadFileSystem.FileInfo.New(Path.Combine(context.Path.Directory!.FullName, url)); var markdown = context.GetDocumentationFile?.Invoke(file) as MarkdownFile; var title = markdown?.Title; diff --git a/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs b/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs index 3c36551f4..456dda631 100644 --- a/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs @@ -9,9 +9,9 @@ namespace Elastic.Markdown.Tests.Directives; public abstract class AdmonitionTests(ITestOutputHelper output, string directive) : DirectiveTest(output, $$""" -```{{{directive}}} +:::{{{directive}}} This is an attention block -``` +::: A regular paragraph. """ ) @@ -65,10 +65,10 @@ A regular paragraph. public class DropdownTitleTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{dropdown} This is my custom dropdown +:::{dropdown} This is my custom dropdown :open: This is an attention block -``` +::: A regular paragraph. """ ) diff --git a/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs b/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs index 356b9d08a..675646d56 100644 --- a/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/AdmonitionUnsupportedTests.cs @@ -11,11 +11,11 @@ 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] diff --git a/tests/Elastic.Markdown.Tests/Directives/AppliesBlockTests.cs b/tests/Elastic.Markdown.Tests/Directives/AppliesBlockTests.cs index ba56b693d..5bcded536 100644 --- a/tests/Elastic.Markdown.Tests/Directives/AppliesBlockTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/AppliesBlockTests.cs @@ -12,9 +12,9 @@ namespace Elastic.Markdown.Tests.Directives; public class AppliesBlockTests(ITestOutputHelper output) : DirectiveTest(output, """ # heading -```{applies} +:::{applies} :eck: unavailable -``` +::: """ ) { diff --git a/tests/Elastic.Markdown.Tests/Directives/ImageTests.cs b/tests/Elastic.Markdown.Tests/Directives/ImageTests.cs index d2a862fad..d0dfb0bfe 100644 --- a/tests/Elastic.Markdown.Tests/Directives/ImageTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/ImageTests.cs @@ -12,10 +12,10 @@ namespace Elastic.Markdown.Tests.Directives; public class ImageBlockTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{image} img/observability.png +:::{image} img/observability.png :alt: Elasticsearch :width: 250px -``` +::: """ ) { @@ -43,13 +43,13 @@ public void ImageIsFoundSoNoErrorIsEmitted() public class FigureTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{figure} https://github.com/rowanc1/pics/blob/main/sunset.png?raw=true +:::{figure} https://github.com/rowanc1/pics/blob/main/sunset.png?raw=true :label: myFigure :alt: Sunset at the beach :align: center Relaxing at the beach 🏝 🌊 😎 -``` +::: """ ) { diff --git a/tests/Elastic.Markdown.Tests/Directives/MermaidTests.cs b/tests/Elastic.Markdown.Tests/Directives/MermaidTests.cs index b4d2dd614..88721444c 100644 --- a/tests/Elastic.Markdown.Tests/Directives/MermaidTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/MermaidTests.cs @@ -10,7 +10,7 @@ namespace Elastic.Markdown.Tests.Directives; public class MermaidBlockTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{mermaid} /_static/img/observability.png +:::{mermaid} /_static/img/observability.png flowchart LR A[Jupyter Notebook] --> C B[MyST Markdown] --> C @@ -21,7 +21,7 @@ B[MyST Markdown] --> C D --> H[React] D --> I[HTML] D <--> J[JATS] -``` +::: """ ) { diff --git a/tests/Elastic.Markdown.Tests/Directives/TabTests.cs b/tests/Elastic.Markdown.Tests/Directives/TabTests.cs index 735f8f997..f31b05f00 100644 --- a/tests/Elastic.Markdown.Tests/Directives/TabTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/TabTests.cs @@ -10,22 +10,22 @@ namespace Elastic.Markdown.Tests.Directives; public class TabTests(ITestOutputHelper output) : DirectiveTest(output, """ -`````{tab-set} +:::::{tab-set} -````{tab-item} Admonition -```{tip} +::::{tab-item} Admonition +:::{tip} Tabs are easy. You can even embed other directives like the admonition you see here. -``` -```` +::: +:::: -````{tab-item} Text +::::{tab-item} Text # Markdown And of course you can use regular markdown -```` +:::: -````{tab-item} Code +::::{tab-item} Code # Getting started with SQL ```sql @@ -35,8 +35,8 @@ And of course you can use regular markdown Dan Simmons |Hyperion |482 |1989-05-26T00:00:00.000Z Frank Herbert |Dune |604 |1965-06-01T00:00:00.000Z ``` -```` -````` +:::: +::::: """ ) { @@ -58,14 +58,16 @@ public void ParsesTabItems() public class MultipleTabTests(ITestOutputHelper output) : DirectiveTest(output, """ -`````{tab-set} -````{tab-item} Admonition -```{tip} +:::::{tab-set} +::::{tab-item} Admonition +:::{tip} Tabs are easy. You can even embed other directives like the admonition you see here. -``` -```` -````` +::: +:::: +::::: + Paragraph + :::::{tab-set} ::::{tab-item} Admonition :::{tip} @@ -95,45 +97,45 @@ public void ParsesMultipleTabSets() } public class GroupTabTests(ITestOutputHelper output) : DirectiveTest(output, - """ - ::::{tab-set} - :group: languages - :::{tab-item} Java - :sync: java - Content for Java tab - ::: - - :::{tab-item} Golang - :sync: golang - Content for Golang tab - ::: - - :::{tab-item} C# - :sync: csharp - Content for C# tab - ::: - - :::: - - ::::{tab-set} - :group: languages - :::{tab-item} Java - :sync: java - Content for Java tab - ::: - - :::{tab-item} Golang - :sync: golang - Content for Golang tab - ::: - - :::{tab-item} C# - :sync: csharp - Content for C# tab - ::: - - :::: - """ +""" +::::{tab-set} +:group: languages +:::{tab-item} Java +:sync: java +Content for Java tab +::: + +:::{tab-item} Golang +:sync: golang +Content for Golang tab +::: + +:::{tab-item} C# +:sync: csharp +Content for C# tab +::: + +:::: + +::::{tab-set} +:group: languages +:::{tab-item} Java +:sync: java +Content for Java tab +::: + +:::{tab-item} Golang +:sync: golang +Content for Golang tab +::: + +:::{tab-item} C# +:sync: csharp +Content for C# tab +::: + +:::: +""" ) { [Fact] diff --git a/tests/Elastic.Markdown.Tests/Directives/VersionTests.cs b/tests/Elastic.Markdown.Tests/Directives/VersionTests.cs index f5fc59cda..ebfbfa19b 100644 --- a/tests/Elastic.Markdown.Tests/Directives/VersionTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/VersionTests.cs @@ -12,9 +12,9 @@ namespace Elastic.Markdown.Tests.Directives; public abstract class VersionTests(ITestOutputHelper output, string directive) : DirectiveTest(output, $$""" -```{{{directive}}} 1.0.1-beta1 more information +:::{{{directive}}} 1.0.1-beta1 more information Version brief summary -``` +::: A regular paragraph. """ ) @@ -53,9 +53,9 @@ public class VersionDeprectatedTests(ITestOutputHelper output) : VersionTests(ou public abstract class VersionValidationTests(ITestOutputHelper output, string version) : DirectiveTest(output, $$""" -```{versionchanged} {{version}} more information +:::{versionchanged} {{version}} more information Version brief summary -``` +::: A regular paragraph. """ ); diff --git a/tests/Elastic.Markdown.Tests/FileInclusion/IncludeTests.cs b/tests/Elastic.Markdown.Tests/FileInclusion/IncludeTests.cs index 274b6ac48..5e05902aa 100644 --- a/tests/Elastic.Markdown.Tests/FileInclusion/IncludeTests.cs +++ b/tests/Elastic.Markdown.Tests/FileInclusion/IncludeTests.cs @@ -14,8 +14,8 @@ namespace Elastic.Markdown.Tests.FileInclusion; public class IncludeTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{include} _snippets/test.md -``` +:::{include} _snippets/test.md +::: """ ) { @@ -45,8 +45,8 @@ public class IncludeSubstitutionTests(ITestOutputHelper output) : DirectiveTest< sub: foo: "bar" --- -```{include} _snippets/test.md -``` +:::{include} _snippets/test.md +::: """ ) { @@ -71,8 +71,8 @@ public void InclusionInheritsYamlContext() => public class IncludeNotFoundTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{include} _snippets/notfound.md -``` +:::{include} _snippets/notfound.md +::: """ ) { @@ -94,8 +94,8 @@ public void EmitsError() public class IncludeRequiresArgument(ITestOutputHelper output) : DirectiveTest(output, """ -```{include} -``` +:::{include} +::: """ ) { diff --git a/tests/Elastic.Markdown.Tests/FileInclusion/LiteralIncludeTests.cs b/tests/Elastic.Markdown.Tests/FileInclusion/LiteralIncludeTests.cs index 82043a1e9..7176cdad9 100644 --- a/tests/Elastic.Markdown.Tests/FileInclusion/LiteralIncludeTests.cs +++ b/tests/Elastic.Markdown.Tests/FileInclusion/LiteralIncludeTests.cs @@ -13,9 +13,9 @@ namespace Elastic.Markdown.Tests.FileInclusion; public class LiteralIncludeUsingPropertyTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{include} _snippets/test.txt +:::{include} _snippets/test.txt :literal: true -``` +::: """ ) { @@ -39,8 +39,8 @@ public void IncludesInclusionHtml() => public class LiteralIncludeTests(ITestOutputHelper output) : DirectiveTest(output, """ -```{literalinclude} _snippets/test.md -``` +:::{literalinclude} _snippets/test.md +::: """ ) { diff --git a/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs index 91be9bef4..2be2c78d5 100644 --- a/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs @@ -13,10 +13,10 @@ namespace Elastic.Markdown.Tests.Inline; public abstract class DirectiveBlockLinkTests(ITestOutputHelper output, [LanguageInjection("markdown")] string content) : InlineTest(output, $$""" -```{warning} +:::{warning} :name: caution_ref This is a 'warning' admonition -``` +::: {{content}} @@ -31,10 +31,10 @@ protected override void AddToFileSystem(MockFileSystem fileSystem) title: Special Requirements --- -```{important} +:::{important} :name: hint_ref This is a 'important' admonition -``` +::: """; fileSystem.AddFile(@"docs/source/testing/req.md", inclusion); fileSystem.AddFile(@"docs/source/_static/img/observability.png", new MockFileData("")); diff --git a/tests/Elastic.Markdown.Tests/SettingsInclusion/IncludeTests.cs b/tests/Elastic.Markdown.Tests/SettingsInclusion/IncludeTests.cs index 0b55a97aa..813c06b50 100644 --- a/tests/Elastic.Markdown.Tests/SettingsInclusion/IncludeTests.cs +++ b/tests/Elastic.Markdown.Tests/SettingsInclusion/IncludeTests.cs @@ -14,8 +14,8 @@ namespace Elastic.Markdown.Tests.SettingsInclusion; public class IncludeTests(ITestOutputHelper output) : DirectiveTest(output, $$""" -```{settings} /{{SettingsPath.Replace("docs/source/", "")}} -``` +:::{settings} /{{SettingsPath.Replace("docs/source/", "")}} +::: """ ) { @@ -43,8 +43,8 @@ public void IncludesInclusionHtml() => } public class RandomFileEmitsAnError(ITestOutputHelper output) : DirectiveTest(output, """ -```{settings} _snippets/test.md -``` +:::{settings} _snippets/test.md +::: """ ) {