Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sebastienros/fluid#578 Implement tag names #579

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions Fluid.Tests/Extensibility/ExtensibilityTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Fluid.Ast;
using Fluid.Parser;
using Parlot.Fluent;
using System;
using Xunit;
Expand All @@ -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]
Expand Down Expand Up @@ -53,10 +56,13 @@ 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.Equal("hello", ((IHasTagName)template.Statements[0]).TagName);
Assert.Equal("test", ((IHasValue)template.Statements[0]).Value);
}

[Fact]
Expand All @@ -70,10 +76,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]
Expand All @@ -88,10 +96,13 @@ 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.Equal("hello", ((IHasTagName)template.Statements[0]).TagName);
Assert.Equal("test", ((IHasValue)template.Statements[0]).Value);
}

[Fact]
Expand Down
8 changes: 4 additions & 4 deletions Fluid/FluidParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,25 +540,25 @@ public void RegisterExpressionTag(string tagName, Func<Expression, TextWriter, T
public void RegisterParserBlock<T>(string tagName, Parser<T> parser, Func<T, IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
{
RegisteredTags[tagName] = parser.AndSkip(TagEnd).And(AnyTagsList).AndSkip(CreateTag("end" + tagName).ElseError($"'{{% end{tagName} %}}' was expected"))
.Then<Statement>(x => new ParserBlockStatement<T>(x.Item1, x.Item2, render))
.Then<Statement>(x => new ParserBlockStatement<T>(tagName, x.Item1, x.Item2, render))
.ElseError($"Invalid {tagName} tag")
;
}

public void RegisterParserTag<T>(string tagName, Parser<T> parser, Func<T, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
{
RegisteredTags[tagName] = parser.AndSkip(TagEnd).Then<Statement>(x => new ParserTagStatement<T>(x, render));
RegisteredTags[tagName] = parser.AndSkip(TagEnd).Then<Statement>(x => new ParserTagStatement<T>(tagName, x, render));
}

public void RegisterEmptyTag(string tagName, Func<TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
{
RegisteredTags[tagName] = TagEnd.Then<Statement>(x => new EmptyTagStatement(render)).ElseError($"Unexpected arguments in {tagName} tag");
RegisteredTags[tagName] = TagEnd.Then<Statement>(x => new EmptyTagStatement(tagName, render)).ElseError($"Unexpected arguments in {tagName} tag");
}

public void RegisterEmptyBlock(string tagName, Func<IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
{
RegisteredTags[tagName] = TagEnd.SkipAnd(AnyTagsList).AndSkip(CreateTag("end" + tagName).ElseError($"'{{% end{tagName} %}}' was expected"))
.Then<Statement>(x => new EmptyBlockStatement(x, render))
.Then<Statement>(x => new EmptyBlockStatement(tagName, x, render))
.ElseError($"Invalid '{tagName}' tag")
;
}
Expand Down
4 changes: 3 additions & 1 deletion Fluid/IFluidTemplate.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down
6 changes: 4 additions & 2 deletions Fluid/Parser/EmptyBlockStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@

namespace Fluid.Parser
{
internal sealed class EmptyBlockStatement : Statement
internal sealed class EmptyBlockStatement : Statement, IHasTagName
{
private readonly Func<IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> _render;

public EmptyBlockStatement(List<Statement> statements, Func<IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
public EmptyBlockStatement(string tagName, List<Statement> statements, Func<IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
{
_render = render ?? throw new ArgumentNullException(nameof(render));
TagName = tagName;
Statements = statements ?? throw new ArgumentNullException(nameof(statements));
}

public string TagName { get; init; }
public List<Statement> Statements { get; }

public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
Expand Down
7 changes: 5 additions & 2 deletions Fluid/Parser/EmptyTagStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@

namespace Fluid.Parser
{
internal sealed class EmptyTagStatement : Statement
internal sealed class EmptyTagStatement : Statement, IHasTagName
{
private readonly Func<TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> _render;

public EmptyTagStatement(Func<TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
public EmptyTagStatement(string tagName, Func<TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
{
_render = render ?? throw new ArgumentNullException(nameof(render));
TagName = tagName;
}

public string TagName { get; init; }

public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
return _render(writer, encoder, context);
Expand Down
11 changes: 11 additions & 0 deletions Fluid/Parser/IHasTagName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Fluid.Parser
{
public interface IHasTagName
{
string TagName { get; }
}
}
16 changes: 16 additions & 0 deletions Fluid/Parser/IHasValue.cs
Original file line number Diff line number Diff line change
@@ -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<out T> : IHasValue
{
new T Value { get; }
}
}
7 changes: 5 additions & 2 deletions Fluid/Parser/ParserBlockStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@

namespace Fluid.Parser
{
internal sealed class ParserBlockStatement<T> : TagStatement
internal sealed class ParserBlockStatement<T> : TagStatement, IHasTagName, IHasValue<T>
{
private readonly Func<T, IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> _render;

public ParserBlockStatement(T value, List<Statement> statements, Func<T, IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render) : base(statements)
public ParserBlockStatement(string tagName, T value, List<Statement> statements, Func<T, IReadOnlyList<Statement>, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render) : base(statements)
{
Value = value;
TagName = tagName;
_render = render ?? throw new ArgumentNullException(nameof(render));
}

public T Value { get; }
public string TagName { get; init; }
object IHasValue.Value => Value;

public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
Expand Down
7 changes: 5 additions & 2 deletions Fluid/Parser/ParserTagStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@

namespace Fluid.Parser
{
internal sealed class ParserTagStatement<T> : Statement
internal sealed class ParserTagStatement<T> : Statement, IHasTagName, IHasValue<T>
{
private readonly Func<T, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> _render;

public ParserTagStatement(T value, Func<T, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
public ParserTagStatement(string tagName, T value, Func<T, TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> render)
{
TagName = tagName;
Value = value;
_render = render ?? throw new ArgumentNullException(nameof(render));
}

public string TagName { get; init; }
public T Value { get; }
object IHasValue.Value => Value;

public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
Expand Down