haml 5.2.2 → 6.0.0.beta.1

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 +1 -0
  3. data/.github/workflows/test.yml +13 -9
  4. data/.gitignore +16 -16
  5. data/CHANGELOG.md +13 -3
  6. data/Gemfile +18 -11
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +13 -19
  9. data/Rakefile +95 -93
  10. data/bin/bench +66 -0
  11. data/bin/console +11 -0
  12. data/bin/ruby +3 -0
  13. data/bin/setup +7 -0
  14. data/bin/stackprof +27 -0
  15. data/bin/test +24 -0
  16. data/exe/haml +6 -0
  17. data/ext/haml/extconf.rb +10 -0
  18. data/ext/haml/haml.c +537 -0
  19. data/ext/haml/hescape.c +108 -0
  20. data/ext/haml/hescape.h +20 -0
  21. data/haml.gemspec +39 -37
  22. data/lib/haml/ambles.rb +20 -0
  23. data/lib/haml/attribute_builder.rb +135 -179
  24. data/lib/haml/attribute_compiler.rb +85 -194
  25. data/lib/haml/attribute_parser.rb +86 -126
  26. data/lib/haml/cli.rb +154 -0
  27. data/lib/haml/compiler/children_compiler.rb +126 -0
  28. data/lib/haml/compiler/comment_compiler.rb +39 -0
  29. data/lib/haml/compiler/doctype_compiler.rb +46 -0
  30. data/lib/haml/compiler/script_compiler.rb +116 -0
  31. data/lib/haml/compiler/silent_script_compiler.rb +24 -0
  32. data/lib/haml/compiler/tag_compiler.rb +76 -0
  33. data/lib/haml/compiler.rb +63 -296
  34. data/lib/haml/dynamic_merger.rb +67 -0
  35. data/lib/haml/engine.rb +42 -227
  36. data/lib/haml/error.rb +3 -52
  37. data/lib/haml/escapable.rb +6 -70
  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 +49 -0
  54. data/lib/haml/filters.rb +54 -378
  55. data/lib/haml/force_escapable.rb +29 -0
  56. data/lib/haml/haml_error.rb +66 -0
  57. data/lib/haml/helpers.rb +3 -697
  58. data/lib/haml/html.rb +22 -0
  59. data/lib/haml/identity.rb +13 -0
  60. data/lib/haml/object_ref.rb +30 -0
  61. data/lib/haml/parser.rb +179 -49
  62. data/lib/haml/rails_helpers.rb +51 -0
  63. data/lib/haml/rails_template.rb +55 -0
  64. data/lib/haml/railtie.rb +7 -45
  65. data/lib/haml/ruby_expression.rb +32 -0
  66. data/lib/haml/string_splitter.rb +20 -0
  67. data/lib/haml/template.rb +15 -34
  68. data/lib/haml/temple_line_counter.rb +2 -1
  69. data/lib/haml/util.rb +17 -15
  70. data/lib/haml/version.rb +1 -2
  71. data/lib/haml.rb +8 -20
  72. metadata +211 -57
  73. data/.gitmodules +0 -3
  74. data/.yardopts +0 -22
  75. data/TODO +0 -24
  76. data/benchmark.rb +0 -70
  77. data/bin/haml +0 -9
  78. data/lib/haml/.gitattributes +0 -1
  79. data/lib/haml/buffer.rb +0 -182
  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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Filters
4
+ class Ruby < Base
5
+ def compile(node)
6
+ [:code, node.value[:text]]
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Filters
4
+ class Sass < TiltBase
5
+ def compile(node)
6
+ require 'tilt/sass' if explicit_require?('sass')
7
+ temple = [:multi]
8
+ temple << [:static, "<style>\n"]
9
+ temple << compile_with_tilt(node, 'sass', indent_width: 2)
10
+ temple << [:static, "</style>"]
11
+ temple
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Filters
4
+ class Scss < TiltBase
5
+ def compile(node)
6
+ require 'tilt/sass' if explicit_require?('scss')
7
+ temple = [:multi]
8
+ temple << [:static, "<style>\n"]
9
+ temple << compile_with_tilt(node, 'scss', indent_width: 2)
10
+ temple << [:static, "</style>"]
11
+ temple
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module Haml
3
+ class Filters
4
+ class TextBase < Base
5
+ def compile_text!(temple, node, prefix)
6
+ text = node.value[:text].rstrip.gsub(/^/, prefix)
7
+ if ::Haml::Util.contains_interpolation?(node.value[:text])
8
+ # original: Haml::Filters#compile
9
+ text = ::Haml::Util.unescape_interpolation(text).gsub(/(\\+)n/) do |s|
10
+ escapes = $1.size
11
+ next s if escapes % 2 == 0
12
+ "#{'\\' * (escapes - 1)}\n"
13
+ end
14
+ text.prepend("\n")
15
+ temple << [:dynamic, text]
16
+ else
17
+ node.value[:text].split("\n").size.times do
18
+ temple << [:newline]
19
+ end
20
+ temple << [:static, text]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ require 'tilt'
3
+
4
+ module Haml
5
+ class Filters
6
+ class TiltBase < Base
7
+ def self.render(name, source, indent_width: 0)
8
+ text = ::Tilt["t.#{name}"].new { source }.render
9
+ return text if indent_width == 0
10
+ text.gsub!(/^/, ' ' * indent_width)
11
+ end
12
+
13
+ def explicit_require?(needed_registration)
14
+ Gem::Version.new(Tilt::VERSION) >= Gem::Version.new('2.0.0') &&
15
+ !Tilt.registered?(needed_registration)
16
+ end
17
+
18
+ private
19
+
20
+ def compile_with_tilt(node, name, indent_width: 0)
21
+ if ::Haml::Util.contains_interpolation?(node.value[:text])
22
+ dynamic_compile(node, name, indent_width: indent_width)
23
+ else
24
+ static_compile(node, name, indent_width: indent_width)
25
+ end
26
+ end
27
+
28
+ def static_compile(node, name, indent_width: 0)
29
+ temple = [:multi, [:static, TiltBase.render(name, node.value[:text], indent_width: indent_width)]]
30
+ node.value[:text].split("\n").size.times do
31
+ temple << [:newline]
32
+ end
33
+ temple
34
+ end
35
+
36
+ def dynamic_compile(node, name, indent_width: 0)
37
+ # original: Haml::Filters#compile
38
+ text = ::Haml::Util.unescape_interpolation(node.value[:text]).gsub(/(\\+)n/) do |s|
39
+ escapes = $1.size
40
+ next s if escapes % 2 == 0
41
+ "#{'\\' * (escapes - 1)}\n"
42
+ end
43
+ text.prepend("\n").sub!(/\n"\z/, '"')
44
+
45
+ [:dynamic, "::Haml::Filters::TiltBase.render('#{name}', #{text}, indent_width: #{indent_width})"]
46
+ end
47
+ end
48
+ end
49
+ end
data/lib/haml/filters.rb CHANGED
@@ -1,399 +1,75 @@
1
1
  # frozen_string_literal: true
2
-
3
- require "tilt"
2
+ require 'haml/filters/base'
3
+ require 'haml/filters/text_base'
4
+ require 'haml/filters/tilt_base'
5
+ require 'haml/filters/coffee'
6
+ require 'haml/filters/css'
7
+ require 'haml/filters/erb'
8
+ require 'haml/filters/escaped'
9
+ require 'haml/filters/javascript'
10
+ require 'haml/filters/less'
11
+ require 'haml/filters/markdown'
12
+ require 'haml/filters/plain'
13
+ require 'haml/filters/preserve'
14
+ require 'haml/filters/ruby'
15
+ require 'haml/filters/sass'
16
+ require 'haml/filters/scss'
17
+ require 'haml/filters/cdata'
4
18
 
5
19
  module Haml
6
- # The module containing the default Haml filters,
7
- # as well as the base module, {Haml::Filters::Base}.
8
- #
9
- # @see Haml::Filters::Base
10
- module Filters
11
-
12
- extend self
13
-
14
- # @return [{String => Haml::Filters::Base}] a hash mapping filter names to
15
- # classes.
16
- attr_reader :defined
17
- @defined = {}
18
-
19
- # Loads an external template engine from
20
- # [Tilt](https://github.com/rtomayko/tilt) as a filter. This method is used
21
- # internally by Haml to set up filters for Sass, SCSS, Less, Coffeescript,
22
- # and others. It's left public to make it easy for developers to add their
23
- # own Tilt-based filters if they choose.
24
- #
25
- # @return [Module] The generated filter.
26
- # @param [Hash] options Options for generating the filter module.
27
- # @option options [Boolean] :precompiled Whether the filter should be
28
- # precompiled. Erb, Nokogiri and Builder use this, for example.
29
- # @option options [Class] :template_class The Tilt template class to use,
30
- # in the event it can't be inferred from an extension.
31
- # @option options [String] :extension The extension associated with the
32
- # content, for example "markdown". This lets Tilt choose the preferred
33
- # engine when there are more than one.
34
- # @option options [String,Array<String>] :alias Any aliases for the filter.
35
- # For example, :coffee is also available as :coffeescript.
36
- # @option options [String] :extend The name of a module to extend when
37
- # defining the filter. Defaults to "Plain". This allows filters such as
38
- # Coffee to "inherit" from Javascript, wrapping its output in script tags.
39
- # @since 4.0
40
- def register_tilt_filter(name, options = {})
41
- if constants.map(&:to_s).include?(name.to_s)
42
- raise "#{name} filter already defined"
43
- end
44
-
45
- filter = const_set(name, Module.new)
46
- filter.extend const_get(options[:extend] || "Plain")
47
- filter.extend TiltFilter
48
- filter.extend PrecompiledTiltFilter if options.has_key? :precompiled
49
-
50
- if options.has_key? :template_class
51
- filter.template_class = options[:template_class]
52
- else
53
- filter.tilt_extension = options.fetch(:extension) { name.downcase }
54
- end
55
-
56
- # All ":coffeescript" as alias for ":coffee", etc.
57
- if options.has_key?(:alias)
58
- [options[:alias]].flatten.each {|x| Filters.defined[x.to_s] = filter}
59
- end
60
- filter
61
- end
62
-
63
- # Removes a filter from Haml. If the filter was removed, it returns
64
- # the Module that was removed upon success, or nil on failure. If you try
65
- # to redefine a filter, Haml will raise an error. Use this method first to
66
- # explicitly remove the filter before redefining it.
67
- # @return Module The filter module that has been removed
68
- # @since 4.0
69
- def remove_filter(name)
70
- defined.delete name.to_s.downcase
71
- if constants.map(&:to_s).include?(name.to_s)
72
- remove_const name.to_sym
73
- end
74
- end
75
-
76
- # The base module for Haml filters.
77
- # User-defined filters should be modules including this module.
78
- # The name of the filter is taken by downcasing the module name.
79
- # For instance, if the module is named `FooBar`, the filter will be `:foobar`.
80
- #
81
- # A user-defined filter should override either \{#render} or {\#compile}.
82
- # \{#render} is the most common.
83
- # It takes a string, the filter source,
84
- # and returns another string, the result of the filter.
85
- # For example, the following will define a filter named `:sass`:
86
- #
87
- # module Haml::Filters::Sass
88
- # include Haml::Filters::Base
89
- #
90
- # def render(text)
91
- # ::Sass::Engine.new(text).render
92
- # end
93
- # end
94
- #
95
- # For details on overriding \{#compile}, see its documentation.
96
- #
97
- # Note that filters overriding \{#render} automatically support `#{}`
98
- # for interpolating Ruby code.
99
- # Those overriding \{#compile} will need to add such support manually
100
- # if it's desired.
101
- module Base
102
- # This method is automatically called when {Base} is included in a module.
103
- # It automatically defines a filter
104
- # with the downcased name of that module.
105
- # For example, if the module is named `FooBar`, the filter will be `:foobar`.
106
- #
107
- # @param base [Module, Class] The module that this is included in
108
- def self.included(base)
109
- Filters.defined[base.name.split("::").last.downcase] = base
110
- base.extend(base)
111
- end
112
-
113
- # Takes the source text that should be passed to the filter
114
- # and returns the result of running the filter on that string.
115
- #
116
- # This should be overridden in most individual filter modules
117
- # to render text with the given filter.
118
- # If \{#compile} is overridden, however, \{#render} doesn't need to be.
119
- #
120
- # @param text [String] The source text for the filter to process
121
- # @return [String] The filtered result
122
- # @raise [Haml::Error] if it's not overridden
123
- def render(_text)
124
- raise Error.new("#{self.inspect}#render not defined!")
125
- end
126
-
127
- # Same as \{#render}, but takes a {Haml::Engine} options hash as well.
128
- # It's only safe to rely on options made available in {Haml::Engine#options\_for\_buffer}.
129
- #
130
- # @see #render
131
- # @param text [String] The source text for the filter to process
132
- # @return [String] The filtered result
133
- # @raise [Haml::Error] if it or \{#render} isn't overridden
134
- def render_with_options(text, _options)
135
- render(text)
136
- end
137
-
138
- # Same as \{#compile}, but requires the necessary files first.
139
- # *This is used by {Haml::Engine} and is not intended to be overridden or used elsewhere.*
140
- #
141
- # @see #compile
142
- def internal_compile(*args)
143
- compile(*args)
144
- end
145
-
146
- # This should be overridden when a filter needs to have access to the Haml
147
- # evaluation context. Rather than applying a filter to a string at
148
- # compile-time, \{#compile} uses the {Haml::Compiler} instance to compile
149
- # the string to Ruby code that will be executed in the context of the
150
- # active Haml template.
151
- #
152
- # Warning: the {Haml::Compiler} interface is neither well-documented
153
- # nor guaranteed to be stable.
154
- # If you want to make use of it, you'll probably need to look at the
155
- # source code and should test your filter when upgrading to new Haml
156
- # versions.
157
- #
158
- # @param compiler [Haml::Compiler] The compiler instance
159
- # @param text [String] The text of the filter
160
- # @raise [Haml::Error] if none of \{#compile}, \{#render}, and
161
- # \{#render_with_options} are overridden
162
- def compile(compiler, text)
163
- filter = self
164
- compiler.instance_eval do
165
- if contains_interpolation?(text)
166
- return if options[:suppress_eval]
167
-
168
- escape = options[:escape_filter_interpolations]
169
- # `escape_filter_interpolations` defaults to `escape_html` if unset.
170
- escape = options[:escape_html] if escape.nil?
171
-
172
- text = unescape_interpolation(text, escape).gsub(/(\\+)n/) do |s|
173
- escapes = $1.size
174
- next s if escapes % 2 == 0
175
- "#{'\\' * (escapes - 1)}\n"
176
- end
177
- # We need to add a newline at the beginning to get the
178
- # filter lines to line up (since the Haml filter contains
179
- # a line that doesn't show up in the source, namely the
180
- # filter name). Then we need to escape the trailing
181
- # newline so that the whole filter block doesn't take up
182
- # too many.
183
- text = %[\n#{text.sub(/\n"\Z/, "\\n\"")}]
184
- push_script <<RUBY.rstrip, :escape_html => false
185
- find_and_preserve(#{filter.inspect}.render_with_options(#{text}, _hamlout.options))
186
- RUBY
187
- return
188
- end
189
-
190
- rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text.to_s, compiler.options), compiler.options[:preserve])
191
- push_text("#{rendered.rstrip}\n")
192
- end
193
- end
194
- end
195
-
196
- # Does not parse the filtered text.
197
- # This is useful for large blocks of text without HTML tags, when you don't
198
- # want lines starting with `.` or `-` to be parsed.
199
- module Plain
200
- include Base
201
-
202
- # @see Base#render
203
- def render(text); text; end
204
- end
205
-
206
- # Surrounds the filtered text with `<script>` and CDATA tags. Useful for
207
- # including inline Javascript.
208
- module Javascript
209
- include Base
210
-
211
- # @see Base#render_with_options
212
- def render_with_options(text, options)
213
- indent = options[:cdata] ? ' ' : ' ' # 4 or 2 spaces
214
- if options[:format] == :html5
215
- type = ''
216
- else
217
- type = " type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}"
218
- end
219
-
220
- text = text.rstrip
221
- text.gsub!("\n", "\n#{indent}")
20
+ class Filters
21
+ @registered = {}
222
22
 
223
- %!<script#{type}>\n#{" //<![CDATA[\n" if options[:cdata]}#{indent}#{text}\n#{" //]]>\n" if options[:cdata]}</script>!
224
- end
225
- end
226
-
227
- # Surrounds the filtered text with `<style>` and CDATA tags. Useful for
228
- # including inline CSS.
229
- module Css
230
- include Base
231
-
232
- # @see Base#render_with_options
233
- def render_with_options(text, options)
234
- indent = options[:cdata] ? ' ' : ' ' # 4 or 2 spaces
235
- if options[:format] == :html5
236
- type = ''
237
- else
238
- type = " type=#{options[:attr_wrapper]}text/css#{options[:attr_wrapper]}"
239
- end
240
-
241
- text = text.rstrip
242
- text.gsub!("\n", "\n#{indent}")
243
-
244
- %(<style#{type}>\n#{" /*<![CDATA[*/\n" if options[:cdata]}#{indent}#{text}\n#{" /*]]>*/\n" if options[:cdata]}</style>)
245
- end
246
- end
247
-
248
- # Surrounds the filtered text with CDATA tags.
249
- module Cdata
250
- include Base
251
-
252
- # @see Base#render
253
- def render(text)
254
- "<![CDATA[#{"\n#{text.rstrip}".gsub("\n", "\n ")}\n]]>"
255
- end
256
- end
257
-
258
- # Works the same as {Plain}, but HTML-escapes the text before placing it in
259
- # the document.
260
- module Escaped
261
- include Base
262
-
263
- # @see Base#render
264
- def render(text)
265
- Haml::Helpers.html_escape text
266
- end
267
- end
268
-
269
- # Parses the filtered text with the normal Ruby interpreter. Creates an IO
270
- # object named `haml_io`, anything written to it is output into the Haml
271
- # document. In previous version this filter redirected any output to `$stdout`
272
- # to the Haml document, this was not threadsafe and has been removed, you
273
- # should use `haml_io` instead.
274
- #
275
- # Not available if the {file:REFERENCE.md#suppress_eval-option `:suppress_eval`}
276
- # option is set to true. The Ruby code is evaluated in the same context as
277
- # the Haml template.
278
- module Ruby
279
- include Base
280
- require 'stringio'
23
+ class << self
24
+ attr_reader :registered
281
25
 
282
- # @see Base#compile
283
- def compile(compiler, text)
284
- return if compiler.options[:suppress_eval]
285
- compiler.instance_eval do
286
- push_silent "#{<<-FIRST.tr("\n", ';')}#{text}#{<<-LAST.tr("\n", ';')}"
287
- begin
288
- haml_io = StringIO.new(_hamlout.buffer, 'a')
289
- FIRST
290
- ensure
291
- haml_io.close
292
- haml_io = nil
293
- end
294
- LAST
26
+ def remove_filter(name)
27
+ registered.delete(name.to_s.downcase.to_sym)
28
+ if constants.map(&:to_s).include?(name.to_s)
29
+ remove_const name.to_sym
295
30
  end
296
31
  end
297
- end
298
32
 
299
- # Inserts the filtered text into the template with whitespace preserved.
300
- # `preserve`d blocks of text aren't indented, and newlines are replaced with
301
- # the HTML escape code for newlines, to preserve nice-looking output.
302
- #
303
- # @see Haml::Helpers#preserve
304
- module Preserve
305
- include Base
33
+ private
306
34
 
307
- # @see Base#render
308
- def render(text)
309
- Haml::Helpers.preserve text
35
+ def register(name, compiler)
36
+ registered[name] = compiler
310
37
  end
311
38
  end
312
39
 
313
- # @private
314
- module TiltFilter
315
- extend self
316
- attr_accessor :tilt_extension, :options
317
- attr_writer :template_class
318
-
319
- def template_class
320
- (@template_class if defined? @template_class) or begin
321
- @template_class = Tilt["t.#{tilt_extension}"] or
322
- raise Error.new(Error.message(:cant_run_filter, tilt_extension))
323
- rescue LoadError => e
324
- dep = e.message.split('--').last.strip
325
- raise Error.new(Error.message(:gem_install_filter_deps, tilt_extension, dep))
326
- end
327
- end
328
-
329
- def self.extended(base)
330
- base.options = {}
331
- # There's a bug in 1.9.2 where the same parse tree cannot be shared
332
- # across several singleton classes -- this bug is fixed in 1.9.3.
333
- # We work around this by using a string eval instead of a block eval
334
- # so that a new parse tree is created for each singleton class.
335
- base.instance_eval %Q{
336
- include Base
337
-
338
- def render_with_options(text, compiler_options)
339
- text = template_class.new(nil, 1, options) {text}.render
340
- super(text, compiler_options)
341
- end
342
- }
343
- end
40
+ register :coffee, Coffee
41
+ register :coffeescript, CoffeeScript
42
+ register :css, Css
43
+ register :erb, Erb
44
+ register :escaped, Escaped
45
+ register :javascript, Javascript
46
+ register :less, Less
47
+ register :markdown, Markdown
48
+ register :plain, Plain
49
+ register :preserve, Preserve
50
+ register :ruby, Ruby
51
+ register :sass, Sass
52
+ register :scss, Scss
53
+ register :cdata, Cdata
54
+
55
+ def initialize(options = {})
56
+ @options = options
57
+ @compilers = {}
344
58
  end
345
59
 
346
- # @private
347
- module PrecompiledTiltFilter
348
- def precompiled(text)
349
- template_class.new(nil, 1, options) { text }.send(:precompiled, {}).first
350
- end
351
-
352
- def compile(compiler, text)
353
- return if compiler.options[:suppress_eval]
354
- compiler.send(:push_script, precompiled(text))
355
- end
60
+ def compile(node)
61
+ node.value[:text] ||= ''
62
+ find_compiler(node).compile(node)
356
63
  end
357
64
 
358
- # @!parse module Sass; end
359
- register_tilt_filter "Sass", :extend => "Css"
360
-
361
- # @!parse module Scss; end
362
- register_tilt_filter "Scss", :extend => "Css"
363
-
364
- # @!parse module Less; end
365
- register_tilt_filter "Less", :extend => "Css"
366
-
367
- # @!parse module Markdown; end
368
- register_tilt_filter "Markdown"
369
-
370
- # @!parse module Erb; end
371
- register_tilt_filter "Erb", :precompiled => true
65
+ private
372
66
 
373
- # @!parse module Coffee; end
374
- register_tilt_filter "Coffee", :alias => "coffeescript", :extend => "Javascript"
67
+ def find_compiler(node)
68
+ name = node.value[:name].to_sym
69
+ compiler = Filters.registered[name]
70
+ raise FilterNotFound.new("FilterCompiler for '#{name}' was not found", node.line.to_i - 1) unless compiler
375
71
 
376
- # Parses the filtered text with ERB.
377
- # Not available if the {file:REFERENCE.md#suppress_eval-option
378
- # `:suppress_eval`} option is set to true. Embedded Ruby code is evaluated
379
- # in the same context as the Haml template.
380
- module Erb
381
- class << self
382
- def precompiled(text)
383
- #workaround for https://github.com/rtomayko/tilt/pull/183
384
- require 'erubis' if (defined?(::Erubis) && !defined?(::Erubis::Eruby))
385
- super.sub(/^#coding:.*?\n/, '')
386
- end
387
- end
72
+ @compilers[name] ||= compiler.new(@options)
388
73
  end
389
74
  end
390
75
  end
391
-
392
- # These filters have been demoted to Haml Contrib but are still included by
393
- # default in Haml 4.0. Still, we rescue from load error if for some reason
394
- # haml-contrib is not installed.
395
- begin
396
- require "haml/filters/maruku"
397
- require "haml/filters/textile"
398
- rescue LoadError
399
- end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'haml/escapable'
3
+
4
+ module Haml
5
+ # This module allows Temple::Filter to dispatch :fescape on `#compile`.
6
+ module FescapeDispathcer
7
+ def on_fescape(flag, exp)
8
+ [:fescape, flag, compile(exp)]
9
+ end
10
+ end
11
+ ::Temple::Filter.include FescapeDispathcer
12
+
13
+ # Unlike Haml::Escapable, this escapes value even if it's html_safe.
14
+ class ForceEscapable < Escapable
15
+ def initialize(opts = {})
16
+ super
17
+ @escape_code = options[:escape_code] || "::Haml::Util.escape_html((%s))"
18
+ @escaper = eval("proc {|v| #{@escape_code % 'v'} }")
19
+ end
20
+
21
+ alias_method :on_fescape, :on_escape
22
+
23
+ # ForceEscapable doesn't touch :escape expression.
24
+ # This method is not used if it's inserted after Haml::Escapable.
25
+ def on_escape(flag, exp)
26
+ [:escape, flag, compile(exp)]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Haml
4
+ # An exception raised by Haml code.
5
+ # TODO: unify Haml::Error (former Hamlit::Error) and Haml::HamlError (former Haml::Error)
6
+ class HamlError < StandardError
7
+
8
+ MESSAGES = {
9
+ bad_script_indent: '"%s" is indented at wrong level: expected %d, but was at %d.',
10
+ cant_run_filter: 'Can\'t run "%s" filter; you must require its dependencies first',
11
+ cant_use_tabs_and_spaces: "Indentation can't use both tabs and spaces.",
12
+ deeper_indenting: "The line was indented %d levels deeper than the previous line.",
13
+ filter_not_defined: 'Filter "%s" is not defined.',
14
+ gem_install_filter_deps: '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
15
+ illegal_element: "Illegal element: classes and ids must have values.",
16
+ illegal_nesting_content: "Illegal nesting: nesting within a tag that already has content is illegal.",
17
+ illegal_nesting_header: "Illegal nesting: nesting within a header command is illegal.",
18
+ illegal_nesting_line: "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
19
+ illegal_nesting_plain: "Illegal nesting: nesting within plain text is illegal.",
20
+ illegal_nesting_self_closing: "Illegal nesting: nesting within a self-closing tag is illegal.",
21
+ inconsistent_indentation: "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
22
+ indenting_at_start: "Indenting at the beginning of the document is illegal.",
23
+ install_haml_contrib: 'To use the "%s" filter, please install the haml-contrib gem.',
24
+ invalid_attribute_list: 'Invalid attribute list: %s.',
25
+ invalid_filter_name: 'Invalid filter name ":%s".',
26
+ invalid_tag: 'Invalid tag: "%s".',
27
+ missing_if: 'Got "%s" with no preceding "if"',
28
+ no_ruby_code: "There's no Ruby code for %s to evaluate.",
29
+ self_closing_content: "Self-closing tags can't have content.",
30
+ unbalanced_brackets: 'Unbalanced brackets.',
31
+ no_end: <<-END
32
+ You don't need to use "- end" in Haml. Un-indent to close a block:
33
+ - if foo?
34
+ %strong Foo!
35
+ - else
36
+ Not foo.
37
+ %p This line is un-indented, so it isn't part of the "if" block
38
+ END
39
+ }.freeze
40
+
41
+ def self.message(key, *args)
42
+ string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
43
+ (args.empty? ? string : string % args).rstrip
44
+ end
45
+
46
+ # The line of the template on which the error occurred.
47
+ #
48
+ # @return [Fixnum]
49
+ attr_reader :line
50
+
51
+ # @param message [String] The error message
52
+ # @param line [Fixnum] See \{#line}
53
+ def initialize(message = nil, line = nil)
54
+ super(message)
55
+ @line = line
56
+ end
57
+ end
58
+
59
+ # SyntaxError is the type of exception raised when Haml encounters an
60
+ # ill-formatted document.
61
+ # It's not particularly interesting,
62
+ # except in that it's a subclass of {Haml::HamlError}.
63
+ class HamlSyntaxError < HamlError; end
64
+
65
+ class HamlInvalidAttributeNameError < HamlSyntaxError; end
66
+ end