From c2d2bbff55bf552ddb06d7cd02996180bda6a163 Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Wed, 18 Mar 2026 14:11:47 +0100 Subject: [PATCH] Engine: Delegate block and context-aware expressions to overridable engine methods --- lib/herb/engine.rb | 21 +++++++++++++ lib/herb/engine/compiler.rb | 58 +++++++++++------------------------- sig/herb/engine.rbs | 4 +++ sig/herb/engine/compiler.rbs | 6 ++-- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/lib/herb/engine.rb b/lib/herb/engine.rb index 1ac261a33..c5aa51e65 100644 --- a/lib/herb/engine.rb +++ b/lib/herb/engine.rb @@ -55,6 +55,9 @@ def initialize(input, properties = {}) @bufvar = properties[:bufvar] || properties[:outvar] || "_buf" @escape = properties.fetch(:escape) { properties.fetch(:escape_html, false) } @escapefunc = properties.fetch(:escapefunc, @escape ? "__herb.h" : "::Herb::Engine.h") + @attrfunc = properties.fetch(:attrfunc, @escape ? "__herb.attr" : "::Herb::Engine.attr") + @jsfunc = properties.fetch(:jsfunc, @escape ? "__herb.js" : "::Herb::Engine.js") + @cssfunc = properties.fetch(:cssfunc, @escape ? "__herb.css" : "::Herb::Engine.css") @src = properties[:src] || String.new @chain_appends = properties[:chain_appends] @buffer_on_stack = false @@ -244,6 +247,24 @@ def add_expression(indicator, code) end end + def add_context_aware_expression(indicator, code, context) + escapefunc = context_escape_function(context) + + if escapefunc.nil? + add_expression(indicator, code) + else + with_buffer { @src << " << #{escapefunc}((" << code << trailing_newline(code) << "))" } + end + end + + def context_escape_function(context) + case context + when :attribute_value then @attrfunc + when :script_content then @jsfunc + when :style_content then @cssfunc + end + end + def add_expression_result(code) with_buffer { @src << " << (" << code << trailing_newline(code) << ").to_s" diff --git a/lib/herb/engine/compiler.rb b/lib/herb/engine/compiler.rb index fc97bb579..471c12b9f 100644 --- a/lib/herb/engine/compiler.rb +++ b/lib/herb/engine/compiler.rb @@ -10,9 +10,6 @@ def initialize(engine, options = {}) @engine = engine @escape = options.fetch(:escape) { options.fetch(:escape_html, false) } - @attrfunc = options.fetch(:attrfunc, @escape ? "__herb.attr" : "::Herb::Engine.attr") - @jsfunc = options.fetch(:jsfunc, @escape ? "__herb.js" : "::Herb::Engine.js") - @cssfunc = options.fetch(:cssfunc, @escape ? "__herb.css" : "::Herb::Engine.css") @tokens = [] #: Array[untyped] @element_stack = [] #: Array[String] @context_stack = [:html_content] @@ -28,26 +25,16 @@ def generate_output @engine.send(:add_text, value) when :code @engine.send(:add_code, value) - when :expr - if [:attribute_value, :script_content, :style_content].include?(context) - add_context_aware_expression(value, context) - else - indicator = @escape ? "==" : "=" - @engine.send(:add_expression, indicator, value) - end - when :expr_escaped - if [:attribute_value, :script_content, :style_content].include?(context) - add_context_aware_expression(value, context) + when :expr, :expr_escaped + indicator = indicator_for(type) + + if context_aware_context?(context) + @engine.send(:add_context_aware_expression, indicator, value, context) else - indicator = @escape ? "=" : "==" @engine.send(:add_expression, indicator, value) end - when :expr_block - indicator = @escape ? "==" : "=" - @engine.send(:add_expression_block, indicator, value) - when :expr_block_escaped - indicator = @escape ? "=" : "==" - @engine.send(:add_expression_block, indicator, value) + when :expr_block, :expr_block_escaped + @engine.send(:add_expression_block, indicator_for(type), value) when :expr_block_end @engine.send(:add_expression_block_end, value, escaped: escaped) end @@ -342,27 +329,6 @@ def pop_context @context_stack.pop end - def add_context_aware_expression(code, context) - closing = code.include?("#") ? "\n))" : "))" - - case context - when :attribute_value - @engine.send(:with_buffer) { - @engine.src << " << #{@attrfunc}((" << code << closing - } - when :script_content - @engine.send(:with_buffer) { - @engine.src << " << #{@jsfunc}((" << code << closing - } - when :style_content - @engine.send(:with_buffer) { - @engine.src << " << #{@cssfunc}((" << code << closing - } - else - @engine.send(:add_expression_result_escaped, code) - end - end - def process_erb_tag(node, skip_comment_check: false) opening = node.tag_opening.value @@ -503,6 +469,16 @@ def process_erb_output(node, opening, code) @trim_next_whitespace = true if has_right_trim end + def indicator_for(type) + escaped = [:expr_escaped, :expr_block_escaped].include?(type) + + escaped ^ @escape ? "==" : "=" + end + + def context_aware_context?(context) + [:attribute_value, :script_content, :style_content].include?(context) + end + def should_escape_output?(opening) is_double_equals = opening == "<%==" is_double_equals ? !@escape : @escape diff --git a/sig/herb/engine.rbs b/sig/herb/engine.rbs index 0896307e9..14d0d3582 100644 --- a/sig/herb/engine.rbs +++ b/sig/herb/engine.rbs @@ -55,6 +55,10 @@ module Herb def add_expression: (untyped indicator, untyped code) -> untyped + def add_context_aware_expression: (untyped indicator, untyped code, untyped context) -> untyped + + def context_escape_function: (untyped context) -> untyped + def add_expression_result: (untyped code) -> untyped def add_expression_result_escaped: (untyped code) -> untyped diff --git a/sig/herb/engine/compiler.rbs b/sig/herb/engine/compiler.rbs index e56c9041e..013c9a5f0 100644 --- a/sig/herb/engine/compiler.rbs +++ b/sig/herb/engine/compiler.rbs @@ -89,8 +89,6 @@ module Herb def pop_context: () -> untyped - def add_context_aware_expression: (untyped code, untyped context) -> untyped - def process_erb_tag: (untyped node, ?skip_comment_check: untyped) -> untyped def add_text: (untyped text) -> untyped @@ -119,6 +117,10 @@ module Herb def process_erb_output: (untyped node, untyped opening, untyped code) -> untyped + def indicator_for: (untyped type) -> untyped + + def context_aware_context?: (untyped context) -> untyped + def should_escape_output?: (untyped opening) -> untyped def add_expression_with_escaping: (untyped code, untyped should_escape) -> untyped