diff --git a/Fluid.Tests/ForStatementTests.cs b/Fluid.Tests/ForStatementTests.cs index 0d578338..4566ba77 100644 --- a/Fluid.Tests/ForStatementTests.cs +++ b/Fluid.Tests/ForStatementTests.cs @@ -345,6 +345,69 @@ public async Task ForEvaluatesMemberOptionsOffsetOnly() Assert.Equal("54", sw.ToString()); } + + [Fact] + public async Task NegativeTargetShouldNotRenderLoop() + { + var e = new ForStatement( + new List { new TextSpanStatement("x") }, + "i", + new RangeExpression( + new LiteralExpression(NumberValue.Create(0)), + new LiteralExpression(NumberValue.Create(-1)) + ), + null, null, false + ); + + var sw = new StringWriter(); + await e.WriteToAsync(sw, HtmlEncoder.Default, new TemplateContext()); + + Assert.Equal("", sw.ToString()); + } + + [Fact] + public async Task InvalidRangeShouldNotRenderLoop() + { + var e = new ForStatement( + new List { new TextSpanStatement("x") }, + "i", + new RangeExpression( + new LiteralExpression(NumberValue.Create(-10)), + new LiteralExpression(NumberValue.Create(-20)) + ), + null, null, false + ); + + var sw = new StringWriter(); + await e.WriteToAsync(sw, HtmlEncoder.Default, new TemplateContext()); + + Assert.Equal("", sw.ToString()); + } + + [Fact] + public async Task NegativeLimitShouldStripFromEnd() + { + var context = new TemplateContext() + .SetValue("limit", -3) + ; + + var e = new ForStatement( + new List { CreateMemberStatement("i") }, + "i", + new RangeExpression( + new LiteralExpression(NumberValue.Create(1)), + new LiteralExpression(NumberValue.Create(9)) + ), + new MemberExpression(new IdentifierSegment("limit")), + offset: null, + false + ); + + var sw = new StringWriter(); + await e.WriteToAsync(sw, HtmlEncoder.Default, context); + + Assert.Equal("123456", sw.ToString()); + } static Statement CreateMemberStatement(string p) { diff --git a/Fluid/Ast/ForStatement.cs b/Fluid/Ast/ForStatement.cs index a4134238..1cbbbe56 100644 --- a/Fluid/Ast/ForStatement.cs +++ b/Fluid/Ast/ForStatement.cs @@ -54,9 +54,6 @@ public ForStatement( public bool Reversed { get; } public Statement Else { get; } - private List _rangeElements; - private int _rangeStart, _rangeEnd; - public override async ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { List list = null; @@ -68,26 +65,14 @@ public override async ValueTask WriteToAsync(TextWriter writer, Text } else if (Range != null) { - int start = Convert.ToInt32((await Range.From.EvaluateAsync(context)).ToNumberValue()); - int end = Convert.ToInt32((await Range.To.EvaluateAsync(context)).ToNumberValue()); + var start = Convert.ToInt32((await Range.From.EvaluateAsync(context)).ToNumberValue()); + var end = Convert.ToInt32((await Range.To.EvaluateAsync(context)).ToNumberValue()); - // Cache range - if (_rangeElements == null || _rangeStart != start || _rangeEnd != end) - { - _rangeElements = new List(end - start); + list = new List(Math.Max(1, end - start)); - for (var i = start; i <= end; i++) - { - _rangeElements.Add(NumberValue.Create(i)); - } - - list = _rangeElements; - _rangeStart = start; - _rangeEnd = end; - } - else + for (var i = start; i <= end; i++) { - list = _rangeElements; + list.Add(NumberValue.Create(i)); } } @@ -113,7 +98,16 @@ public override async ValueTask WriteToAsync(TextWriter writer, Text if (Limit is not null) { var limit = (int) (await Limit.EvaluateAsync(context)).ToNumberValue(); - count = Math.Min(count, limit); + + // Limit can be negative + if (limit >= 0) + { + count = Math.Min(count, limit); + } + else + { + count = Math.Max(0, count + limit); + } } if (count == 0)