diff --git a/Fluid.Tests/ParserTests.cs b/Fluid.Tests/ParserTests.cs index 7cee0738..982b1692 100644 --- a/Fluid.Tests/ParserTests.cs +++ b/Fluid.Tests/ParserTests.cs @@ -205,6 +205,45 @@ public void ShouldParseCommentWithBlocks() Assert.Equal(" {%if true%} {%endif%} ", (statements.ElementAt(0) as CommentStatement).Text.ToString()); } + [Fact] + public void ShouldParseInlineComment() + { + var statements = Parse(@"{% # this is an inline comment %}"); + + Assert.Single(statements); + Assert.IsType(statements.ElementAt(0)); + Assert.Equal(" this is an inline comment", (statements.ElementAt(0) as CommentStatement).Text.ToString()); + } + + [Fact] + public void ShouldParseEmptyInlineComment() + { + var statements = Parse(@"{% #%}"); + + Assert.Single(statements); + Assert.IsType(statements.ElementAt(0)); + Assert.Equal("", (statements.ElementAt(0) as CommentStatement).Text.ToString()); + } + + [Fact] + public void ShouldParseInlineCommentWithoutLiquidTags() + { + var statements = Parse(@"{% # this is a simple comment %}"); + + Assert.Single(statements); + Assert.IsType(statements.ElementAt(0)); + } + + [Fact] + public void ShouldParseInlineCommentWithWhitespaceTrim() + { + var statements = Parse(@"{%- # this is a trimmed comment -%}"); + + Assert.Single(statements); + Assert.IsType(statements.ElementAt(0)); + Assert.Equal(" this is a trimmed comment", (statements.ElementAt(0) as CommentStatement).Text.ToString()); + } + [Fact] public void ShouldParseIfTag() { diff --git a/Fluid.Tests/TemplateTests.cs b/Fluid.Tests/TemplateTests.cs index 0fc94326..91e588a3 100644 --- a/Fluid.Tests/TemplateTests.cs +++ b/Fluid.Tests/TemplateTests.cs @@ -1209,5 +1209,63 @@ public async Task ArraysShouldCompareElements() var result = await template.RenderAsync(context); Assert.Contains("true", result); } + + [Fact] + public async Task InlineCommentShouldNotRender() + { + var source = "Hello {% # this is a comment %} World"; + await CheckAsync(source, "Hello World"); + } + + [Fact] + public async Task InlineCommentShouldNotRenderAnyContent() + { + var source = "{% # this is a comment with text %}Result"; + await CheckAsync(source, "Result"); + } + + [Fact] + public async Task InlineCommentShouldWorkWithWhitespaceTrim() + { + var source = "Hello{%- # this is a comment -%}World"; + await CheckAsync(source, "HelloWorld"); + } + + [Fact] + public async Task InlineCommentShouldWorkInTemplates() + { + var source = @" + {% # Start of template %} + {% assign name = 'John' %} + {% # Output the name %} + Hello {{ name }}! + {% # End of template %} + "; + + _parser.TryParse(source, out var template, out var error); + var context = new TemplateContext(); + var result = await template.RenderAsync(context); + Assert.Contains("Hello John!", result); + Assert.DoesNotContain("Start of template", result); + Assert.DoesNotContain("Output the name", result); + Assert.DoesNotContain("End of template", result); + } + + [Fact] + public async Task InlineCommentShouldWorkBetweenTags() + { + var source = @" + {% if true %} + {% # This is between if tags %} + Success + {% endif %} + "; + + _parser.TryParse(source, out var template, out var error); + var context = new TemplateContext(); + var result = await template.RenderAsync(context); + Assert.Contains("Success", result); + Assert.DoesNotContain("This is between if tags", result); + } } } diff --git a/Fluid/FluidParser.cs b/Fluid/FluidParser.cs index 8c09798e..22bb4556 100644 --- a/Fluid/FluidParser.cs +++ b/Fluid/FluidParser.cs @@ -304,6 +304,13 @@ public FluidParser(FluidParserOptions parserOptions) ; CommentTag.Name = "CommentTag"; + var InlineCommentTag = AnyCharBefore(TagEnd, canBeEmpty: true) + .AndSkip(TagEnd) + .Then(x => new CommentStatement(x)) + .ElseError("Invalid inline comment tag") + ; + InlineCommentTag.Name = "InlineCommentTag"; + var CaptureTag = Identifier.ElseError(string.Format(ErrorMessages.IdentifierAfterTag, "capture")) .AndSkip(TagEnd) .And(AnyTagsList) @@ -507,6 +514,7 @@ public FluidParser(FluidParserOptions parserOptions) RegisteredTags["break"] = BreakTag; RegisteredTags["continue"] = ContinueTag; RegisteredTags["comment"] = CommentTag; + RegisteredTags["#"] = InlineCommentTag; RegisteredTags["capture"] = CaptureTag; RegisteredTags["cycle"] = CycleTag; RegisteredTags["decrement"] = DecrementTag; @@ -565,7 +573,10 @@ public FluidParser(FluidParserOptions parserOptions) return ReadFromList(modifiers); } - var AnyTags = TagStart.SkipAnd(Identifier.ElseError(ErrorMessages.IdentifierAfterTagStart).Switch((context, previous) => + var AnyTags = TagStart.SkipAnd(OneOf( + Terms.Char('#').Then(x => "#"), + Identifier.ElseError(ErrorMessages.IdentifierAfterTagStart) + ).Switch((context, previous) => { // Because tags like 'else' are not listed, they won't count in TagsList, and will stop being processed // as inner tags in blocks like {% if %} TagsList {% endif $} @@ -582,7 +593,10 @@ public FluidParser(FluidParserOptions parserOptions) } })); - var KnownTags = TagStart.SkipAnd(Identifier.ElseError(ErrorMessages.IdentifierAfterTagStart).Switch((context, previous) => + var KnownTags = TagStart.SkipAnd(OneOf( + Terms.Char('#').Then(x => "#"), + Identifier.ElseError(ErrorMessages.IdentifierAfterTagStart) + ).Switch((context, previous) => { // Because tags like 'else' are not listed, they won't count in TagsList, and will stop being processed // as inner tags in blocks like {% if %} TagsList {% endif $}