haml 5.2.2 → 6.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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