Skip to content

Commit

Permalink
Ensure include/literalinclude emits validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Mpdreamz committed Nov 13, 2024
1 parent a488085 commit 520a710
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 31 deletions.
21 changes: 14 additions & 7 deletions src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,25 @@ public abstract class DirectiveBlock(DirectiveBlockParser parser, Dictionary<str
/// <param name="context"></param>
public abstract void FinalizeAndValidate(ParserContext context);

protected void ParseBool(string key, Action<bool> setter)
protected bool PropBool(params string[] keys)
{
var value = Properties.GetValueOrDefault(key);
var value = Prop(keys);
if (string.IsNullOrEmpty(value))
return Properties.ContainsKey(key);

return bool.TryParse(value, out var result) && result;
}

protected string? Prop(params string[] keys)
{
foreach (var key in keys)
{
setter(Properties.ContainsKey(key));
return;
if (Properties.TryGetValue(key, out var value))
return value;
}

if (bool.TryParse(value, out var result))
setter(result);
//todo invalidate
return default;
}


}
48 changes: 27 additions & 21 deletions src/Elastic.Markdown/Myst/Directives/IncludeBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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 System.IO.Abstractions;
using Elastic.Markdown.Diagnostics;

namespace Elastic.Markdown.Myst.Directives;

Expand All @@ -14,43 +15,46 @@ public class IncludeBlock(DirectiveBlockParser parser, Dictionary<string, string

public IDirectoryInfo DocumentationSourcePath { get; } = context.Parser.SourcePath;

public IFileInfo IncludeFromPath { get; } = context.Path;

public YamlFrontMatter? FrontMatter { get; } = context.FrontMatter;

public string? IncludePath { get; private set; }

public bool Literal { get; protected set; }
public bool Found { get; private set; }

protected virtual string Directive { get; } = "include";

public bool Literal { get; protected set; }
public string? Language { get; private set; }
public string? Caption { get; private set; }
public string? Label { get; private set; }

public bool Found { get; private set; }

//TODO add all options from
//https://mystmd.org/guide/directives#directive-include
public override void FinalizeAndValidate(ParserContext context)
{
var includePath = Arguments; //todo validate
Literal |= bool.TryParse(Properties.GetValueOrDefault("literal"), out var b) && b;
Language = Properties.GetValueOrDefault("language");
if (includePath is null)

Literal |= PropBool("literal");
Language = Prop("lang", "language", "code");
Caption = Prop("caption");
Label = Prop("label");

if (string.IsNullOrWhiteSpace(includePath))
{
//TODO emit empty error
context.EmitError(Line, Column, $"```{{{Directive}}}".Length , "include requires an argument.");
return;
}

var includeFrom = context.Path.Directory!.FullName;
if (includePath.StartsWith('/'))
includeFrom = DocumentationSourcePath.FullName;

IncludePath = Path.Combine(includeFrom, includePath.TrimStart('/'));
if (FileSystem.File.Exists(IncludePath))
Found = true;
else
{
var includeFrom = IncludeFromPath.Directory!.FullName;
if (includePath.StartsWith('/'))
includeFrom = DocumentationSourcePath.FullName;

IncludePath = Path.Combine(includeFrom, includePath.TrimStart('/'));
if (FileSystem.File.Exists(IncludePath))
Found = true;
else
{
//TODO emit error
}
}
context.EmitError(Line, Column, "```{include}".Length , $"`{IncludePath}` does not exist.");


}
Expand All @@ -61,4 +65,6 @@ public class LiteralIncludeBlock : IncludeBlock
{
public LiteralIncludeBlock(DirectiveBlockParser parser, Dictionary<string, string> properties, ParserContext context)
: base(parser, properties, context) => Literal = true;

protected override string Directive { get; } = "literalinclude";
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public class UnsupportedDirectiveBlock(DirectiveBlockParser parser, string direc
public string IssueUrl => $"https://github.com/elastic/docs-builder/issues/{issueId}";

public override void FinalizeAndValidate(ParserContext context) =>
context.EmitWarning(line:1, column:1, length:2, message: $"Directive block '{directive}' is unsupported. See {IssueUrl} for more information.");
context.EmitWarning(line:1, column:1, length:directive.Length, message: $"Directive block '{directive}' is unsupported. See {IssueUrl} for more information.");
}
3 changes: 1 addition & 2 deletions tests/Elastic.Markdown.Tests/Directives/UnsupportedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ A regular paragraph.
[Fact]
public void EmitsUnsupportedWarnings()
{
Collector.Diagnostics.Should().NotBeNullOrEmpty()
.And.HaveCount(1);
Collector.Diagnostics.Should().NotBeNullOrEmpty().And.HaveCount(1);
Collector.Diagnostics.Should().OnlyContain(d => d.Severity == Severity.Warning);
Collector.Diagnostics.Should()
.OnlyContain(d => d.Message.StartsWith($"Directive block '{directive}' is unsupported."));
Expand Down
49 changes: 49 additions & 0 deletions tests/Elastic.Markdown.Tests/FileInclusion/IncludeTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// 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.Diagnostics;
using Elastic.Markdown.Myst.Directives;
using Elastic.Markdown.Tests.Directives;
using FluentAssertions;
Expand Down Expand Up @@ -66,3 +68,50 @@ public void InclusionInheritsYamlContext() =>
.And.Be("<p><em>Hello bar</em></p>\n")
;
}


public class IncludeNotFoundTests(ITestOutputHelper output) : DirectiveTest<IncludeBlock>(output,
"""
```{include} _snippets/notfound.md
```
"""
)
{
[Fact]
public void ParsesBlock() => Block.Should().NotBeNull();

[Fact]
public void IncludesNothing() => Html.Should().Be("");

[Fact]
public void EmitsError()
{
Collector.Diagnostics.Should().NotBeNullOrEmpty().And.HaveCount(1);
Collector.Diagnostics.Should().OnlyContain(d => d.Severity == Severity.Error);
Collector.Diagnostics.Should()
.OnlyContain(d => d.Message.Contains("notfound.md` does not exist"));
}
}

public class IncludeRequiresArgument(ITestOutputHelper output) : DirectiveTest<IncludeBlock>(output,
"""
```{include}
```
"""
)
{
[Fact]
public void ParsesBlock() => Block.Should().NotBeNull();

[Fact]
public void IncludesNothing() => Html.Should().Be("");

[Fact]
public void EmitsError()
{
Collector.Diagnostics.Should().NotBeNullOrEmpty().And.HaveCount(1);
Collector.Diagnostics.Should().OnlyContain(d => d.Severity == Severity.Error);
Collector.Diagnostics.Should()
.OnlyContain(d => d.Message.Contains("include requires an argument."));
}
}

0 comments on commit 520a710

Please sign in to comment.