haml 5.0.4 → 6.0.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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +40 -0
- data/.gitignore +16 -15
- data/CHANGELOG.md +62 -1
- data/Gemfile +18 -14
- data/MIT-LICENSE +2 -2
- data/README.md +4 -5
- data/REFERENCE.md +46 -12
- data/Rakefile +93 -103
- data/bin/bench +66 -0
- data/bin/console +11 -0
- data/bin/ruby +3 -0
- data/bin/setup +7 -0
- data/bin/stackprof +27 -0
- data/bin/test +24 -0
- data/exe/haml +6 -0
- data/ext/haml/extconf.rb +10 -0
- data/ext/haml/haml.c +537 -0
- data/ext/haml/hescape.c +108 -0
- data/ext/haml/hescape.h +20 -0
- data/haml.gemspec +39 -30
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +140 -128
- data/lib/haml/attribute_compiler.rb +86 -181
- data/lib/haml/attribute_parser.rb +86 -124
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +126 -0
- data/lib/haml/compiler/comment_compiler.rb +39 -0
- data/lib/haml/compiler/doctype_compiler.rb +46 -0
- data/lib/haml/compiler/script_compiler.rb +116 -0
- data/lib/haml/compiler/silent_script_compiler.rb +24 -0
- data/lib/haml/compiler/tag_compiler.rb +76 -0
- data/lib/haml/compiler.rb +64 -298
- data/lib/haml/dynamic_merger.rb +67 -0
- data/lib/haml/engine.rb +43 -219
- data/lib/haml/error.rb +29 -27
- data/lib/haml/escapable.rb +6 -42
- data/lib/haml/filters/base.rb +12 -0
- data/lib/haml/filters/cdata.rb +20 -0
- data/lib/haml/filters/coffee.rb +17 -0
- data/lib/haml/filters/css.rb +33 -0
- data/lib/haml/filters/erb.rb +10 -0
- data/lib/haml/filters/escaped.rb +22 -0
- data/lib/haml/filters/javascript.rb +33 -0
- data/lib/haml/filters/less.rb +20 -0
- data/lib/haml/filters/markdown.rb +11 -0
- data/lib/haml/filters/plain.rb +29 -0
- data/lib/haml/filters/preserve.rb +22 -0
- data/lib/haml/filters/ruby.rb +10 -0
- data/lib/haml/filters/sass.rb +15 -0
- data/lib/haml/filters/scss.rb +15 -0
- data/lib/haml/filters/text_base.rb +25 -0
- data/lib/haml/filters/tilt_base.rb +49 -0
- data/lib/haml/filters.rb +55 -378
- data/lib/haml/force_escapable.rb +29 -0
- data/lib/haml/helpers.rb +4 -696
- data/lib/haml/html.rb +22 -0
- data/lib/haml/identity.rb +13 -0
- data/lib/haml/object_ref.rb +30 -0
- data/lib/haml/parser.rb +208 -43
- data/lib/haml/rails_helpers.rb +51 -0
- data/lib/haml/rails_template.rb +55 -0
- data/lib/haml/railtie.rb +7 -40
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +20 -0
- data/lib/haml/template.rb +15 -33
- data/lib/haml/temple_line_counter.rb +2 -0
- data/lib/haml/util.rb +23 -21
- data/lib/haml/version.rb +1 -1
- data/lib/haml.rb +8 -19
- metadata +222 -50
- data/.gitmodules +0 -3
- data/.travis.yml +0 -54
- data/.yardopts +0 -23
- data/TODO +0 -24
- data/benchmark.rb +0 -66
- data/bin/haml +0 -9
- data/lib/haml/.gitattributes +0 -1
- data/lib/haml/buffer.rb +0 -235
- data/lib/haml/exec.rb +0 -348
- data/lib/haml/generator.rb +0 -41
- data/lib/haml/helpers/action_view_extensions.rb +0 -59
- data/lib/haml/helpers/action_view_mods.rb +0 -129
- data/lib/haml/helpers/action_view_xss_mods.rb +0 -59
- data/lib/haml/helpers/safe_erubi_template.rb +0 -19
- data/lib/haml/helpers/safe_erubis_template.rb +0 -32
- data/lib/haml/helpers/xss_mods.rb +0 -110
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -34
- data/lib/haml/sass_rails_filter.rb +0 -46
- data/lib/haml/template/options.rb +0 -26
- data/lib/haml/temple_engine.rb +0 -121
- data/yard/default/.gitignore +0 -1
- data/yard/default/fulldoc/html/css/common.sass +0 -15
- data/yard/default/layout/html/footer.erb +0 -12
data/lib/haml/compiler.rb
CHANGED
|
@@ -1,330 +1,96 @@
|
|
|
1
|
-
# frozen_string_literal:
|
|
2
|
-
require 'haml/
|
|
3
|
-
require 'haml/
|
|
4
|
-
require 'haml/
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'haml/compiler/children_compiler'
|
|
3
|
+
require 'haml/compiler/comment_compiler'
|
|
4
|
+
require 'haml/compiler/doctype_compiler'
|
|
5
|
+
require 'haml/compiler/script_compiler'
|
|
6
|
+
require 'haml/compiler/silent_script_compiler'
|
|
7
|
+
require 'haml/compiler/tag_compiler'
|
|
8
|
+
require 'haml/filters'
|
|
9
|
+
require 'haml/identity'
|
|
5
10
|
|
|
6
11
|
module Haml
|
|
7
12
|
class Compiler
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@
|
|
14
|
-
@
|
|
15
|
-
@
|
|
16
|
-
@
|
|
17
|
-
@filters = Filters.defined.merge(options[:filters])
|
|
18
|
-
@attribute_compiler = AttributeCompiler.new(@options)
|
|
13
|
+
def initialize(options = {})
|
|
14
|
+
identity = Identity.new
|
|
15
|
+
@children_compiler = ChildrenCompiler.new
|
|
16
|
+
@comment_compiler = CommentCompiler.new
|
|
17
|
+
@doctype_compiler = DoctypeCompiler.new(options)
|
|
18
|
+
@filter_compiler = Filters.new(options)
|
|
19
|
+
@script_compiler = ScriptCompiler.new(identity, options)
|
|
20
|
+
@silent_script_compiler = SilentScriptCompiler.new
|
|
21
|
+
@tag_compiler = TagCompiler.new(identity, options)
|
|
19
22
|
end
|
|
20
23
|
|
|
21
|
-
def call(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def compile(node)
|
|
27
|
-
parent, @node = @node, node
|
|
28
|
-
if node.children.empty?
|
|
29
|
-
send(:"compile_#{node.type}")
|
|
30
|
-
else
|
|
31
|
-
send(:"compile_#{node.type}") {node.children.each {|c| compile c}}
|
|
32
|
-
end
|
|
33
|
-
ensure
|
|
34
|
-
@node = parent
|
|
24
|
+
def call(ast)
|
|
25
|
+
return runtime_error(ast) if ast.is_a?(Error)
|
|
26
|
+
compile(ast)
|
|
27
|
+
rescue Error => e
|
|
28
|
+
runtime_error(e)
|
|
35
29
|
end
|
|
36
30
|
|
|
37
31
|
private
|
|
38
32
|
|
|
39
|
-
def
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def compile_script(&block)
|
|
60
|
-
push_script(@node.value[:text],
|
|
61
|
-
:preserve_script => @node.value[:preserve],
|
|
62
|
-
:escape_html => @node.value[:escape_html],
|
|
63
|
-
:nuke_inner_whitespace => nuke_inner_whitespace?(@node),
|
|
64
|
-
&block)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def compile_silent_script
|
|
68
|
-
return if @options.suppress_eval
|
|
69
|
-
push_silent(@node.value[:text])
|
|
70
|
-
keyword = @node.value[:keyword]
|
|
71
|
-
|
|
72
|
-
if block_given?
|
|
73
|
-
yield
|
|
74
|
-
push_silent("end", :can_suppress) unless @node.value[:dont_push_end]
|
|
75
|
-
elsif keyword == "end"
|
|
76
|
-
if @node.parent.children.last.equal?(@node)
|
|
77
|
-
# Since this "end" is ending the block,
|
|
78
|
-
# we don't need to generate an additional one
|
|
79
|
-
@node.parent.value[:dont_push_end] = true
|
|
80
|
-
end
|
|
81
|
-
# Don't restore dont_* for end because it isn't a conditional branch.
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def compile_haml_comment; end
|
|
86
|
-
|
|
87
|
-
def compile_tag
|
|
88
|
-
t = @node.value
|
|
89
|
-
|
|
90
|
-
# Get rid of whitespace outside of the tag if we need to
|
|
91
|
-
rstrip_buffer! if t[:nuke_outer_whitespace]
|
|
92
|
-
|
|
93
|
-
if @options.suppress_eval
|
|
94
|
-
object_ref = :nil
|
|
95
|
-
parse = false
|
|
96
|
-
value = t[:parse] ? nil : t[:value]
|
|
97
|
-
dynamic_attributes = Haml::Parser::DynamicAttributes.new
|
|
98
|
-
else
|
|
99
|
-
object_ref = t[:object_ref]
|
|
100
|
-
parse = t[:parse]
|
|
101
|
-
value = t[:value]
|
|
102
|
-
dynamic_attributes = t[:dynamic_attributes]
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
if @options[:trace]
|
|
106
|
-
t[:attributes].merge!({"data-trace" => @options.filename.split('/views').last << ":" << @node.line.to_s})
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
push_text("<#{t[:name]}")
|
|
110
|
-
push_temple(@attribute_compiler.compile(t[:attributes], object_ref, dynamic_attributes))
|
|
111
|
-
push_text(
|
|
112
|
-
if t[:self_closing] && @options.xhtml?
|
|
113
|
-
" />#{"\n" unless t[:nuke_outer_whitespace]}"
|
|
114
|
-
else
|
|
115
|
-
">#{"\n" unless (t[:self_closing] && @options.html?) ? t[:nuke_outer_whitespace] : (!block_given? || t[:preserve_tag] || t[:nuke_inner_whitespace])}"
|
|
116
|
-
end
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
if value && !parse
|
|
120
|
-
push_text("#{value}</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
return if t[:self_closing]
|
|
124
|
-
|
|
125
|
-
if value.nil?
|
|
126
|
-
yield if block_given?
|
|
127
|
-
rstrip_buffer! if t[:nuke_inner_whitespace]
|
|
128
|
-
push_text("</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
|
|
129
|
-
return
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
if parse
|
|
133
|
-
push_script(value, t.merge(:in_tag => true))
|
|
134
|
-
push_text("</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def compile_comment
|
|
139
|
-
condition = "#{@node.value[:conditional]}>" if @node.value[:conditional]
|
|
140
|
-
revealed = @node.value[:revealed]
|
|
141
|
-
|
|
142
|
-
open = "<!--#{condition}#{'<!-->' if revealed}"
|
|
143
|
-
|
|
144
|
-
close = "#{'<!--' if revealed}#{'<![endif]' if condition}-->"
|
|
145
|
-
|
|
146
|
-
unless block_given?
|
|
147
|
-
push_text("#{open} ")
|
|
148
|
-
|
|
149
|
-
if @node.value[:parse]
|
|
150
|
-
push_script(@node.value[:text], :in_tag => true, :nuke_inner_whitespace => true)
|
|
151
|
-
else
|
|
152
|
-
push_text(@node.value[:text])
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
push_text(" #{close}\n")
|
|
156
|
-
return
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
push_text("#{open}\n")
|
|
160
|
-
yield if block_given?
|
|
161
|
-
push_text("#{close}\n")
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def compile_doctype
|
|
165
|
-
doctype = text_for_doctype
|
|
166
|
-
push_text("#{doctype}\n") if doctype
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def compile_filter
|
|
170
|
-
unless filter = @filters[@node.value[:name]]
|
|
171
|
-
name = @node.value[:name]
|
|
172
|
-
if ["maruku", "textile"].include?(name)
|
|
173
|
-
raise Error.new(Error.message(:install_haml_contrib, name), @node.line - 1)
|
|
174
|
-
else
|
|
175
|
-
raise Error.new(Error.message(:filter_not_defined, name), @node.line - 1)
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
filter.internal_compile(self, @node.value[:text])
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def text_for_doctype
|
|
182
|
-
if @node.value[:type] == "xml"
|
|
183
|
-
return nil if @options.html?
|
|
184
|
-
wrapper = @options.attr_wrapper
|
|
185
|
-
return "<?xml version=#{wrapper}1.0#{wrapper} encoding=#{wrapper}#{@node.value[:encoding] || "utf-8"}#{wrapper} ?>"
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
if @options.html5?
|
|
189
|
-
'<!DOCTYPE html>'
|
|
33
|
+
def compile(node)
|
|
34
|
+
case node.type
|
|
35
|
+
when :root
|
|
36
|
+
compile_children(node)
|
|
37
|
+
when :comment
|
|
38
|
+
compile_comment(node)
|
|
39
|
+
when :doctype
|
|
40
|
+
compile_doctype(node)
|
|
41
|
+
when :filter
|
|
42
|
+
compile_filter(node)
|
|
43
|
+
when :plain
|
|
44
|
+
compile_plain(node)
|
|
45
|
+
when :script
|
|
46
|
+
compile_script(node)
|
|
47
|
+
when :silent_script
|
|
48
|
+
compile_silent_script(node)
|
|
49
|
+
when :tag
|
|
50
|
+
compile_tag(node)
|
|
51
|
+
when :haml_comment
|
|
52
|
+
[:multi]
|
|
190
53
|
else
|
|
191
|
-
|
|
192
|
-
if @node.value[:version] == "1.1"
|
|
193
|
-
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
|
|
194
|
-
elsif @node.value[:version] == "5"
|
|
195
|
-
'<!DOCTYPE html>'
|
|
196
|
-
else
|
|
197
|
-
case @node.value[:type]
|
|
198
|
-
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
|
|
199
|
-
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
|
|
200
|
-
when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
|
|
201
|
-
when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
|
|
202
|
-
when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
|
|
203
|
-
else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
elsif @options.html4?
|
|
208
|
-
case @node.value[:type]
|
|
209
|
-
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
|
|
210
|
-
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
|
|
211
|
-
else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
|
212
|
-
end
|
|
213
|
-
end
|
|
54
|
+
raise InternalError.new("Unexpected node type: #{node.type}")
|
|
214
55
|
end
|
|
215
56
|
end
|
|
216
57
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def push_silent(text, can_suppress = false)
|
|
220
|
-
flush_merged_text
|
|
221
|
-
return if can_suppress && @options.suppress_eval?
|
|
222
|
-
newline = (text == "end") ? ";" : "\n"
|
|
223
|
-
@temple << [:code, "#{resolve_newlines}#{text}#{newline}"]
|
|
224
|
-
@output_line = @output_line + text.count("\n") + newline.count("\n")
|
|
58
|
+
def compile_children(node)
|
|
59
|
+
@children_compiler.compile(node) { |n| compile(n) }
|
|
225
60
|
end
|
|
226
61
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
@to_merge << [:text, text]
|
|
62
|
+
def compile_comment(node)
|
|
63
|
+
@comment_compiler.compile(node) { |n| compile_children(n) }
|
|
230
64
|
end
|
|
231
65
|
|
|
232
|
-
def
|
|
233
|
-
|
|
234
|
-
@temple.concat([[:newline]] * resolve_newlines.count("\n"))
|
|
235
|
-
@temple << temple
|
|
236
|
-
@output_line += TempleLineCounter.count_lines(temple)
|
|
66
|
+
def compile_doctype(node)
|
|
67
|
+
@doctype_compiler.compile(node)
|
|
237
68
|
end
|
|
238
69
|
|
|
239
|
-
def
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
@to_merge.each do |type, val|
|
|
243
|
-
case type
|
|
244
|
-
when :text
|
|
245
|
-
@temple << [:static, val]
|
|
246
|
-
when :script
|
|
247
|
-
@temple << [:dynamic, val]
|
|
248
|
-
else
|
|
249
|
-
raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Compiler@to_merge.")
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
@to_merge = []
|
|
70
|
+
def compile_filter(node)
|
|
71
|
+
@filter_compiler.compile(node)
|
|
254
72
|
end
|
|
255
73
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
#
|
|
259
|
-
# If `opts[:preserve_script]` is true, Haml::Helpers#find_and_preserve is run on
|
|
260
|
-
# the result before it is added to `@buffer`
|
|
261
|
-
def push_script(text, opts = {})
|
|
262
|
-
return if @options.suppress_eval?
|
|
263
|
-
|
|
264
|
-
no_format = !(opts[:preserve_script] || opts[:preserve_tag] || opts[:escape_html])
|
|
265
|
-
|
|
266
|
-
unless block_given?
|
|
267
|
-
push_generated_script(no_format ? "(#{text}\n).to_s" : build_script_formatter("(#{text}\n)", opts))
|
|
268
|
-
push_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace]
|
|
269
|
-
return
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
flush_merged_text
|
|
273
|
-
push_silent "haml_temp = #{text}"
|
|
274
|
-
yield
|
|
275
|
-
push_silent('end', :can_suppress) unless @node.value[:dont_push_end]
|
|
276
|
-
@temple << [:dynamic, no_format ? 'haml_temp.to_s;' : build_script_formatter('haml_temp', opts)]
|
|
74
|
+
def compile_plain(node)
|
|
75
|
+
[:static, node.value[:text]]
|
|
277
76
|
end
|
|
278
77
|
|
|
279
|
-
def
|
|
280
|
-
|
|
281
|
-
if opts[:escape_html]
|
|
282
|
-
text = "::Haml::Helpers.html_escape(#{text})"
|
|
283
|
-
end
|
|
284
|
-
if opts[:nuke_inner_whitespace]
|
|
285
|
-
text = "(#{text}).strip"
|
|
286
|
-
end
|
|
287
|
-
if opts[:preserve_tag]
|
|
288
|
-
text = "_hamlout.fix_textareas!(::Haml::Helpers.preserve(#{text}))"
|
|
289
|
-
elsif opts[:preserve_script]
|
|
290
|
-
text = "_hamlout.fix_textareas!(::Haml::Helpers.find_and_preserve(#{text}, _hamlout.options[:preserve]))"
|
|
291
|
-
end
|
|
292
|
-
"#{text};"
|
|
78
|
+
def compile_script(node)
|
|
79
|
+
@script_compiler.compile(node) { |n| compile_children(n) }
|
|
293
80
|
end
|
|
294
81
|
|
|
295
|
-
def
|
|
296
|
-
@
|
|
297
|
-
@output_line += text.count("\n")
|
|
82
|
+
def compile_silent_script(node)
|
|
83
|
+
@silent_script_compiler.compile(node) { |n| compile_children(n) }
|
|
298
84
|
end
|
|
299
85
|
|
|
300
|
-
def
|
|
301
|
-
|
|
302
|
-
return "" if diff <= 0
|
|
303
|
-
@output_line = @node.line
|
|
304
|
-
"\n" * diff
|
|
86
|
+
def compile_tag(node)
|
|
87
|
+
@tag_compiler.compile(node) { |n| compile_children(n) }
|
|
305
88
|
end
|
|
306
89
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if last.nil?
|
|
312
|
-
push_silent("_hamlout.rstrip!", false)
|
|
313
|
-
return
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
case last.first
|
|
317
|
-
when :text
|
|
318
|
-
last[1].rstrip!
|
|
319
|
-
if last[1].empty?
|
|
320
|
-
@to_merge.slice! index
|
|
321
|
-
rstrip_buffer! index
|
|
322
|
-
end
|
|
323
|
-
when :script
|
|
324
|
-
last[1].gsub!(/\(haml_temp, (.*?)\);$/, '(haml_temp.rstrip, \1);')
|
|
325
|
-
rstrip_buffer! index - 1
|
|
326
|
-
else
|
|
327
|
-
raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Compiler@to_merge.")
|
|
90
|
+
def runtime_error(error)
|
|
91
|
+
[:multi].tap do |temple|
|
|
92
|
+
error.line.times { temple << [:newline] } if error.line
|
|
93
|
+
temple << [:code, %Q[raise #{error.class}.new(%q[#{error.message}], #{error.line.inspect})]]
|
|
328
94
|
end
|
|
329
95
|
end
|
|
330
96
|
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Haml
|
|
3
|
+
# Compile [:multi, [:static, 'foo'], [:dynamic, 'bar']] to [:dynamic, '"foo#{bar}"']
|
|
4
|
+
class DynamicMerger < Temple::Filter
|
|
5
|
+
def on_multi(*exps)
|
|
6
|
+
exps = exps.dup
|
|
7
|
+
result = [:multi]
|
|
8
|
+
buffer = []
|
|
9
|
+
|
|
10
|
+
until exps.empty?
|
|
11
|
+
type, arg = exps.first
|
|
12
|
+
if type == :dynamic && arg.count("\n") == 0
|
|
13
|
+
buffer << exps.shift
|
|
14
|
+
elsif type == :static && exps.size > (count = arg.count("\n")) &&
|
|
15
|
+
exps[1, count].all? { |e| e == [:newline] }
|
|
16
|
+
(1 + count).times { buffer << exps.shift }
|
|
17
|
+
elsif type == :newline && exps.size > (count = count_newline(exps)) &&
|
|
18
|
+
exps[count].first == :static && count == exps[count].last.count("\n")
|
|
19
|
+
(count + 1).times { buffer << exps.shift }
|
|
20
|
+
else
|
|
21
|
+
result.concat(merge_dynamic(buffer))
|
|
22
|
+
buffer = []
|
|
23
|
+
result << compile(exps.shift)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
result.concat(merge_dynamic(buffer))
|
|
27
|
+
|
|
28
|
+
result.size == 2 ? result[1] : result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def merge_dynamic(exps)
|
|
34
|
+
# Merge exps only when they have both :static and :dynamic
|
|
35
|
+
unless exps.any? { |type,| type == :static } && exps.any? { |type,| type == :dynamic }
|
|
36
|
+
return exps
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
strlit_body = String.new
|
|
40
|
+
exps.each do |type, arg|
|
|
41
|
+
case type
|
|
42
|
+
when :static
|
|
43
|
+
strlit_body << arg.dump.sub!(/\A"/, '').sub!(/"\z/, '').gsub('\n', "\n")
|
|
44
|
+
when :dynamic
|
|
45
|
+
strlit_body << "\#{#{arg}}"
|
|
46
|
+
when :newline
|
|
47
|
+
# newline is added by `gsub('\n', "\n")`
|
|
48
|
+
else
|
|
49
|
+
raise "unexpected type #{type.inspect} is given to #merge_dynamic"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
[[:dynamic, "%Q\0#{strlit_body}\0"]]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def count_newline(exps)
|
|
56
|
+
count = 0
|
|
57
|
+
exps.each do |exp|
|
|
58
|
+
if exp == [:newline]
|
|
59
|
+
count += 1
|
|
60
|
+
else
|
|
61
|
+
return count
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
return count
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|