From 0e14a0d7adb6a1f2a093ee9d88a942d7b97922ff Mon Sep 17 00:00:00 2001 From: Hugo Parente Lima Date: Fri, 8 May 2026 17:36:39 -0300 Subject: [PATCH] Fix increment/decrement tags to use a separate counter namespace increment/decrement now maintain their own counter namespace independent of assign variables, output the counter value to the template (increment post-increments, decrement pre-decrements), and fall back to the counter namespace when a variable is not found in the assign namespace. --- spec/integration/golden_liquid.pending | 7 ------- src/liquid/codegen_visitor.cr | 8 ++++++++ src/liquid/context.cr | 20 ++++++++++++++++++++ src/liquid/render_visitor.cr | 14 ++------------ 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/spec/integration/golden_liquid.pending b/spec/integration/golden_liquid.pending index 53aee0e..08f7a45 100644 --- a/spec/integration/golden_liquid.pending +++ b/spec/integration/golden_liquid.pending @@ -35,8 +35,6 @@ liquid.golden.date_filter timestamp integer liquid.golden.date_filter timestamp string liquid.golden.date_filter undefined argument liquid.golden.date_filter well formed string -liquid.golden.decrement_tag increment and decrement named counter -liquid.golden.decrement_tag named counter liquid.golden.default_filter missing argument liquid.golden.echo_tag access an array item by index liquid.golden.echo_tag access an array item by negative index @@ -134,11 +132,6 @@ liquid.golden.include_tag some keyword arguments without leading comma liquid.golden.include_tag some keyword arguments with range literal liquid.golden.include_tag string literal name liquid.golden.include_tag use globals from outer scope -liquid.golden.increment_tag assign and increment -liquid.golden.increment_tag incrementing counter renders before incrementing -liquid.golden.increment_tag multiple named counters -liquid.golden.increment_tag named counter -liquid.golden.increment_tag named counters are in scope for subsequent expressions liquid.golden.inline_comment_tag can't comment tags liquid.golden.inline_comment_tag empty liquid.golden.inline_comment_tag liquid tag diff --git a/src/liquid/codegen_visitor.cr b/src/liquid/codegen_visitor.cr index 3cc7945..01374b8 100644 --- a/src/liquid/codegen_visitor.cr +++ b/src/liquid/codegen_visitor.cr @@ -154,5 +154,13 @@ module Liquid node.children.each &.accept self pop end + + def visit(node : Increment) + to_io %(Liquid::Block::Increment.new("#{escape node.var_name}")) + end + + def visit(node : Decrement) + to_io %(Liquid::Block::Decrement.new("#{escape node.var_name}")) + end end end diff --git a/src/liquid/context.cr b/src/liquid/context.cr index 543e7a9..db09080 100644 --- a/src/liquid/context.cr +++ b/src/liquid/context.cr @@ -19,6 +19,7 @@ module Liquid end @data : Hash(String, Any) + @counters = Hash(String, Int32).new(0) # :nodoc: # These values are used/reused when calling filters in a expression using this context. @@ -80,12 +81,31 @@ module Liquid Any.new(nil) end + def increment_counter(name : String) : Int32 + val = @counters[name] + @counters[name] = val + 1 + val + end + + def decrement_counter(name : String) : Int32 + @counters[name] -= 1 + @counters[name] + end + + def counter(name : String) : Int32 + @counters[name] + end + # Fetch a variable from context, add `UndefinedVariable` error if the variable isn't found and behave according the # error mode. def get(var : String) : Any value = @data[var]? return value if value + if @counters.has_key?(var) + return Any.new(@counters[var]) + end + add_error(UndefinedVariable.new(var)) end diff --git a/src/liquid/render_visitor.cr b/src/liquid/render_visitor.cr index 4d33128..454352d 100644 --- a/src/liquid/render_visitor.cr +++ b/src/liquid/render_visitor.cr @@ -91,21 +91,11 @@ module Liquid end def visit(node : Increment) - var = @data.get node.var_name - if var && (num = var.as_i?) - @data.set node.var_name, num + 1 - else - @data.set node.var_name, 1 - end + @io << @data.increment_counter(node.var_name) end def visit(node : Decrement) - var = @data.get node.var_name - if var && (num = var.as_i?) - @data.set node.var_name, num - 1 - else - @data.set node.var_name, -1 - end + @io << @data.decrement_counter(node.var_name) end def visit(node : ExpressionNode)