Skip to content

Commit a8c3f8b

Browse files
committed
- (Lava) Fixed parsing errors caused by nested Lava comments inside '{% lava %}' tag blocks.
1 parent 22a1112 commit a8c3f8b

5 files changed

Lines changed: 262 additions & 18 deletions

File tree

Rock.Lava.Fluid/FluidEngine.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ private FluidTemplate CreateNewFluidTemplate( string lavaTemplate )
606606
IFluidTemplate fluidTemplate;
607607

608608
/*
609-
10/27/2025 - NA
609+
10/27/2025 - N.A.
610610
611611
Added ConvertToLiquidElsif method while having to remove our custom RegisterLavaElseIfTag() in our
612612
custom LavaFluidParser in order to get shortcodes working inside of {% lava %} blocks. It might
@@ -617,7 +617,14 @@ with more effort.
617617
618618
Reason: Ensures proper parsing of conditional blocks when {% lava %} tags and shortcodes are present.
619619
*/
620-
var success = _parser.TryParse( ConvertToLiquidElsif( lavaTemplate ), out fluidTemplate, out error );
620+
621+
/*
622+
11/17/2025 - N.A.
623+
624+
Changed from calling ConvertToLiquidElsif(...) to use the unused ConvertToLiquid(...) method since
625+
it handles both the "elseif" conversion and uses our RemoveLavaComments() method.
626+
*/
627+
var success = _parser.TryParse( ConvertToLiquid( lavaTemplate ), out fluidTemplate, out error );
621628

622629
var fluidTemplateObject = ( FluidTemplate ) fluidTemplate;
623630

Rock.Lava/Engine/LavaToLiquidTemplateConverter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ public string ConvertToLiquid( string lavaTemplateText )
4444
string liquidTemplateText;
4545

4646
liquidTemplateText = RemoveLavaComments( lavaTemplateText );
47-
48-
liquidTemplateText = ReplaceTemplateShortcodes( liquidTemplateText );
4947
liquidTemplateText = ReplaceElseIfKeyword( liquidTemplateText );
5048

5149
return liquidTemplateText;

Rock.Tests.Performance/Benchmarks/Lava/LavaParsingPerformance.cs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Rock.Tests.Performance.Benchmarks.Security
1212
/// Performs some basic performance tests on the...
1313
/// class. TL;DR; It's fast.
1414
/// </summary>
15-
[MemoryDiagnoser( false )]
15+
[MemoryDiagnoser( true )]
1616
[Attributes.OperationsPerSecondColumn]
1717
[GroupBenchmarksBy( BenchmarkLogicalGroupRule.ByCategory )]
1818
[CategoriesColumn]
@@ -22,7 +22,7 @@ public class LavaParsingPerformance
2222

2323
#region Test Data
2424

25-
private readonly string _template = @"
25+
private readonly string _templateIfElseIf = @"
2626
{% liquid
2727
2828
assign NickName = 'Ted'
@@ -43,10 +43,24 @@ public class LavaParsingPerformance
4343
echo result
4444
%}
4545
";
46-
private readonly string _expectedOutput = "male";
46+
private readonly string _expectedOutputIfElseIf = "male";
4747

48-
#endregion
48+
private readonly string _templateWithComments = @"
49+
{% liquid
4950
51+
//- Comment level one
52+
assign isTest = true
53+
if isTest
54+
55+
//- Comment level two
56+
assign isTest = false
57+
58+
endif
59+
echo isTest
60+
%}
61+
";
62+
private readonly string _expectedOutputWithComments = "false";
63+
#endregion
5064

5165
[GlobalSetup]
5266
public void Setup()
@@ -59,25 +73,39 @@ public void Setup()
5973
fluidEngine.Initialize( engineOptions );
6074
fluidEngine.RegisterFilters( typeof( Rock.Lava.Filters.TemplateFilters ) );
6175

62-
// Verify that it's working as expected.
63-
var output = fluidEngine.ParseTemplate( _template );
76+
// Verify both templates are working as expected.
77+
var output = fluidEngine.ParseTemplate( _templateIfElseIf );
6478
if ( output.HasErrors != false )
6579
{
6680
throw new System.Exception( "Lava engine setup failed: the template has errors." );
6781
}
6882

6983
var renderResult = fluidEngine.RenderTemplate( output.Template, new LavaRenderParameters() );
70-
if ( output == null || renderResult.Text.Trim() != _expectedOutput )
84+
if ( output == null || renderResult.Text.Trim() != _expectedOutputIfElseIf )
7185
{
72-
throw new System.Exception( "Lava engine setup failed: unexpected output." );
86+
throw new System.Exception( $"Lava engine setup failed: unexpected _expectedOutputIfElseIf output (was {renderResult.Text.Trim()})" );
87+
}
88+
89+
output = fluidEngine.ParseTemplate( _templateWithComments );
90+
renderResult = fluidEngine.RenderTemplate( output.Template, new LavaRenderParameters() );
91+
if ( output == null || renderResult.Text.Trim() != _expectedOutputWithComments )
92+
{
93+
throw new System.Exception( $"Lava engine setup failed: unexpected _expectedOutputWithComments output (was {renderResult.Text.Trim()})." );
7394
}
7495
}
7596

7697
[Benchmark]
77-
[BenchmarkCategory( "Lava" )]
98+
[BenchmarkCategory( "Lava ElseIf" )]
7899
public LavaParseResult ParseLavaIfElseIfTemplate()
79100
{
80-
return fluidEngine.ParseTemplate( _template );
101+
return fluidEngine.ParseTemplate( _templateIfElseIf );
102+
}
103+
104+
[Benchmark]
105+
[BenchmarkCategory( "Lava Comments" )]
106+
public LavaParseResult ParseLavaTemplateWithComments()
107+
{
108+
return fluidEngine.ParseTemplate( _templateWithComments );
81109
}
82110

83111
}

Rock.Tests/Lava/LiquidKeywordTests.cs

Lines changed: 154 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -604,13 +604,79 @@ echo result
604604
";
605605
var expectedOutput = @"male";
606606

607-
TestHelper.AssertTemplateOutput( expectedOutput, template, mergeValues );
607+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
608+
{
609+
TestHelper.AssertTemplateOutput( engine, expectedOutput, template, mergeValues, ignoreWhitespace: true );
610+
} );
611+
608612
}
609613

614+
/// <summary>
615+
/// Verify that Rock's custom "//-" comments works inside an IF tag that is inside a {% lava %} {% liquid %} tag.
616+
/// </summary>
617+
[TestMethod]
618+
public void LavaTag_WithInnerIfTagAndLineComments_IsProcessedCorrectly()
619+
{
620+
var template = @"
621+
{% liquid
622+
623+
//- Comment level one
624+
assign isTest = true
625+
if isTest
626+
627+
//- Comment level two
628+
assign isTest = false
629+
630+
endif
631+
echo isTest
632+
633+
%}
634+
";
635+
var expectedOutput = @"false";
636+
637+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
638+
{
639+
TestHelper.AssertTemplateOutput( engine, expectedOutput, template, ignoreWhitespace: true );
640+
} );
641+
}
642+
643+
644+
/// <summary>
645+
/// Verify that Rock's custom "/- -/" block comments works inside an IF tag that is inside a {% lava %} {% liquid %} tag.
646+
/// </summary>
647+
[TestMethod]
648+
public void LavaTag_WithCommentesNestedInIfTag_IsProcessedCorrectly()
649+
{
650+
var template = @"
651+
{% lava
652+
//- Comment level one
653+
assign isTest = true
654+
if isTest
655+
656+
/-
657+
assign isTest = false
658+
-/
659+
660+
endif
661+
echo isTest
662+
%}
663+
";
664+
var expectedOutput = @"true";
665+
666+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
667+
{
668+
TestHelper.AssertTemplateOutput( engine, expectedOutput, template, ignoreWhitespace: true );
669+
} );
670+
671+
}
672+
673+
/// <summary>
674+
/// Verify that Rock's custom "/- -/" block comments works inside an IF tag that is inside a {% lava %} {% liquid %} tag.
675+
/// </summary>
610676
[TestMethod]
611677
public void LavaTag_WithInnerIfTagAndBlockComment_SpanningMultipleLines_IsProcessedCorrectly()
612678
{
613-
var input = @"
679+
var template = @"
614680
{% liquid
615681
assign isTest = true
616682
/- This is a block comment...
@@ -623,10 +689,94 @@ ... spanning multiple lines. -/
623689
echo isTest
624690
%}
625691
";
626-
627692
var expectedOutput = @"false";
628693

629-
TestHelper.AssertTemplateOutput( expectedOutput, input );
694+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
695+
{
696+
TestHelper.AssertTemplateOutput( engine, expectedOutput, template, ignoreWhitespace: true );
697+
} );
698+
}
699+
700+
/// <summary>
701+
/// Verify that Rock's custom "//-" block comments works inside a FOR tag that is inside a {% lava %} {% liquid %} tag.
702+
/// </summary>
703+
[TestMethod]
704+
public void LavaTag_WithSingleLineCommentInsideForTag_IsProcessedCorrectly()
705+
{
706+
var template = @"
707+
{% lava
708+
//- Comment level one
709+
assign test = -1
710+
assign loopCount = 5
711+
for i in (0..loopCount)
712+
713+
//- Comment level two
714+
assign test = i
715+
716+
endfor
717+
echo test
718+
%}
719+
";
720+
var expectedOutput = @"5";
721+
722+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
723+
{
724+
TestHelper.AssertTemplateOutput( engine, expectedOutput, template, ignoreWhitespace: true );
725+
} );
726+
}
727+
728+
/// <summary>
729+
/// Verify that Rock's custom "//-" block comments works inside a FOR tag that is inside a {% lava %} {% liquid %} tag.
730+
/// </summary>
731+
[TestMethod]
732+
public void LavaTag_WithCommentBlockInsideForTag_IsProcessedCorrectly()
733+
{
734+
var template = @"
735+
{% lava
736+
//- Comment level one
737+
assign test = -1
738+
assign loopCount = 5
739+
for i in (0..loopCount)
740+
741+
/-
742+
assign test = i
743+
-/
744+
745+
endfor
746+
echo test
747+
%}
748+
";
749+
var expectedOutput = @"-1";
750+
751+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
752+
{
753+
TestHelper.AssertTemplateOutput( engine, expectedOutput, template, ignoreWhitespace: true );
754+
} );
755+
}
756+
757+
/// <summary>
758+
/// Verify that Liquid's "#" comments works inside a {% lava %} {% liquid %} tag.
759+
/// NOTE: This is not supported in Rock (or Fluid as far as I can tell).
760+
/// </summary>
761+
[TestMethod]
762+
[Ignore] // Ignored because this is not currently supported in Fluid.
763+
public void LavaTag_WithSingleLineHashComment_IsProcessedCorrectly()
764+
{
765+
var template = @"
766+
{% liquid
767+
768+
# This is a comment
769+
assign test = 5
770+
771+
echo test
772+
%}
773+
";
774+
var expectedOutput = @"5";
775+
776+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
777+
{
778+
TestHelper.AssertTemplateOutput( engine, expectedOutput, template, ignoreWhitespace: true );
779+
} );
630780
}
631781
#endregion
632782

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// <copyright>
2+
// Copyright by the Spark Development Network
3+
//
4+
// Licensed under the Rock Community License (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.rockrms.com/license
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
//
17+
using Microsoft.VisualStudio.TestTools.UnitTesting;
18+
19+
using Rock.Lava;
20+
using Rock.Tests.Shared;
21+
22+
namespace Rock.Tests.Lava.Tags
23+
{
24+
/// <summary>
25+
/// Verifies Lava shortcode behavior.
26+
/// </summary>
27+
[TestClass]
28+
[TestCategory( TestFeatures.Lava )]
29+
public class LavaShortcodeTagTests : LavaUnitTestBase
30+
{
31+
/// <summary>
32+
/// A dynamic shortcode should produce the expected output.
33+
/// </summary>
34+
[TestMethod]
35+
public void CustomDynamicLavaShortCode_ProducesExpectedOutput()
36+
{
37+
var shortcodeTemplate = @"
38+
{% assign x = 42 %}
39+
The answer is {{ x }}.
40+
";
41+
// Create a new test shortcode with the "execute" command permission.
42+
var shortcodeDefinition = new DynamicShortcodeDefinition();
43+
44+
shortcodeDefinition.ElementType = LavaShortcodeTypeSpecifier.Inline;
45+
shortcodeDefinition.TemplateMarkup = shortcodeTemplate;
46+
shortcodeDefinition.Name = "shortcode_execute";
47+
48+
var input = @"
49+
{[ shortcode_execute ]}
50+
";
51+
var expectedOutput = "The answer is 42.";
52+
53+
TestHelper.ExecuteForActiveEngines( ( engine ) =>
54+
{
55+
engine.RegisterShortcode( shortcodeDefinition.Name, ( shortcodeName ) => { return shortcodeDefinition; } );
56+
57+
TestHelper.AssertTemplateOutput( engine, expectedOutput, input, ignoreWhitespace: true );
58+
} );
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)