haml 5.2.2 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/test.yml +15 -15
  4. data/.gitignore +16 -16
  5. data/.yardopts +0 -3
  6. data/CHANGELOG.md +168 -4
  7. data/FAQ.md +1 -1
  8. data/Gemfile +21 -10
  9. data/MIT-LICENSE +1 -1
  10. data/README.md +22 -34
  11. data/REFERENCE.md +95 -159
  12. data/Rakefile +15 -82
  13. data/bin/bench +66 -0
  14. data/bin/console +11 -0
  15. data/bin/ruby +3 -0
  16. data/bin/setup +7 -0
  17. data/bin/stackprof +27 -0
  18. data/bin/test +24 -0
  19. data/exe/haml +6 -0
  20. data/haml.gemspec +34 -36
  21. data/lib/haml/ambles.rb +20 -0
  22. data/lib/haml/attribute_builder.rb +127 -184
  23. data/lib/haml/attribute_compiler.rb +90 -194
  24. data/lib/haml/attribute_parser.rb +92 -126
  25. data/lib/haml/cli.rb +154 -0
  26. data/lib/haml/compiler/children_compiler.rb +155 -0
  27. data/lib/haml/compiler/comment_compiler.rb +51 -0
  28. data/lib/haml/compiler/doctype_compiler.rb +52 -0
  29. data/lib/haml/compiler/script_compiler.rb +114 -0
  30. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  31. data/lib/haml/compiler/tag_compiler.rb +76 -0
  32. data/lib/haml/compiler.rb +63 -296
  33. data/lib/haml/dynamic_merger.rb +67 -0
  34. data/lib/haml/engine.rb +48 -227
  35. data/lib/haml/error.rb +5 -4
  36. data/lib/haml/escape.rb +13 -0
  37. data/lib/haml/escape_any.rb +21 -0
  38. data/lib/haml/filters/base.rb +12 -0
  39. data/lib/haml/filters/cdata.rb +20 -0
  40. data/lib/haml/filters/coffee.rb +17 -0
  41. data/lib/haml/filters/css.rb +33 -0
  42. data/lib/haml/filters/erb.rb +10 -0
  43. data/lib/haml/filters/escaped.rb +22 -0
  44. data/lib/haml/filters/javascript.rb +33 -0
  45. data/lib/haml/filters/less.rb +20 -0
  46. data/lib/haml/filters/markdown.rb +11 -0
  47. data/lib/haml/filters/plain.rb +29 -0
  48. data/lib/haml/filters/preserve.rb +22 -0
  49. data/lib/haml/filters/ruby.rb +10 -0
  50. data/lib/haml/filters/sass.rb +15 -0
  51. data/lib/haml/filters/scss.rb +15 -0
  52. data/lib/haml/filters/text_base.rb +25 -0
  53. data/lib/haml/filters/tilt_base.rb +59 -0
  54. data/lib/haml/filters.rb +54 -378
  55. data/lib/haml/force_escape.rb +29 -0
  56. data/lib/haml/helpers.rb +3 -697
  57. data/lib/haml/html.rb +22 -0
  58. data/lib/haml/identity.rb +13 -0
  59. data/lib/haml/object_ref.rb +35 -0
  60. data/lib/haml/parser.rb +158 -23
  61. data/lib/haml/rails_helpers.rb +53 -0
  62. data/lib/haml/rails_template.rb +62 -0
  63. data/lib/haml/railtie.rb +3 -46
  64. data/lib/haml/ruby_expression.rb +32 -0
  65. data/lib/haml/string_splitter.rb +140 -0
  66. data/lib/haml/template.rb +15 -34
  67. data/lib/haml/temple_line_counter.rb +2 -1
  68. data/lib/haml/util.rb +19 -15
  69. data/lib/haml/version.rb +1 -2
  70. data/lib/haml/whitespace.rb +8 -0
  71. data/lib/haml.rb +8 -20
  72. metadata +188 -50
  73. data/.gitmodules +0 -3
  74. data/TODO +0 -24
  75. data/benchmark.rb +0 -70
  76. data/bin/haml +0 -9
  77. data/lib/haml/.gitattributes +0 -1
  78. data/lib/haml/buffer.rb +0 -182
  79. data/lib/haml/escapable.rb +0 -77
  80. data/lib/haml/exec.rb +0 -347
  81. data/lib/haml/generator.rb +0 -42
  82. data/lib/haml/helpers/action_view_extensions.rb +0 -60
  83. data/lib/haml/helpers/action_view_mods.rb +0 -132
  84. data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
  85. data/lib/haml/helpers/safe_erubi_template.rb +0 -20
  86. data/lib/haml/helpers/safe_erubis_template.rb +0 -33
  87. data/lib/haml/helpers/xss_mods.rb +0 -114
  88. data/lib/haml/options.rb +0 -273
  89. data/lib/haml/plugin.rb +0 -54
  90. data/lib/haml/sass_rails_filter.rb +0 -47
  91. data/lib/haml/template/options.rb +0 -27
  92. data/lib/haml/temple_engine.rb +0 -124
  93. data/yard/default/.gitignore +0 -1
  94. data/yard/default/fulldoc/html/css/common.sass +0 -15
  95. data/yard/default/layout/html/footer.erb +0 -12
data/lib/haml/cli.rb ADDED
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+ require 'haml'
3
+ require 'thor'
4
+
5
+ module Haml
6
+ class CLI < Thor
7
+ class_option :escape_html, type: :boolean, default: true
8
+ class_option :escape_attrs, type: :boolean, default: true
9
+
10
+ desc 'render HAML', 'Render haml template'
11
+ option :load_path, type: :string, aliases: %w[-I]
12
+ option :require, type: :string, aliases: %w[-r]
13
+ def render(file)
14
+ process_load_options
15
+ code = generate_code(file)
16
+ puts eval(code, binding, file)
17
+ end
18
+
19
+ desc 'compile HAML', 'Show compile result'
20
+ option :actionview, type: :boolean, default: false, aliases: %w[-a]
21
+ option :color, type: :boolean, default: true
22
+ option :check, type: :boolean, default: false, aliases: %w[-c]
23
+ def compile(file)
24
+ code = generate_code(file)
25
+ if options[:check]
26
+ if error = validate_ruby(code, file)
27
+ abort error.message.split("\n").first
28
+ end
29
+ puts "Syntax OK"
30
+ return
31
+ end
32
+ puts_code(code, color: options[:color])
33
+ end
34
+
35
+ desc 'temple HAML', 'Show temple intermediate expression'
36
+ option :color, type: :boolean, default: true
37
+ def temple(file)
38
+ pp_object(generate_temple(file), color: options[:color])
39
+ end
40
+
41
+ desc 'parse HAML', 'Show parse result'
42
+ option :color, type: :boolean, default: true
43
+ def parse(file)
44
+ pp_object(generate_ast(file), color: options[:color])
45
+ end
46
+
47
+ desc 'version', 'Show the used haml version'
48
+ def version
49
+ puts Haml::VERSION
50
+ end
51
+
52
+ private
53
+
54
+ def process_load_options
55
+ if options[:load_path]
56
+ options[:load_path].split(':').each do |dir|
57
+ $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir)
58
+ end
59
+ end
60
+
61
+ if options[:require]
62
+ require options[:require]
63
+ end
64
+ end
65
+
66
+ def read_file(file)
67
+ if file == '-'
68
+ STDIN.read
69
+ else
70
+ File.read(file)
71
+ end
72
+ end
73
+
74
+ def generate_code(file)
75
+ template = read_file(file)
76
+ if options[:actionview]
77
+ require 'action_view'
78
+ require 'action_view/base'
79
+ require 'haml/rails_template'
80
+ handler = Haml::RailsTemplate.new
81
+ template = ActionView::Template.new(template, 'inline template', handler, { locals: [] })
82
+ code = handler.call(template)
83
+ <<-end_src
84
+ def _inline_template___2144273726781623612_70327218547300(local_assigns, output_buffer)
85
+ _old_virtual_path, @virtual_path = @virtual_path, nil;_old_output_buffer = @output_buffer;;#{code}
86
+ ensure
87
+ @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
88
+ end
89
+ end_src
90
+ else
91
+ Haml::Engine.new(engine_options).call(template)
92
+ end
93
+ end
94
+
95
+ def generate_ast(file)
96
+ template = read_file(file)
97
+ Haml::Parser.new(engine_options).call(template)
98
+ end
99
+
100
+ def generate_temple(file)
101
+ ast = generate_ast(file)
102
+ Haml::Compiler.new(engine_options).call(ast)
103
+ end
104
+
105
+ def engine_options
106
+ Haml::Engine.options.to_h.merge(
107
+ escape_attrs: options[:escape_attrs],
108
+ escape_html: options[:escape_html],
109
+ )
110
+ end
111
+
112
+ # Flexible default_task, compatible with haml's CLI
113
+ def method_missing(*args)
114
+ return super(*args) if args.length > 1
115
+ render(args.first.to_s)
116
+ end
117
+
118
+ def puts_code(code, color: true)
119
+ begin
120
+ require 'irb/color'
121
+ rescue LoadError
122
+ color = false
123
+ end
124
+ if color
125
+ puts IRB::Color.colorize_code(code)
126
+ else
127
+ puts code
128
+ end
129
+ end
130
+
131
+ # Enable colored pretty printing only for development environment.
132
+ def pp_object(arg, color: true)
133
+ begin
134
+ require 'irb/color_printer'
135
+ rescue LoadError
136
+ color = false
137
+ end
138
+ if color
139
+ IRB::ColorPrinter.pp(arg)
140
+ else
141
+ require 'pp'
142
+ pp(arg)
143
+ end
144
+ end
145
+
146
+ def validate_ruby(code, file)
147
+ begin
148
+ eval("BEGIN {return nil}; #{code}", binding, file)
149
+ rescue ::SyntaxError # Not to be confused with Haml::SyntaxError
150
+ $!
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+ require 'haml/temple_line_counter'
3
+
4
+ module Haml
5
+ class Compiler
6
+ class ChildrenCompiler
7
+ def initialize
8
+ @lineno = 1
9
+ @multi_flattener = Temple::Filters::MultiFlattener.new
10
+ end
11
+
12
+ def compile(node, &block)
13
+ temple = [:multi]
14
+ return temple if node.children.empty?
15
+
16
+ temple << [:whitespace] if prepend_whitespace?(node)
17
+ node.children.each do |n|
18
+ rstrip_whitespace!(temple) if nuke_prev_whitespace?(n)
19
+ insert_newlines!(temple, n)
20
+ temple << moving_lineno(n) { block.call(n) }
21
+ temple << [:whitespace] if insert_whitespace?(n)
22
+ end
23
+ rstrip_whitespace!(temple) if nuke_inner_whitespace?(node)
24
+ temple
25
+ end
26
+
27
+ private
28
+
29
+ def insert_newlines!(temple, node)
30
+ (node.line - @lineno).times do
31
+ temple << [:newline]
32
+ end
33
+
34
+ @lineno = node.line
35
+ end
36
+
37
+ def moving_lineno(node, &block)
38
+ # before: As they may have children, we need to increment lineno before compilation.
39
+ case node.type
40
+ when :script, :silent_script
41
+ @lineno += 1
42
+ when :tag
43
+ [node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_hash|
44
+ @lineno += attribute_hash.count("\n")
45
+ end
46
+ @lineno += 1 if node.children.empty? && node.value[:parse]
47
+ end
48
+
49
+ temple = block.call # compile
50
+
51
+ # after: filter may not have children, and for some dynamic filters we can't predict the number of lines.
52
+ case node.type
53
+ when :filter
54
+ @lineno += TempleLineCounter.count_lines(temple)
55
+ end
56
+
57
+ temple
58
+ end
59
+
60
+ def prepend_whitespace?(node)
61
+ return false unless %i[comment tag].include?(node.type)
62
+ !nuke_inner_whitespace?(node)
63
+ end
64
+
65
+ def nuke_inner_whitespace?(node)
66
+ case
67
+ when node.type == :tag
68
+ node.value[:nuke_inner_whitespace]
69
+ when node.parent.nil?
70
+ false
71
+ else
72
+ nuke_inner_whitespace?(node.parent)
73
+ end
74
+ end
75
+
76
+ def nuke_prev_whitespace?(node)
77
+ case node.type
78
+ when :tag
79
+ node.value[:nuke_outer_whitespace]
80
+ when :silent_script
81
+ !node.children.empty? && nuke_prev_whitespace?(node.children.first)
82
+ else
83
+ false
84
+ end
85
+ end
86
+
87
+ def nuke_outer_whitespace?(node)
88
+ return false if node.type != :tag
89
+ node.value[:nuke_outer_whitespace]
90
+ end
91
+
92
+ def rstrip_whitespace!(temple)
93
+ return if temple.size == 1
94
+
95
+ case temple[0]
96
+ when :multi
97
+ case temple[-1][0]
98
+ when :whitespace
99
+ temple.delete_at(-1)
100
+ when :multi, :block
101
+ rstrip_whitespace!(temple[-1])
102
+ end
103
+ when :block
104
+ _block, code, exp = temple
105
+ if code.start_with?(/\s*if\s/)
106
+ # Remove [:whitespace] before `end`
107
+ exp.replace(@multi_flattener.call(exp))
108
+ rstrip_whitespace!(exp)
109
+
110
+ # Remove [:whitespace] before `else` if exists
111
+ else_index = find_else_index(exp)
112
+ if else_index
113
+ whitespace_index = else_index - 1
114
+ while exp[whitespace_index] == [:newline]
115
+ whitespace_index -= 1
116
+ end
117
+ if exp[whitespace_index] == [:whitespace]
118
+ exp.delete_at(whitespace_index)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def find_else_index(temple)
126
+ multi, *args = temple
127
+ return nil if multi != :multi
128
+
129
+ args.each_with_index do |arg, index|
130
+ if arg[0] == :code && arg[1].match?(/\A\s*else\s*\z/)
131
+ return index + 1
132
+ end
133
+ end
134
+ nil
135
+ end
136
+
137
+ def insert_whitespace?(node)
138
+ return false if nuke_outer_whitespace?(node)
139
+
140
+ case node.type
141
+ when :doctype
142
+ node.value[:type] != 'xml'
143
+ when :comment, :plain, :tag
144
+ true
145
+ when :script
146
+ node.children.empty? && !nuke_inner_whitespace?(node)
147
+ when :filter
148
+ !%w[ruby].include?(node.value[:name])
149
+ else
150
+ false
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Compiler
4
+ class CommentCompiler
5
+ def compile(node, &block)
6
+ if node.value[:conditional]
7
+ compile_conditional_comment(node, &block)
8
+ else
9
+ compile_html_comment(node, &block)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def compile_html_comment(node, &block)
16
+ if node.children.empty?
17
+ [:html, :comment, compile_text(node)]
18
+ else
19
+ [:html, :comment, yield(node)]
20
+ end
21
+ end
22
+
23
+ def compile_conditional_comment(node, &block)
24
+ condition = node.value[:conditional]
25
+ if node.value[:conditional] =~ /\A\[(\[*[^\[\]]+\]*)\]/
26
+ condition = $1
27
+ end
28
+
29
+ content =
30
+ if node.children.empty?
31
+ compile_text(node)
32
+ else
33
+ yield(node)
34
+ end
35
+ [:html, :condcomment, condition, content, node.value[:revealed]]
36
+ end
37
+
38
+ def compile_text(node)
39
+ text =
40
+ if node.value[:parse]
41
+ # Just always escaping the result for safety. We could respect
42
+ # escape_html, but I don't see any use case for it.
43
+ [:escape, true, [:dynamic, node.value[:text]]]
44
+ else
45
+ [:static, node.value[:text]]
46
+ end
47
+ [:multi, [:static, ' '], text, [:static, ' ']]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Compiler
4
+ class DoctypeCompiler
5
+ def initialize(options = {})
6
+ @format = options[:format]
7
+ end
8
+
9
+ def compile(node)
10
+ case node.value[:type]
11
+ when ''
12
+ html_doctype(node)
13
+ when 'xml'
14
+ xml_doctype
15
+ when 'rdfa'
16
+ rdfa_doctype
17
+ else
18
+ [:html, :doctype, node.value[:type]]
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def html_doctype(node)
25
+ version = node.value[:version] || :transitional
26
+ case @format
27
+ when :xhtml
28
+ [:html, :doctype, version]
29
+ when :html4
30
+ [:html, :doctype, :transitional]
31
+ when :html5
32
+ [:html, :doctype, :html]
33
+ else
34
+ [:html, :doctype, @format]
35
+ end
36
+ end
37
+
38
+ def xml_doctype
39
+ case @format
40
+ when :xhtml
41
+ [:static, "<?xml version='1.0' encoding='utf-8' ?>\n"]
42
+ else
43
+ [:multi]
44
+ end
45
+ end
46
+
47
+ def rdfa_doctype
48
+ [:static, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">']
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+ require 'temple/static_analyzer'
3
+ require 'haml/ruby_expression'
4
+ require 'haml/string_splitter'
5
+
6
+ module Haml
7
+ class Compiler
8
+ class ScriptCompiler
9
+ def self.find_and_preserve(input, tags)
10
+ tags = tags.map { |tag| Regexp.escape(tag) }.join('|')
11
+ re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
12
+ input.to_s.gsub(re) do |s|
13
+ s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
14
+ "<#{$1}#{$2}>#{Haml::Helpers.preserve($3)}</#{$1}>"
15
+ end
16
+ end
17
+
18
+ def initialize(identity, options)
19
+ @identity = identity
20
+ @disable_capture = options[:disable_capture]
21
+ end
22
+
23
+ def compile(node, &block)
24
+ unless Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
25
+ return dynamic_compile(node, &block)
26
+ end
27
+
28
+ no_children = node.children.empty?
29
+ case
30
+ when no_children && node.value[:escape_interpolation]
31
+ compile_interpolated_plain(node)
32
+ when no_children && RubyExpression.string_literal?(node.value[:text])
33
+ delegate_optimization(node)
34
+ when no_children && Temple::StaticAnalyzer.static?(node.value[:text])
35
+ static_compile(node)
36
+ else
37
+ dynamic_compile(node, &block)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # String-interpolated plain text must be compiled with this method
44
+ # because we have to escape only interpolated values.
45
+ def compile_interpolated_plain(node)
46
+ temple = [:multi]
47
+ StringSplitter.compile(node.value[:text]).each do |type, value|
48
+ case type
49
+ when :static
50
+ temple << [:static, value]
51
+ when :dynamic
52
+ temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
53
+ end
54
+ end
55
+ temple << [:newline]
56
+ end
57
+
58
+ # :dynamic is optimized in other filter: StringSplitter
59
+ def delegate_optimization(node)
60
+ [:multi,
61
+ [:escape, node.value[:escape_html], [:dynamic, node.value[:text]]],
62
+ [:newline],
63
+ ]
64
+ end
65
+
66
+ def static_compile(node)
67
+ str = eval(node.value[:text]).to_s
68
+ if node.value[:escape_html]
69
+ str = Haml::Util.escape_html(str)
70
+ elsif node.value[:preserve]
71
+ str = ScriptCompiler.find_and_preserve(str, %w(textarea pre code))
72
+ end
73
+ [:multi, [:static, str], [:newline]]
74
+ end
75
+
76
+ def dynamic_compile(node, &block)
77
+ var = @identity.generate
78
+ temple = compile_script_assign(var, node, &block)
79
+ temple << compile_script_result(var, node)
80
+ end
81
+
82
+ def compile_script_assign(var, node, &block)
83
+ if node.children.empty?
84
+ [:multi,
85
+ [:code, "#{var} = (#{node.value[:text]}"],
86
+ [:newline],
87
+ [:code, ')'],
88
+ ]
89
+ else
90
+ [:multi,
91
+ [:block, "#{var} = #{node.value[:text]}",
92
+ [:multi, [:newline], @disable_capture ? yield(node) : [:capture, Temple::Utils.unique_name, yield(node)]]
93
+ ],
94
+ ]
95
+ end
96
+ end
97
+
98
+ def compile_script_result(result, node)
99
+ if !node.value[:escape_html] && node.value[:preserve]
100
+ result = find_and_preserve(result)
101
+ end
102
+ [:escapeany, node.value[:escape_html], [:dynamic, result]]
103
+ end
104
+
105
+ def find_and_preserve(code)
106
+ %Q[::Haml::Compiler::ScriptCompiler.find_and_preserve(#{code}, %w(textarea pre code))]
107
+ end
108
+
109
+ def escape_html(temple)
110
+ [:escape, true, temple]
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Compiler
4
+ class SilentScriptCompiler
5
+ def compile(node, &block)
6
+ if node.children.empty?
7
+ [:multi, [:code, node.value[:text]], [:newline]]
8
+ else
9
+ compile_with_children(node, &block)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def compile_with_children(node, &block)
16
+ [:multi,
17
+ [:block, node.value[:text],
18
+ [:multi, [:newline], yield(node)],
19
+ ],
20
+ ]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+ require 'haml/util'
3
+ require 'haml/attribute_compiler'
4
+ require 'haml/string_splitter'
5
+
6
+ module Haml
7
+ class Compiler
8
+ class TagCompiler
9
+ def initialize(identity, options)
10
+ @autoclose = options[:autoclose]
11
+ @identity = identity
12
+ @attribute_compiler = AttributeCompiler.new(identity, options)
13
+ end
14
+
15
+ def compile(node, &block)
16
+ attrs = @attribute_compiler.compile(node)
17
+ contents = compile_contents(node, &block)
18
+ [:html, :tag, node.value[:name], attrs, contents]
19
+ end
20
+
21
+ private
22
+
23
+ def compile_contents(node, &block)
24
+ case
25
+ when !node.children.empty?
26
+ yield(node)
27
+ when node.value[:value].nil? && self_closing?(node)
28
+ nil
29
+ when node.value[:parse]
30
+ return compile_interpolated_plain(node) if node.value[:escape_interpolation]
31
+ if Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
32
+ return delegate_optimization(node) if RubyExpression.string_literal?(node.value[:value])
33
+ return delegate_optimization(node) if Temple::StaticAnalyzer.static?(node.value[:value])
34
+ end
35
+
36
+ var = @identity.generate
37
+ [:multi,
38
+ [:code, "#{var} = (#{node.value[:value]}"],
39
+ [:newline],
40
+ [:code, ')'],
41
+ [:escape, node.value[:escape_html], [:dynamic, var]]
42
+ ]
43
+ else
44
+ [:static, node.value[:value]]
45
+ end
46
+ end
47
+
48
+ # :dynamic is optimized in other filters: StringSplitter or StaticAnalyzer
49
+ def delegate_optimization(node)
50
+ [:multi,
51
+ [:escape, node.value[:escape_html], [:dynamic, node.value[:value]]],
52
+ [:newline],
53
+ ]
54
+ end
55
+
56
+ # We should handle interpolation here to escape only interpolated values.
57
+ def compile_interpolated_plain(node)
58
+ temple = [:multi]
59
+ StringSplitter.compile(node.value[:value]).each do |type, value|
60
+ case type
61
+ when :static
62
+ temple << [:static, value]
63
+ when :dynamic
64
+ temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
65
+ end
66
+ end
67
+ temple << [:newline]
68
+ end
69
+
70
+ def self_closing?(node)
71
+ return true if @autoclose && @autoclose.include?(node.value[:name])
72
+ node.value[:self_closing]
73
+ end
74
+ end
75
+ end
76
+ end