From ac81a31289d17ccaef19a725d877077483e26a61 Mon Sep 17 00:00:00 2001 From: knightmeister Date: Wed, 14 Jun 2023 10:30:46 +1000 Subject: [PATCH 1/2] sebastienros/fluid#578 Implement tag names --- Fluid.Tests/Extensibility/ExtensibilityTests.cs | 17 +++++++++++++---- Fluid/FluidParser.cs | 8 ++++---- Fluid/IFluidTemplate.cs | 4 +++- Fluid/Parser/EmptyBlockStatement.cs | 6 ++++-- Fluid/Parser/EmptyTagStatement.cs | 7 +++++-- Fluid/Parser/IHasTagName.cs | 11 +++++++++++ Fluid/Parser/ParserBlockStatement.cs | 6 ++++-- Fluid/Parser/ParserTagStatement.cs | 6 ++++-- 8 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 Fluid/Parser/IHasTagName.cs diff --git a/Fluid.Tests/Extensibility/ExtensibilityTests.cs b/Fluid.Tests/Extensibility/ExtensibilityTests.cs index 6f53eebb..bce89fc8 100644 --- a/Fluid.Tests/Extensibility/ExtensibilityTests.cs +++ b/Fluid.Tests/Extensibility/ExtensibilityTests.cs @@ -1,4 +1,5 @@ using Fluid.Ast; +using Fluid.Parser; using Parlot.Fluent; using System; using Xunit; @@ -19,10 +20,12 @@ public void ShouldRenderEmptyTags() return Statement.Normal(); }); - var template = parser.Parse("{% hello %}"); + var template = (FluidTemplate) parser.Parse("{% hello %}"); var result = template.Render(); Assert.Equal("Hello World", result); + Assert.True(template.Statements.Count == 1); + Assert.True(((IHasTagName)template.Statements[0]).TagName == "hello"); } [Fact] @@ -53,10 +56,12 @@ public void ShouldRenderIdentifierTags() return Statement.Normal(); }); - var template = parser.Parse("{% hello test %}"); + var template = (FluidTemplate)parser.Parse("{% hello test %}"); var result = template.Render(); Assert.Equal("Hello test", result); + Assert.True(template.Statements.Count == 1); + Assert.True(((IHasTagName)template.Statements[0]).TagName == "hello"); } [Fact] @@ -70,10 +75,12 @@ public void ShouldRenderEmptyBlocks() return s.RenderStatementsAsync(w, e, c); }); - var template = parser.Parse("{% hello %} hi {%- endhello %}"); + var template = (FluidTemplate)parser.Parse("{% hello %} hi {%- endhello %}"); var result = template.Render(); Assert.Equal("Hello World hi", result); + Assert.True(template.Statements.Count == 1); + Assert.True(((IHasTagName)template.Statements[0]).TagName == "hello"); } [Fact] @@ -88,10 +95,12 @@ public void ShouldRenderIdentifierBlocks() return s.RenderStatementsAsync(w, e, c); }); - var template = parser.Parse("{% hello test %} hi {%- endhello %}"); + var template = (FluidTemplate)parser.Parse("{% hello test %} hi {%- endhello %}"); var result = template.Render(); Assert.Equal("Hello test hi", result); + Assert.True(template.Statements.Count == 1); + Assert.True(((IHasTagName)template.Statements[0]).TagName == "hello"); } [Fact] diff --git a/Fluid/FluidParser.cs b/Fluid/FluidParser.cs index be6d56d0..5159dd64 100644 --- a/Fluid/FluidParser.cs +++ b/Fluid/FluidParser.cs @@ -540,25 +540,25 @@ public void RegisterExpressionTag(string tagName, Func(string tagName, Parser parser, Func, TextWriter, TextEncoder, TemplateContext, ValueTask> render) { RegisteredTags[tagName] = parser.AndSkip(TagEnd).And(AnyTagsList).AndSkip(CreateTag("end" + tagName).ElseError($"'{{% end{tagName} %}}' was expected")) - .Then(x => new ParserBlockStatement(x.Item1, x.Item2, render)) + .Then(x => new ParserBlockStatement(tagName, x.Item1, x.Item2, render)) .ElseError($"Invalid {tagName} tag") ; } public void RegisterParserTag(string tagName, Parser parser, Func> render) { - RegisteredTags[tagName] = parser.AndSkip(TagEnd).Then(x => new ParserTagStatement(x, render)); + RegisteredTags[tagName] = parser.AndSkip(TagEnd).Then(x => new ParserTagStatement(tagName, x, render)); } public void RegisterEmptyTag(string tagName, Func> render) { - RegisteredTags[tagName] = TagEnd.Then(x => new EmptyTagStatement(render)).ElseError($"Unexpected arguments in {tagName} tag"); + RegisteredTags[tagName] = TagEnd.Then(x => new EmptyTagStatement(tagName, render)).ElseError($"Unexpected arguments in {tagName} tag"); } public void RegisterEmptyBlock(string tagName, Func, TextWriter, TextEncoder, TemplateContext, ValueTask> render) { RegisteredTags[tagName] = TagEnd.SkipAnd(AnyTagsList).AndSkip(CreateTag("end" + tagName).ElseError($"'{{% end{tagName} %}}' was expected")) - .Then(x => new EmptyBlockStatement(x, render)) + .Then(x => new EmptyBlockStatement(tagName, x, render)) .ElseError($"Invalid '{tagName}' tag") ; } diff --git a/Fluid/IFluidTemplate.cs b/Fluid/IFluidTemplate.cs index 18ed4e95..a852eaf3 100644 --- a/Fluid/IFluidTemplate.cs +++ b/Fluid/IFluidTemplate.cs @@ -1,6 +1,8 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Text.Encodings.Web; using System.Threading.Tasks; +using Fluid.Ast; namespace Fluid { diff --git a/Fluid/Parser/EmptyBlockStatement.cs b/Fluid/Parser/EmptyBlockStatement.cs index 8f8561d2..a17aada6 100644 --- a/Fluid/Parser/EmptyBlockStatement.cs +++ b/Fluid/Parser/EmptyBlockStatement.cs @@ -7,16 +7,18 @@ namespace Fluid.Parser { - internal sealed class EmptyBlockStatement : Statement + internal sealed class EmptyBlockStatement : Statement, IHasTagName { private readonly Func, TextWriter, TextEncoder, TemplateContext, ValueTask> _render; - public EmptyBlockStatement(List statements, Func, TextWriter, TextEncoder, TemplateContext, ValueTask> render) + public EmptyBlockStatement(string tagName, List statements, Func, TextWriter, TextEncoder, TemplateContext, ValueTask> render) { _render = render ?? throw new ArgumentNullException(nameof(render)); + TagName = tagName; Statements = statements ?? throw new ArgumentNullException(nameof(statements)); } + public string TagName { get; init; } public List Statements { get; } public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) diff --git a/Fluid/Parser/EmptyTagStatement.cs b/Fluid/Parser/EmptyTagStatement.cs index f0df9a45..4fb910df 100644 --- a/Fluid/Parser/EmptyTagStatement.cs +++ b/Fluid/Parser/EmptyTagStatement.cs @@ -6,15 +6,18 @@ namespace Fluid.Parser { - internal sealed class EmptyTagStatement : Statement + internal sealed class EmptyTagStatement : Statement, IHasTagName { private readonly Func> _render; - public EmptyTagStatement(Func> render) + public EmptyTagStatement(string tagName, Func> render) { _render = render ?? throw new ArgumentNullException(nameof(render)); + TagName = tagName; } + public string TagName { get; init; } + public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { return _render(writer, encoder, context); diff --git a/Fluid/Parser/IHasTagName.cs b/Fluid/Parser/IHasTagName.cs new file mode 100644 index 00000000..04002636 --- /dev/null +++ b/Fluid/Parser/IHasTagName.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Fluid.Parser +{ + public interface IHasTagName + { + string TagName { get; } + } +} diff --git a/Fluid/Parser/ParserBlockStatement.cs b/Fluid/Parser/ParserBlockStatement.cs index f196ec7e..6cdaf215 100644 --- a/Fluid/Parser/ParserBlockStatement.cs +++ b/Fluid/Parser/ParserBlockStatement.cs @@ -7,17 +7,19 @@ namespace Fluid.Parser { - internal sealed class ParserBlockStatement : TagStatement + internal sealed class ParserBlockStatement : TagStatement, IHasTagName { private readonly Func, TextWriter, TextEncoder, TemplateContext, ValueTask> _render; - public ParserBlockStatement(T value, List statements, Func, TextWriter, TextEncoder, TemplateContext, ValueTask> render) : base(statements) + public ParserBlockStatement(string tagName, T value, List statements, Func, TextWriter, TextEncoder, TemplateContext, ValueTask> render) : base(statements) { Value = value; + TagName = tagName; _render = render ?? throw new ArgumentNullException(nameof(render)); } public T Value { get; } + public string TagName { get; init; } public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { diff --git a/Fluid/Parser/ParserTagStatement.cs b/Fluid/Parser/ParserTagStatement.cs index 679fccac..7193c5ec 100644 --- a/Fluid/Parser/ParserTagStatement.cs +++ b/Fluid/Parser/ParserTagStatement.cs @@ -6,16 +6,18 @@ namespace Fluid.Parser { - internal sealed class ParserTagStatement : Statement + internal sealed class ParserTagStatement : Statement, IHasTagName { private readonly Func> _render; - public ParserTagStatement(T value, Func> render) + public ParserTagStatement(string tagName, T value, Func> render) { + TagName = tagName; Value = value; _render = render ?? throw new ArgumentNullException(nameof(render)); } + public string TagName { get; init; } public T Value { get; } public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) From 4376b98984698cf32edf9cf239d9b736f908450f Mon Sep 17 00:00:00 2001 From: knightmeister Date: Wed, 14 Jun 2023 11:25:11 +1000 Subject: [PATCH 2/2] sebastienros/fluid#578 Implement introspectio of the tag value --- Fluid.Tests/Extensibility/ExtensibilityTests.cs | 6 ++++-- Fluid/Parser/IHasValue.cs | 16 ++++++++++++++++ Fluid/Parser/ParserBlockStatement.cs | 3 ++- Fluid/Parser/ParserTagStatement.cs | 3 ++- 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 Fluid/Parser/IHasValue.cs diff --git a/Fluid.Tests/Extensibility/ExtensibilityTests.cs b/Fluid.Tests/Extensibility/ExtensibilityTests.cs index bce89fc8..567c41a4 100644 --- a/Fluid.Tests/Extensibility/ExtensibilityTests.cs +++ b/Fluid.Tests/Extensibility/ExtensibilityTests.cs @@ -61,7 +61,8 @@ public void ShouldRenderIdentifierTags() Assert.Equal("Hello test", result); Assert.True(template.Statements.Count == 1); - Assert.True(((IHasTagName)template.Statements[0]).TagName == "hello"); + Assert.Equal("hello", ((IHasTagName)template.Statements[0]).TagName); + Assert.Equal("test", ((IHasValue)template.Statements[0]).Value); } [Fact] @@ -100,7 +101,8 @@ public void ShouldRenderIdentifierBlocks() Assert.Equal("Hello test hi", result); Assert.True(template.Statements.Count == 1); - Assert.True(((IHasTagName)template.Statements[0]).TagName == "hello"); + Assert.Equal("hello", ((IHasTagName)template.Statements[0]).TagName); + Assert.Equal("test", ((IHasValue)template.Statements[0]).Value); } [Fact] diff --git a/Fluid/Parser/IHasValue.cs b/Fluid/Parser/IHasValue.cs new file mode 100644 index 00000000..c5bd24c0 --- /dev/null +++ b/Fluid/Parser/IHasValue.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Fluid.Parser +{ + public interface IHasValue + { + object Value { get; } + } + + public interface IHasValue : IHasValue + { + new T Value { get; } + } +} diff --git a/Fluid/Parser/ParserBlockStatement.cs b/Fluid/Parser/ParserBlockStatement.cs index 6cdaf215..9c496eed 100644 --- a/Fluid/Parser/ParserBlockStatement.cs +++ b/Fluid/Parser/ParserBlockStatement.cs @@ -7,7 +7,7 @@ namespace Fluid.Parser { - internal sealed class ParserBlockStatement : TagStatement, IHasTagName + internal sealed class ParserBlockStatement : TagStatement, IHasTagName, IHasValue { private readonly Func, TextWriter, TextEncoder, TemplateContext, ValueTask> _render; @@ -20,6 +20,7 @@ public ParserBlockStatement(string tagName, T value, List statements, public T Value { get; } public string TagName { get; init; } + object IHasValue.Value => Value; public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { diff --git a/Fluid/Parser/ParserTagStatement.cs b/Fluid/Parser/ParserTagStatement.cs index 7193c5ec..fbef9abe 100644 --- a/Fluid/Parser/ParserTagStatement.cs +++ b/Fluid/Parser/ParserTagStatement.cs @@ -6,7 +6,7 @@ namespace Fluid.Parser { - internal sealed class ParserTagStatement : Statement, IHasTagName + internal sealed class ParserTagStatement : Statement, IHasTagName, IHasValue { private readonly Func> _render; @@ -19,6 +19,7 @@ public ParserTagStatement(string tagName, T value, Func Value; public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) {