haml 4.0.6 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +19 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +72 -0
  5. data/.yardopts +2 -3
  6. data/CHANGELOG.md +138 -4
  7. data/FAQ.md +4 -14
  8. data/Gemfile +16 -0
  9. data/MIT-LICENSE +2 -2
  10. data/README.md +79 -42
  11. data/REFERENCE.md +142 -67
  12. data/Rakefile +44 -63
  13. data/TODO +24 -0
  14. data/benchmark.rb +70 -0
  15. data/haml.gemspec +45 -0
  16. data/lib/haml.rb +2 -0
  17. data/lib/haml/.gitattributes +1 -0
  18. data/lib/haml/attribute_builder.rb +164 -0
  19. data/lib/haml/attribute_compiler.rb +235 -0
  20. data/lib/haml/attribute_parser.rb +150 -0
  21. data/lib/haml/buffer.rb +29 -136
  22. data/lib/haml/compiler.rb +110 -320
  23. data/lib/haml/engine.rb +34 -41
  24. data/lib/haml/error.rb +28 -24
  25. data/lib/haml/escapable.rb +77 -0
  26. data/lib/haml/exec.rb +38 -20
  27. data/lib/haml/filters.rb +22 -27
  28. data/lib/haml/generator.rb +42 -0
  29. data/lib/haml/helpers.rb +134 -89
  30. data/lib/haml/helpers/action_view_extensions.rb +4 -2
  31. data/lib/haml/helpers/action_view_mods.rb +45 -60
  32. data/lib/haml/helpers/action_view_xss_mods.rb +2 -0
  33. data/lib/haml/helpers/safe_erubi_template.rb +20 -0
  34. data/lib/haml/helpers/safe_erubis_template.rb +5 -1
  35. data/lib/haml/helpers/xss_mods.rb +23 -13
  36. data/lib/haml/options.rb +63 -69
  37. data/lib/haml/parser.rb +292 -228
  38. data/lib/haml/plugin.rb +37 -0
  39. data/lib/haml/railtie.rb +38 -12
  40. data/lib/haml/sass_rails_filter.rb +18 -4
  41. data/lib/haml/template.rb +13 -6
  42. data/lib/haml/template/options.rb +13 -2
  43. data/lib/haml/temple_engine.rb +123 -0
  44. data/lib/haml/temple_line_counter.rb +30 -0
  45. data/lib/haml/util.rb +83 -202
  46. data/lib/haml/version.rb +3 -1
  47. data/yard/default/.gitignore +1 -0
  48. data/yard/default/fulldoc/html/css/common.sass +15 -0
  49. data/yard/default/layout/html/footer.erb +12 -0
  50. metadata +73 -108
  51. data/lib/haml/template/plugin.rb +0 -41
  52. data/test/engine_test.rb +0 -2013
  53. data/test/erb/_av_partial_1.erb +0 -12
  54. data/test/erb/_av_partial_2.erb +0 -8
  55. data/test/erb/action_view.erb +0 -62
  56. data/test/erb/standard.erb +0 -55
  57. data/test/filters_test.rb +0 -254
  58. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  59. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  60. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  61. data/test/gemfiles/Gemfile.rails-4.0.x +0 -5
  62. data/test/helper_test.rb +0 -583
  63. data/test/markaby/standard.mab +0 -52
  64. data/test/mocks/article.rb +0 -6
  65. data/test/parser_test.rb +0 -105
  66. data/test/results/content_for_layout.xhtml +0 -12
  67. data/test/results/eval_suppressed.xhtml +0 -9
  68. data/test/results/helpers.xhtml +0 -70
  69. data/test/results/helpful.xhtml +0 -10
  70. data/test/results/just_stuff.xhtml +0 -70
  71. data/test/results/list.xhtml +0 -12
  72. data/test/results/nuke_inner_whitespace.xhtml +0 -40
  73. data/test/results/nuke_outer_whitespace.xhtml +0 -148
  74. data/test/results/original_engine.xhtml +0 -20
  75. data/test/results/partial_layout.xhtml +0 -5
  76. data/test/results/partial_layout_erb.xhtml +0 -5
  77. data/test/results/partials.xhtml +0 -21
  78. data/test/results/render_layout.xhtml +0 -3
  79. data/test/results/silent_script.xhtml +0 -74
  80. data/test/results/standard.xhtml +0 -162
  81. data/test/results/tag_parsing.xhtml +0 -23
  82. data/test/results/very_basic.xhtml +0 -5
  83. data/test/results/whitespace_handling.xhtml +0 -90
  84. data/test/template_test.rb +0 -354
  85. data/test/templates/_av_partial_1.haml +0 -9
  86. data/test/templates/_av_partial_1_ugly.haml +0 -9
  87. data/test/templates/_av_partial_2.haml +0 -5
  88. data/test/templates/_av_partial_2_ugly.haml +0 -5
  89. data/test/templates/_layout.erb +0 -3
  90. data/test/templates/_layout_for_partial.haml +0 -3
  91. data/test/templates/_partial.haml +0 -8
  92. data/test/templates/_text_area.haml +0 -3
  93. data/test/templates/_text_area_helper.html.haml +0 -4
  94. data/test/templates/action_view.haml +0 -47
  95. data/test/templates/action_view_ugly.haml +0 -47
  96. data/test/templates/breakage.haml +0 -8
  97. data/test/templates/content_for_layout.haml +0 -8
  98. data/test/templates/eval_suppressed.haml +0 -11
  99. data/test/templates/helpers.haml +0 -55
  100. data/test/templates/helpful.haml +0 -11
  101. data/test/templates/just_stuff.haml +0 -85
  102. data/test/templates/list.haml +0 -12
  103. data/test/templates/nuke_inner_whitespace.haml +0 -32
  104. data/test/templates/nuke_outer_whitespace.haml +0 -144
  105. data/test/templates/original_engine.haml +0 -17
  106. data/test/templates/partial_layout.haml +0 -3
  107. data/test/templates/partial_layout_erb.erb +0 -4
  108. data/test/templates/partialize.haml +0 -1
  109. data/test/templates/partials.haml +0 -12
  110. data/test/templates/render_layout.haml +0 -2
  111. data/test/templates/silent_script.haml +0 -45
  112. data/test/templates/standard.haml +0 -43
  113. data/test/templates/standard_ugly.haml +0 -43
  114. data/test/templates/tag_parsing.haml +0 -21
  115. data/test/templates/very_basic.haml +0 -4
  116. data/test/templates/whitespace_handling.haml +0 -87
  117. data/test/test_helper.rb +0 -81
  118. data/test/util_test.rb +0 -63
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  require 'haml/parser'
@@ -7,6 +9,7 @@ require 'haml/helpers'
7
9
  require 'haml/buffer'
8
10
  require 'haml/filters'
9
11
  require 'haml/error'
12
+ require 'haml/temple_engine'
10
13
 
11
14
  module Haml
12
15
  # This is the frontend for using Haml programmatically.
@@ -35,9 +38,6 @@ module Haml
35
38
  # @return [String]
36
39
  attr_accessor :indentation
37
40
 
38
- attr_accessor :compiler
39
- attr_accessor :parser
40
-
41
41
  # Tilt currently depends on these moved methods, provide a stable API
42
42
  def_delegators :compiler, :precompiled, :precompiled_method_return_value
43
43
 
@@ -52,25 +52,28 @@ module Haml
52
52
  # see {file:REFERENCE.md#options the Haml options documentation}
53
53
  # @raise [Haml::Error] if there's a Haml syntax error in the template
54
54
  def initialize(template, options = {})
55
+ # Reflect changes of `Haml::Options.defaults` to `Haml::TempleEngine` options, but `#initialize_encoding`
56
+ # should be run against the arguemnt `options[:encoding]` for backward compatibility with old `Haml::Engine`.
57
+ options = Options.defaults.dup.tap { |o| o.delete(:encoding) }.merge!(options)
55
58
  @options = Options.new(options)
56
59
 
57
60
  @template = check_haml_encoding(template) do |msg, line|
58
61
  raise Haml::Error.new(msg, line)
59
62
  end
60
63
 
61
- initialize_encoding options[:encoding]
62
-
63
- @parser = @options.parser_class.new(@template, @options)
64
- @compiler = @options.compiler_class.new(@options)
64
+ @temple_engine = TempleEngine.new(options)
65
+ @temple_engine.compile(@template)
66
+ end
65
67
 
66
- @compiler.compile(@parser.parse)
68
+ # Deprecated API for backword compatibility
69
+ def compiler
70
+ @temple_engine
67
71
  end
68
72
 
69
73
  # Processes the template and returns the result as a string.
70
74
  #
71
75
  # `scope` is the context in which the template is evaluated.
72
- # If it's a `Binding` or `Proc` object,
73
- # Haml uses it as the second argument to `Kernel#eval`;
76
+ # If it's a `Binding`, Haml uses it as the second argument to `Kernel#eval`;
74
77
  # otherwise, Haml just uses its `#instance_eval` context.
75
78
  #
76
79
  # Note that Haml modifies the evaluation context
@@ -95,23 +98,23 @@ module Haml
95
98
  # within the template.
96
99
  #
97
100
  # Due to some Ruby quirks,
98
- # if `scope` is a `Binding` or `Proc` object and a block is given,
101
+ # if `scope` is a `Binding` object and a block is given,
99
102
  # the evaluation context may not be quite what the user expects.
100
103
  # In particular, it's equivalent to passing `eval("self", scope)` as `scope`.
101
104
  # This won't have an effect in most cases,
102
105
  # but if you're relying on local variables defined in the context of `scope`,
103
106
  # they won't work.
104
107
  #
105
- # @param scope [Binding, Proc, Object] The context in which the template is evaluated
108
+ # @param scope [Binding, Object] The context in which the template is evaluated
106
109
  # @param locals [{Symbol => Object}] Local variables that will be made available
107
110
  # to the template
108
111
  # @param block [#to_proc] A block that can be yielded to within the template
109
112
  # @return [String] The rendered template
110
113
  def render(scope = Object.new, locals = {}, &block)
111
- parent = scope.instance_variable_defined?('@haml_buffer') ? scope.instance_variable_get('@haml_buffer') : nil
114
+ parent = scope.instance_variable_defined?(:@haml_buffer) ? scope.instance_variable_get(:@haml_buffer) : nil
112
115
  buffer = Haml::Buffer.new(parent, @options.for_buffer)
113
116
 
114
- if scope.is_a?(Binding) || scope.is_a?(Proc)
117
+ if scope.is_a?(Binding)
115
118
  scope_object = eval("self", scope)
116
119
  scope = scope_object.instance_eval{binding} if block_given?
117
120
  else
@@ -121,20 +124,16 @@ module Haml
121
124
 
122
125
  set_locals(locals.merge(:_hamlout => buffer, :_erbout => buffer.buffer), scope, scope_object)
123
126
 
124
- scope_object.instance_eval do
125
- extend Haml::Helpers
126
- @haml_buffer = buffer
127
- end
127
+ scope_object.extend(Haml::Helpers)
128
+ scope_object.instance_variable_set(:@haml_buffer, buffer)
128
129
  begin
129
- eval(@compiler.precompiled_with_return_value, scope, @options[:filename], @options[:line])
130
+ eval(@temple_engine.precompiled_with_return_value, scope, @options.filename, @options.line)
130
131
  rescue ::SyntaxError => e
131
132
  raise SyntaxError, e.message
132
133
  end
133
134
  ensure
134
135
  # Get rid of the current buffer
135
- scope_object.instance_eval do
136
- @haml_buffer = buffer.upper if buffer
137
- end
136
+ scope_object.instance_variable_set(:@haml_buffer, buffer.upper) if buffer
138
137
  end
139
138
  alias_method :to_html, :render
140
139
 
@@ -159,11 +158,11 @@ module Haml
159
158
  #
160
159
  # The proc doesn't take a block; any yields in the template will fail.
161
160
  #
162
- # @param scope [Binding, Proc, Object] The context in which the template is evaluated
161
+ # @param scope [Binding, Object] The context in which the template is evaluated
163
162
  # @param local_names [Array<Symbol>] The names of the locals that can be passed to the proc
164
163
  # @return [Proc] The proc that will run the template
165
164
  def render_proc(scope = Object.new, *local_names)
166
- if scope.is_a?(Binding) || scope.is_a?(Proc)
165
+ if scope.is_a?(Binding)
167
166
  scope_object = eval("self", scope)
168
167
  else
169
168
  scope_object = scope
@@ -171,8 +170,13 @@ module Haml
171
170
  end
172
171
 
173
172
  begin
174
- eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" +
175
- compiler.precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], @options[:line])
173
+ str = @temple_engine.precompiled_with_ambles(local_names)
174
+ eval(
175
+ "Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {}; #{str}}\n",
176
+ scope,
177
+ @options.filename,
178
+ @options.line
179
+ )
176
180
  rescue ::SyntaxError => e
177
181
  raise SyntaxError, e.message
178
182
  end
@@ -182,7 +186,7 @@ module Haml
182
186
  # that renders the template and returns the result as a string.
183
187
  #
184
188
  # If `object` is a class or module,
185
- # the method will instead by defined as an instance method.
189
+ # the method will instead be defined as an instance method.
186
190
  # For example:
187
191
  #
188
192
  # t = Time.now
@@ -219,25 +223,14 @@ module Haml
219
223
  def def_method(object, name, *local_names)
220
224
  method = object.is_a?(Module) ? :module_eval : :instance_eval
221
225
 
222
- object.send(method, "def #{name}(_haml_locals = {}); #{compiler.precompiled_with_ambles(local_names)}; end",
223
- @options[:filename], @options[:line])
226
+ object.send(method, "def #{name}(_haml_locals = {}); #{@temple_engine.precompiled_with_ambles(local_names)}; end",
227
+ @options.filename, @options.line)
224
228
  end
225
229
 
226
230
  private
227
231
 
228
- if RUBY_VERSION < "1.9"
229
- def initialize_encoding(given_value)
230
- end
231
- else
232
- def initialize_encoding(given_value)
233
- unless given_value
234
- @options.encoding = Encoding.default_internal || @template.encoding
235
- end
236
- end
237
- end
238
-
239
232
  def set_locals(locals, scope, scope_object)
240
- scope_object.send(:instance_variable_set, '@_haml_locals', locals)
233
+ scope_object.instance_variable_set :@_haml_locals, locals
241
234
  set_locals = locals.keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n")
242
235
  eval(set_locals, scope)
243
236
  end
@@ -1,31 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Haml
2
4
  # An exception raised by Haml code.
3
5
  class Error < StandardError
4
6
 
5
7
  MESSAGES = {
6
- :bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
7
- :cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
8
- :cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
9
- :deeper_indenting => "The line was indented %d levels deeper than the previous line.",
10
- :filter_not_defined => 'Filter "%s" is not defined.',
11
- :gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
12
- :illegal_element => "Illegal element: classes and ids must have values.",
13
- :illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
14
- :illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
15
- :illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
16
- :illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
17
- :illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
18
- :inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
19
- :indenting_at_start => "Indenting at the beginning of the document is illegal.",
20
- :install_haml_contrib => 'To use the "%s" filter, please install the haml-contrib gem.',
21
- :invalid_attribute_list => 'Invalid attribute list: %s.',
22
- :invalid_filter_name => 'Invalid filter name ":%s".',
23
- :invalid_tag => 'Invalid tag: "%s".',
24
- :missing_if => 'Got "%s" with no preceding "if"',
25
- :no_ruby_code => "There's no Ruby code for %s to evaluate.",
26
- :self_closing_content => "Self-closing tags can't have content.",
27
- :unbalanced_brackets => 'Unbalanced brackets.',
28
- :no_end => <<-END
8
+ bad_script_indent: '"%s" is indented at wrong level: expected %d, but was at %d.',
9
+ cant_run_filter: 'Can\'t run "%s" filter; you must require its dependencies first',
10
+ cant_use_tabs_and_spaces: "Indentation can't use both tabs and spaces.",
11
+ deeper_indenting: "The line was indented %d levels deeper than the previous line.",
12
+ filter_not_defined: 'Filter "%s" is not defined.',
13
+ gem_install_filter_deps: '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
14
+ illegal_element: "Illegal element: classes and ids must have values.",
15
+ illegal_nesting_content: "Illegal nesting: nesting within a tag that already has content is illegal.",
16
+ illegal_nesting_header: "Illegal nesting: nesting within a header command is illegal.",
17
+ illegal_nesting_line: "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
18
+ illegal_nesting_plain: "Illegal nesting: nesting within plain text is illegal.",
19
+ illegal_nesting_self_closing: "Illegal nesting: nesting within a self-closing tag is illegal.",
20
+ inconsistent_indentation: "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
21
+ indenting_at_start: "Indenting at the beginning of the document is illegal.",
22
+ install_haml_contrib: 'To use the "%s" filter, please install the haml-contrib gem.',
23
+ invalid_attribute_list: 'Invalid attribute list: %s.',
24
+ invalid_filter_name: 'Invalid filter name ":%s".',
25
+ invalid_tag: 'Invalid tag: "%s".',
26
+ missing_if: 'Got "%s" with no preceding "if"',
27
+ no_ruby_code: "There's no Ruby code for %s to evaluate.",
28
+ self_closing_content: "Self-closing tags can't have content.",
29
+ unbalanced_brackets: 'Unbalanced brackets.',
30
+ no_end: <<-END
29
31
  You don't need to use "- end" in Haml. Un-indent to close a block:
30
32
  - if foo?
31
33
  %strong Foo!
@@ -33,7 +35,7 @@ You don't need to use "- end" in Haml. Un-indent to close a block:
33
35
  Not foo.
34
36
  %p This line is un-indented, so it isn't part of the "if" block
35
37
  END
36
- }
38
+ }.freeze
37
39
 
38
40
  def self.message(key, *args)
39
41
  string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
@@ -58,4 +60,6 @@ END
58
60
  # It's not particularly interesting,
59
61
  # except in that it's a subclass of {Haml::Error}.
60
62
  class SyntaxError < Error; end
63
+
64
+ class InvalidAttributeNameError < SyntaxError; end
61
65
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Haml
4
+ # Like Temple::Filters::Escapable, but with support for escaping by
5
+ # Haml::Herlpers.html_escape and Haml::Herlpers.escape_once.
6
+ class Escapable < Temple::Filter
7
+ # Special value of `flag` to ignore html_safe?
8
+ EscapeSafeBuffer = Struct.new(:value)
9
+
10
+ def initialize(*)
11
+ super
12
+ @escape = false
13
+ @escape_safe_buffer = false
14
+ end
15
+
16
+ def on_escape(flag, exp)
17
+ old_escape, old_escape_safe_buffer = @escape, @escape_safe_buffer
18
+ @escape_safe_buffer = flag.is_a?(EscapeSafeBuffer)
19
+ @escape = @escape_safe_buffer ? flag.value : flag
20
+ compile(exp)
21
+ ensure
22
+ @escape, @escape_safe_buffer = old_escape, old_escape_safe_buffer
23
+ end
24
+
25
+ # The same as Haml::AttributeBuilder.build_attributes
26
+ def on_static(value)
27
+ [:static,
28
+ if @escape == :once
29
+ escape_once(value)
30
+ elsif @escape
31
+ escape(value)
32
+ else
33
+ value
34
+ end
35
+ ]
36
+ end
37
+
38
+ # The same as Haml::AttributeBuilder.build_attributes
39
+ def on_dynamic(value)
40
+ [:dynamic,
41
+ if @escape == :once
42
+ escape_once_code(value)
43
+ elsif @escape
44
+ escape_code(value)
45
+ else
46
+ "(#{value}).to_s"
47
+ end
48
+ ]
49
+ end
50
+
51
+ private
52
+
53
+ def escape_once(value)
54
+ if @escape_safe_buffer
55
+ ::Haml::Helpers.escape_once_without_haml_xss(value)
56
+ else
57
+ ::Haml::Helpers.escape_once(value)
58
+ end
59
+ end
60
+
61
+ def escape(value)
62
+ if @escape_safe_buffer
63
+ ::Haml::Helpers.html_escape_without_haml_xss(value)
64
+ else
65
+ ::Haml::Helpers.html_escape(value)
66
+ end
67
+ end
68
+
69
+ def escape_once_code(value)
70
+ "::Haml::Helpers.escape_once#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
71
+ end
72
+
73
+ def escape_code(value)
74
+ "::Haml::Helpers.html_escape#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
75
+ end
76
+ end
77
+ end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
- require 'fileutils'
3
4
  require 'rbconfig'
4
5
  require 'pp'
5
6
 
@@ -120,7 +121,7 @@ module Haml
120
121
  @options[:input], @options[:output] = input, output
121
122
  end
122
123
 
123
- COLORS = { :red => 31, :green => 32, :yellow => 33 }
124
+ COLORS = {red: 31, green: 32, yellow: 33}.freeze
124
125
 
125
126
  # Prints a status message about performing the given action,
126
127
  # colored using the given color (via terminal escapes) if possible.
@@ -212,11 +213,6 @@ END
212
213
  @options[:output] = StringIO.new
213
214
  end
214
215
 
215
- opts.on('-t', '--style NAME',
216
- 'Output style. Can be indented (default) or ugly.') do |name|
217
- @options[:for_engine][:ugly] = true if name.to_sym == :ugly
218
- end
219
-
220
216
  opts.on('-f', '--format NAME',
221
217
  'Output format. Can be html5 (default), xhtml, or html4.') do |name|
222
218
  @options[:for_engine][:format] = name.to_sym
@@ -237,6 +233,11 @@ END
237
233
  @options[:for_engine][:attr_wrapper] = '"'
238
234
  end
239
235
 
236
+ opts.on('--remove-whitespace',
237
+ 'Remove whitespace surrounding and within tags') do
238
+ @options[:for_engine][:remove_whitespace] = true
239
+ end
240
+
240
241
  opts.on('--cdata',
241
242
  'Always add CDATA sections to javascript and css blocks.') do
242
243
  @options[:for_engine][:cdata] = true
@@ -260,15 +261,13 @@ END
260
261
  @options[:load_paths] << path
261
262
  end
262
263
 
263
- unless RUBY_VERSION < "1.9"
264
- opts.on('-E ex[:in]', 'Specify the default external and internal character encodings.') do |encoding|
265
- external, internal = encoding.split(':')
266
- Encoding.default_external = external if external && !external.empty?
267
- Encoding.default_internal = internal if internal && !internal.empty?
268
- end
264
+ opts.on('-E ex[:in]', 'Specify the default external and internal character encodings.') do |encoding|
265
+ external, internal = encoding.split(':')
266
+ Encoding.default_external = external if external && !external.empty?
267
+ Encoding.default_internal = internal if internal && !internal.empty?
269
268
  end
270
269
 
271
- opts.on('-d', '--debug', "Print out the precompiled Ruby source.") do
270
+ opts.on('-d', '--debug', "Print out the precompiled Ruby source, and show syntax errors in the Ruby code.") do
272
271
  @options[:debug] = true
273
272
  end
274
273
 
@@ -294,20 +293,33 @@ END
294
293
 
295
294
  begin
296
295
 
297
- engine = ::Haml::Engine.new(template, @options[:for_engine])
298
- if @options[:check_syntax]
299
- puts "Syntax OK"
296
+ if @options[:parse]
297
+ parser = ::Haml::Parser.new(::Haml::Options.new(@options))
298
+ pp parser.call(template)
300
299
  return
301
300
  end
302
301
 
303
- if @options[:parse]
304
- pp engine.parser.root
302
+ engine = ::Haml::Engine.new(template, @options[:for_engine])
303
+
304
+ if @options[:check_syntax]
305
+ error = validate_ruby(engine.precompiled)
306
+ if error
307
+ puts error.message.split("\n").first
308
+ exit 1
309
+ end
310
+ puts "Syntax OK"
305
311
  return
306
312
  end
307
313
 
308
314
  if @options[:debug]
309
315
  puts engine.precompiled
310
- puts '=' * 100
316
+ error = validate_ruby(engine.precompiled)
317
+ if error
318
+ puts '=' * 100
319
+ puts error.message.split("\n")[0]
320
+ exit 1
321
+ end
322
+ return
311
323
  end
312
324
 
313
325
  result = engine.to_html
@@ -324,6 +336,12 @@ END
324
336
  output.write(result)
325
337
  output.close() if output.is_a? File
326
338
  end
339
+
340
+ def validate_ruby(code)
341
+ eval("BEGIN {return nil}; #{code}", binding, @options[:filename] || "")
342
+ rescue ::SyntaxError # Not to be confused with Haml::SyntaxError
343
+ $!
344
+ end
327
345
  end
328
346
  end
329
347
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "tilt"
2
4
 
3
5
  module Haml
@@ -59,7 +61,7 @@ module Haml
59
61
  end
60
62
 
61
63
  # Removes a filter from Haml. If the filter was removed, it returns
62
- # the that was remove Module upon success, or nil on failure. If you try
64
+ # the Module that was removed upon success, or nil on failure. If you try
63
65
  # to redefine a filter, Haml will raise an error. Use this method first to
64
66
  # explicitly remove the filter before redefining it.
65
67
  # @return Module The filter module that has been removed
@@ -118,7 +120,7 @@ module Haml
118
120
  # @param text [String] The source text for the filter to process
119
121
  # @return [String] The filtered result
120
122
  # @raise [Haml::Error] if it's not overridden
121
- def render(text)
123
+ def render(_text)
122
124
  raise Error.new("#{self.inspect}#render not defined!")
123
125
  end
124
126
 
@@ -129,7 +131,7 @@ module Haml
129
131
  # @param text [String] The source text for the filter to process
130
132
  # @return [String] The filtered result
131
133
  # @raise [Haml::Error] if it or \{#render} isn't overridden
132
- def render_with_options(text, options)
134
+ def render_with_options(text, _options)
133
135
  render(text)
134
136
  end
135
137
 
@@ -163,10 +165,14 @@ module Haml
163
165
  if contains_interpolation?(text)
164
166
  return if options[:suppress_eval]
165
167
 
166
- text = unescape_interpolation(text).gsub(/(\\+)n/) do |s|
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|
167
173
  escapes = $1.size
168
174
  next s if escapes % 2 == 0
169
- ("\\" * (escapes - 1)) + "\n"
175
+ "#{'\\' * (escapes - 1)}\n"
170
176
  end
171
177
  # We need to add a newline at the beginning to get the
172
178
  # filter lines to line up (since the Haml filter contains
@@ -174,20 +180,15 @@ module Haml
174
180
  # filter name). Then we need to escape the trailing
175
181
  # newline so that the whole filter block doesn't take up
176
182
  # too many.
177
- text = "\n" + text.sub(/\n"\Z/, "\\n\"")
183
+ text = %[\n#{text.sub(/\n"\Z/, "\\n\"")}]
178
184
  push_script <<RUBY.rstrip, :escape_html => false
179
185
  find_and_preserve(#{filter.inspect}.render_with_options(#{text}, _hamlout.options))
180
186
  RUBY
181
187
  return
182
188
  end
183
189
 
184
- rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text, compiler.options), compiler.options[:preserve])
185
-
186
- if options[:ugly]
187
- push_text(rendered.rstrip)
188
- else
189
- push_text(rendered.rstrip.gsub("\n", "\n#{' ' * @output_tabs}"))
190
- end
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")
191
192
  end
192
193
  end
193
194
  end
@@ -216,13 +217,10 @@ RUBY
216
217
  type = " type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}"
217
218
  end
218
219
 
219
- str = "<script#{type}>\n"
220
- str << " //<![CDATA[\n" if options[:cdata]
221
- str << "#{indent}#{text.rstrip.gsub("\n", "\n#{indent}")}\n"
222
- str << " //]]>\n" if options[:cdata]
223
- str << "</script>"
220
+ text = text.rstrip
221
+ text.gsub!("\n", "\n#{indent}")
224
222
 
225
- str
223
+ %!<script#{type}>\n#{" //<![CDATA[\n" if options[:cdata]}#{indent}#{text}\n#{" //]]>\n" if options[:cdata]}</script>!
226
224
  end
227
225
  end
228
226
 
@@ -240,13 +238,10 @@ RUBY
240
238
  type = " type=#{options[:attr_wrapper]}text/css#{options[:attr_wrapper]}"
241
239
  end
242
240
 
243
- str = "<style#{type}>\n"
244
- str << " /*<![CDATA[*/\n" if options[:cdata]
245
- str << "#{indent}#{text.rstrip.gsub("\n", "\n#{indent}")}\n"
246
- str << " /*]]>*/\n" if options[:cdata]
247
- str << "</style>"
241
+ text = text.rstrip
242
+ text.gsub!("\n", "\n#{indent}")
248
243
 
249
- str
244
+ %(<style#{type}>\n#{" /*<![CDATA[*/\n" if options[:cdata]}#{indent}#{text}\n#{" /*]]>*/\n" if options[:cdata]}</style>)
250
245
  end
251
246
  end
252
247
 
@@ -256,7 +251,7 @@ RUBY
256
251
 
257
252
  # @see Base#render
258
253
  def render(text)
259
- "<![CDATA[#{("\n" + text).rstrip.gsub("\n", "\n ")}\n]]>"
254
+ "<![CDATA[#{"\n#{text.rstrip}".gsub("\n", "\n ")}\n]]>"
260
255
  end
261
256
  end
262
257
 
@@ -288,7 +283,7 @@ RUBY
288
283
  def compile(compiler, text)
289
284
  return if compiler.options[:suppress_eval]
290
285
  compiler.instance_eval do
291
- push_silent <<-FIRST.gsub("\n", ';') + text + <<-LAST.gsub("\n", ';')
286
+ push_silent "#{<<-FIRST.tr("\n", ';')}#{text}#{<<-LAST.tr("\n", ';')}"
292
287
  begin
293
288
  haml_io = StringIO.new(_hamlout.buffer, 'a')
294
289
  FIRST