haml 3.0.0.beta.3 → 3.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (82) hide show
  1. data/.yardopts +2 -0
  2. data/REMEMBER +4 -11
  3. data/Rakefile +24 -2
  4. data/VERSION +1 -1
  5. data/lib/haml.rb +5 -2
  6. data/lib/haml/exec.rb +11 -4
  7. data/lib/haml/filters.rb +3 -0
  8. data/lib/haml/helpers.rb +2 -10
  9. data/lib/haml/helpers/action_view_extensions.rb +4 -2
  10. data/lib/haml/helpers/action_view_mods.rb +6 -4
  11. data/lib/haml/html.rb +0 -1
  12. data/lib/haml/precompiler.rb +37 -30
  13. data/lib/haml/railtie.rb +6 -2
  14. data/lib/haml/root.rb +4 -0
  15. data/lib/haml/template.rb +2 -0
  16. data/lib/haml/util.rb +74 -0
  17. data/lib/haml/util/subset_map.rb +101 -0
  18. data/lib/sass.rb +1 -0
  19. data/lib/sass/engine.rb +36 -31
  20. data/lib/sass/files.rb +1 -1
  21. data/lib/sass/plugin.rb +21 -0
  22. data/lib/sass/plugin/staleness_checker.rb +9 -9
  23. data/lib/sass/script.rb +1 -2
  24. data/lib/sass/script/color.rb +4 -3
  25. data/lib/sass/script/css_lexer.rb +11 -1
  26. data/lib/sass/script/css_parser.rb +4 -1
  27. data/lib/sass/script/funcall.rb +9 -0
  28. data/lib/sass/script/interpolation.rb +21 -0
  29. data/lib/sass/script/lexer.rb +30 -13
  30. data/lib/sass/script/node.rb +1 -1
  31. data/lib/sass/script/number.rb +4 -5
  32. data/lib/sass/script/parser.rb +13 -14
  33. data/lib/sass/script/string.rb +8 -2
  34. data/lib/sass/script/string_interpolation.rb +27 -4
  35. data/lib/sass/scss.rb +3 -0
  36. data/lib/sass/scss/css_parser.rb +5 -3
  37. data/lib/sass/scss/parser.rb +146 -64
  38. data/lib/sass/scss/rx.rb +9 -1
  39. data/lib/sass/scss/sass_parser.rb +11 -0
  40. data/lib/sass/scss/script_lexer.rb +2 -0
  41. data/lib/sass/scss/static_parser.rb +48 -0
  42. data/lib/sass/selector.rb +353 -0
  43. data/lib/sass/selector/abstract_sequence.rb +40 -0
  44. data/lib/sass/selector/comma_sequence.rb +80 -0
  45. data/lib/sass/selector/sequence.rb +194 -0
  46. data/lib/sass/selector/simple.rb +107 -0
  47. data/lib/sass/selector/simple_sequence.rb +161 -0
  48. data/lib/sass/tree/comment_node.rb +1 -0
  49. data/lib/sass/tree/debug_node.rb +1 -0
  50. data/lib/sass/tree/directive_node.rb +1 -0
  51. data/lib/sass/tree/extend_node.rb +60 -0
  52. data/lib/sass/tree/for_node.rb +1 -0
  53. data/lib/sass/tree/if_node.rb +2 -0
  54. data/lib/sass/tree/import_node.rb +2 -0
  55. data/lib/sass/tree/mixin_def_node.rb +1 -0
  56. data/lib/sass/tree/mixin_node.rb +21 -5
  57. data/lib/sass/tree/node.rb +59 -12
  58. data/lib/sass/tree/prop_node.rb +20 -21
  59. data/lib/sass/tree/root_node.rb +8 -17
  60. data/lib/sass/tree/rule_node.rb +49 -100
  61. data/lib/sass/tree/variable_node.rb +1 -0
  62. data/lib/sass/tree/warn_node.rb +1 -0
  63. data/lib/sass/tree/while_node.rb +1 -0
  64. data/test/haml/engine_test.rb +185 -3
  65. data/test/haml/helper_test.rb +25 -2
  66. data/test/haml/template_test.rb +2 -2
  67. data/test/haml/templates/helpers.haml +13 -0
  68. data/test/haml/util/subset_map_test.rb +91 -0
  69. data/test/haml/util_test.rb +25 -0
  70. data/test/sass/conversion_test.rb +23 -3
  71. data/test/sass/engine_test.rb +50 -7
  72. data/test/sass/extend_test.rb +1045 -0
  73. data/test/sass/results/complex.css +0 -1
  74. data/test/sass/results/script.css +1 -1
  75. data/test/sass/script_conversion_test.rb +16 -0
  76. data/test/sass/script_test.rb +37 -4
  77. data/test/sass/scss/css_test.rb +17 -3
  78. data/test/sass/scss/rx_test.rb +1 -1
  79. data/test/sass/scss/scss_test.rb +30 -0
  80. data/test/sass/templates/complex.sass +0 -2
  81. data/test/test_helper.rb +5 -0
  82. metadata +18 -4
@@ -65,7 +65,8 @@ module Sass
65
65
  NAME = /#{NMCHAR}+/
66
66
  NUM = /[0-9]+|[0-9]*.[0-9]+/
67
67
  STRING = /#{STRING1}|#{STRING2}/
68
- URL = /((?:[!#%$&*-~]|#{NONASCII}|#{ESCAPE})*)/
68
+ URLCHAR = /[#%&*-~]|#{NONASCII}|#{ESCAPE}/
69
+ URL = /(#{URLCHAR}*)/
69
70
  W = /[ \t\r\n\f]*/
70
71
 
71
72
  # This is more liberal than the spec's definition,
@@ -108,6 +109,13 @@ module Sass
108
109
  # Custom
109
110
  HEXCOLOR = /\#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?/
110
111
  INTERP_START = /#\{/
112
+
113
+ STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\"/
114
+ STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\'/
115
+ STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
116
+ STATIC_VALUE = /(#{NMCHAR}|#{STRING1_NOINTERP}|\s(?!%)|#[a-f0-9]|[,%]|\.[0-9]|\!important)+(?=[;}])/i
117
+
118
+ STATIC_SELECTOR = /(#{NMCHAR}|\s|[,>+*]|[:#.]#{NMSTART})+(?=[{])/i
111
119
  end
112
120
  end
113
121
  end
@@ -0,0 +1,11 @@
1
+ module Sass
2
+ module SCSS
3
+ # A subclass of {Parser} that parses code in Sass documents
4
+ # using some SCSS constructs.
5
+ # This is necessary because SassScript in Sass supports `!`-style variables,
6
+ # whereas in SCSS it doesn't.
7
+ class SassParser < Parser
8
+ @sass_script_parser = Sass::Script::Parser
9
+ end
10
+ end
11
+ end
@@ -4,6 +4,8 @@ module Sass
4
4
  # that makes them usable by {SCSS::Parser} to parse SassScript.
5
5
  # In particular, the lexer doesn't support `!` for a variable prefix.
6
6
  module ScriptLexer
7
+ private
8
+
7
9
  def variable
8
10
  return [:raw, "!important"] if scan(Sass::SCSS::RX::IMPORTANT)
9
11
  _variable(/(\$)(#{Sass::SCSS::RX::IDENT})/)
@@ -0,0 +1,48 @@
1
+ module Sass
2
+ module SCSS
3
+ # A parser for a static SCSS tree.
4
+ # Parses with SCSS extensions, like nested rules and parent selectors,
5
+ # but without dynamic SassScript.
6
+ # This is useful for e.g. \{#parse\_selector parsing selectors}
7
+ # after resolving the interpolation.
8
+ class StaticParser < Parser
9
+ # Parses the text as a selector.
10
+ #
11
+ # @param line [Fixnum] The line on which the selector appears.
12
+ # Used for error reporting
13
+ # @param filename [String, nil] The file in which the selector appears,
14
+ # or nil if there is no such file.
15
+ # Used for error reporting
16
+ # @return [Selector::CommaSequence] The parsed selector
17
+ # @raise [Sass::SyntaxError] if there's a syntax error in the selector
18
+ def parse_selector(line, filename)
19
+ init_scanner!
20
+ selectors = [expr!(:_selector)]
21
+ while tok(/,/)
22
+ ws = str{ss}
23
+ selectors << expr!(:_selector)
24
+ selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
25
+ end
26
+ expected("selector") unless @scanner.eos?
27
+ seq = Selector::CommaSequence.new(selectors)
28
+ seq.line = line
29
+ seq.filename = filename
30
+ seq
31
+ end
32
+
33
+ private
34
+
35
+ def variable; nil; end
36
+ def script_value; nil; end
37
+ def interpolation; nil; end
38
+ def interp_string; s = tok(STRING) and [s]; end
39
+ def interp_ident(ident = IDENT); s = tok(ident) and [s]; end
40
+ def use_css_import?; true; end
41
+
42
+ def special_directive(name)
43
+ return unless name == 'media' || name == 'import'
44
+ super
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,353 @@
1
+ require 'sass/selector/simple'
2
+ require 'sass/selector/abstract_sequence'
3
+ require 'sass/selector/comma_sequence'
4
+ require 'sass/selector/sequence'
5
+ require 'sass/selector/simple_sequence'
6
+
7
+ module Sass
8
+ # A namespace for nodes in the parse tree for selectors.
9
+ #
10
+ # {CommaSequence} is the toplevel seelctor,
11
+ # representing a comma-separated sequence of {Sequence}s,
12
+ # such as `foo bar, baz bang`.
13
+ # {Sequence} is the next level,
14
+ # representing {SimpleSequence}s separated by combinators (e.g. descendant or child),
15
+ # such as `foo bar` or `foo > bar baz`.
16
+ # {SimpleSequence} is a sequence of selectors that all apply to a single element,
17
+ # such as `foo.bar[attr=val]`.
18
+ # Finally, {Simple} is the superclass of the simplest selectors,
19
+ # such as `.foo` or `#bar`.
20
+ module Selector
21
+ # A parent-referencing selector (`&` in Sass).
22
+ # The function of this is to be replaced by the parent selector
23
+ # in the nested hierarchy.
24
+ class Parent < Simple
25
+ # @see Selector#to_a
26
+ def to_a
27
+ ["&"]
28
+ end
29
+
30
+ # Always raises an exception.
31
+ #
32
+ # @raise [Sass::SyntaxError] Parent selectors should be resolved before unification
33
+ # @see Selector#unify
34
+ def unify(sels)
35
+ raise Sass::SyntaxError.new("[BUG] Cannot unify parent selectors.")
36
+ end
37
+ end
38
+
39
+ # A class selector (e.g. `.foo`).
40
+ class Class < Simple
41
+ # The class name.
42
+ #
43
+ # @return [Array<String, Sass::Script::Node>]
44
+ attr_reader :name
45
+
46
+ # @param name [Array<String, Sass::Script::Node>] The class name
47
+ def initialize(name)
48
+ @name = name
49
+ end
50
+
51
+ # @see Selector#to_a
52
+ def to_a
53
+ [".", *@name]
54
+ end
55
+ end
56
+
57
+ # An id selector (e.g. `#foo`).
58
+ class Id < Simple
59
+ # The id name.
60
+ #
61
+ # @return [Array<String, Sass::Script::Node>]
62
+ attr_reader :name
63
+
64
+ # @param name [Array<String, Sass::Script::Node>] The id name
65
+ def initialize(name)
66
+ @name = name
67
+ end
68
+
69
+ # @see Selector#to_a
70
+ def to_a
71
+ ["#", *@name]
72
+ end
73
+
74
+ # Returns `nil` if `sels` contains an {Id} selector
75
+ # with a different name than this one.
76
+ #
77
+ # @see Selector#unify
78
+ def unify(sels)
79
+ return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
80
+ super
81
+ end
82
+ end
83
+
84
+ # A universal selector (`*` in CSS).
85
+ class Universal < Simple
86
+ # The selector namespace.
87
+ # `nil` means the default namespace,
88
+ # `[""]` means no namespace,
89
+ # `["*"]` means any namespace.
90
+ #
91
+ # @return [Array<String, Sass::Script::Node>, nil]
92
+ attr_reader :namespace
93
+
94
+ # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
95
+ def initialize(namespace)
96
+ @namespace = namespace
97
+ end
98
+
99
+ # @see Selector#to_a
100
+ def to_a
101
+ @namespace ? @namespace + ["|*"] : ["*"]
102
+ end
103
+
104
+ # Unification of a universal selector is somewhat complicated,
105
+ # especially when a namespace is specified.
106
+ # If there is no namespace specified
107
+ # or any namespace is specified (namespace `"*"`),
108
+ # then `sel` is returned without change
109
+ # (unless it's empty, in which case `"*"` is required).
110
+ #
111
+ # If a namespace is specified
112
+ # but `sel` does not specify a namespace,
113
+ # then the given namespace is applied to `sel`,
114
+ # either by adding this {Universal} selector
115
+ # or applying this namespace to an existing {Element} selector.
116
+ #
117
+ # If both this selector *and* `sel` specify namespaces,
118
+ # those namespaces are unified via {Simple#unify_namespaces}
119
+ # and the unified namespace is used, if possible.
120
+ #
121
+ # @todo There are lots of cases that this documentation specifies;
122
+ # make sure we thoroughly test **all of them**.
123
+ # @todo Keep track of whether a default namespace has been declared
124
+ # and handle namespace-unspecified selectors accordingly.
125
+ # @todo If any branch of a CommaSequence ends up being just `"*"`,
126
+ # then all other branches should be eliminated
127
+ #
128
+ # @see Selector#unify
129
+ def unify(sels)
130
+ name =
131
+ case sels.first
132
+ when Universal; :universal
133
+ when Element; sels.first.name
134
+ else
135
+ return [self] + sels unless namespace.nil? || namespace == ['*']
136
+ return sels unless sels.empty?
137
+ return [self]
138
+ end
139
+
140
+ ns, accept = unify_namespaces(namespace, sels.first.namespace)
141
+ return unless accept
142
+ [name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
143
+ end
144
+ end
145
+
146
+ # An element selector (e.g. `h1`).
147
+ class Element < Simple
148
+ # The element name.
149
+ #
150
+ # @return [Array<String, Sass::Script::Node>]
151
+ attr_reader :name
152
+
153
+ # The selector namespace.
154
+ # `nil` means the default namespace,
155
+ # `[""]` means no namespace,
156
+ # `["*"]` means any namespace.
157
+ #
158
+ # @return [Array<String, Sass::Script::Node>, nil]
159
+ attr_reader :namespace
160
+
161
+ # @param name [Array<String, Sass::Script::Node>] The element name
162
+ # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
163
+ def initialize(name, namespace)
164
+ @name = name
165
+ @namespace = namespace
166
+ end
167
+
168
+ # @see Selector#to_a
169
+ def to_a
170
+ @namespace ? @namespace + ["|"] + @name : @name
171
+ end
172
+
173
+ # Unification of an element selector is somewhat complicated,
174
+ # especially when a namespace is specified.
175
+ # First, if `sel` contains another {Element} with a different \{#name},
176
+ # then the selectors can't be unified and `nil` is returned.
177
+ #
178
+ # Otherwise, if `sel` doesn't specify a namespace,
179
+ # or it specifies any namespace (via `"*"`),
180
+ # then it's returned with this element selector
181
+ # (e.g. `.foo` becomes `a.foo` or `svg|a.foo`).
182
+ # Similarly, if this selector doesn't specify a namespace,
183
+ # the namespace from `sel` is used.
184
+ #
185
+ # If both this selector *and* `sel` specify namespaces,
186
+ # those namespaces are unified via {Simple#unify_namespaces}
187
+ # and the unified namespace is used, if possible.
188
+ #
189
+ # @todo There are lots of cases that this documentation specifies;
190
+ # make sure we thoroughly test **all of them**.
191
+ # @todo Keep track of whether a default namespace has been declared
192
+ # and handle namespace-unspecified selectors accordingly.
193
+ #
194
+ # @see Selector#unify
195
+ def unify(sels)
196
+ case sels.first
197
+ when Universal;
198
+ when Element; return unless name == sels.first.name
199
+ else return [self] + sels
200
+ end
201
+
202
+ ns, accept = unify_namespaces(namespace, sels.first.namespace)
203
+ return unless accept
204
+ [Element.new(name, ns)] + sels[1..-1]
205
+ end
206
+ end
207
+
208
+ # Selector interpolation (`#{}` in Sass).
209
+ class Interpolation < Simple
210
+ # The script to run.
211
+ #
212
+ # @return [Sass::Script::Node]
213
+ attr_reader :script
214
+
215
+ # @param script [Sass::Script::Node] The script to run
216
+ def initialize(script)
217
+ @script = script
218
+ end
219
+
220
+ # @see Selector#to_a
221
+ def to_a
222
+ [@script]
223
+ end
224
+
225
+ # Always raises an exception.
226
+ #
227
+ # @raise [Sass::SyntaxError] Interpolation selectors should be resolved before unification
228
+ # @see Selector#unify
229
+ def unify(sels)
230
+ raise Sass::SyntaxError.new("[BUG] Cannot unify interpolation selectors.")
231
+ end
232
+ end
233
+
234
+ # An attribute selector (e.g. `[href^="http://"]`).
235
+ class Attribute < Simple
236
+ # The attribute name.
237
+ #
238
+ # @return [Array<String, Sass::Script::Node>]
239
+ attr_reader :name
240
+
241
+ # The attribute namespace.
242
+ # `nil` means the default namespace,
243
+ # `[""]` means no namespace,
244
+ # `["*"]` means any namespace.
245
+ #
246
+ # @return [Array<String, Sass::Script::Node>, nil]
247
+ attr_reader :namespace
248
+
249
+ # The matching operator, e.g. `"="` or `"^="`.
250
+ #
251
+ # @return [String]
252
+ attr_reader :operator
253
+
254
+ # The right-hand side of the operator.
255
+ #
256
+ # @return [Array<String, Sass::Script::Node>]
257
+ attr_reader :value
258
+
259
+ # @param name [Array<String, Sass::Script::Node>] The attribute name
260
+ # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
261
+ # @param operator [String] The matching operator, e.g. `"="` or `"^="`
262
+ # @param value [Array<String, Sass::Script::Node>] See \{#value}
263
+ def initialize(name, namespace, operator, value)
264
+ @name = name
265
+ @namespace = namespace
266
+ @operator = operator
267
+ @value = value
268
+ end
269
+
270
+ # @see Selector#to_a
271
+ def to_a
272
+ res = ["["]
273
+ res.concat(@namespace) << "|" if @namespace
274
+ res.concat @name
275
+ (res << @operator).concat @value if @value
276
+ res << "]"
277
+ end
278
+ end
279
+
280
+ # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
281
+ # It can have arguments (e.g. `:nth-child(2n+1)`).
282
+ class Pseudo < Simple
283
+ # The type of the selector.
284
+ # `:class` if this is a pseudoclass selector,
285
+ # `:element` if it's a pseudoelement.
286
+ #
287
+ # @return [Symbol]
288
+ attr_reader :type
289
+
290
+ # The name of the selector.
291
+ #
292
+ # @return [Array<String, Sass::Script::Node>]
293
+ attr_reader :name
294
+
295
+ # The argument to the selector,
296
+ # or `nil` if no argument was given.
297
+ #
298
+ # This may include SassScript nodes that will be run during resolution.
299
+ # Note that this should not include SassScript nodes
300
+ # after resolution has taken place.
301
+ #
302
+ # @return [Array<String, Sass::Script::Node>, nil]
303
+ attr_reader :arg
304
+
305
+ # @param type [Symbol] See \{#type}
306
+ # @param name [Array<String, Sass::Script::Node>] The name of the selector
307
+ # @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
308
+ # or nil if no argument was given
309
+ def initialize(type, name, arg)
310
+ @type = type
311
+ @name = name
312
+ @arg = arg
313
+ end
314
+
315
+ # @see Selector#to_a
316
+ def to_a
317
+ res = [@type == :class ? ":" : "::"] + @name
318
+ (res << "(").concat(Haml::Util.strip_string_array(@arg)) << ")" if @arg
319
+ res
320
+ end
321
+
322
+ # Returns `nil` if this is a pseudoclass selector
323
+ # and `sels` contains a pseudoclass selector different than this one.
324
+ #
325
+ # @see Selector#unify
326
+ def unify(sels)
327
+ return if type == :element && sels.any? do |sel|
328
+ sel.is_a?(Pseudo) && sel.type == :element &&
329
+ (sel.name != self.name || sel.arg != self.arg)
330
+ end
331
+ super
332
+ end
333
+ end
334
+
335
+ # A negation pseudoclass selector (e.g. `:not(.foo)`).
336
+ class Negation < Simple
337
+ # The selector to negate.
338
+ #
339
+ # @return [Selector]
340
+ attr_reader :selector
341
+
342
+ # @param [Selector] The selector to negate
343
+ def initialize(selector)
344
+ @selector = selector
345
+ end
346
+
347
+ # @see Selector#to_a
348
+ def to_a
349
+ [":not("] + @selector.to_a + [")"]
350
+ end
351
+ end
352
+ end
353
+ end
@@ -0,0 +1,40 @@
1
+ module Sass
2
+ module Selector
3
+ # The abstract parent class of the various selector sequence classes.
4
+ #
5
+ # All subclasses should implement a `members` method
6
+ # that returns an array of object that respond to `#line=` and `#filename=`.
7
+ class AbstractSequence
8
+ # The line of the Sass template on which this selector was declared.
9
+ #
10
+ # @return [Fixnum]
11
+ attr_reader :line
12
+
13
+ # The name of the file in which this selector was declared.
14
+ #
15
+ # @return [String, nil]
16
+ attr_reader :filename
17
+
18
+ # Sets the line of the Sass template on which this selector was declared.
19
+ # This also sets the line for all child selectors.
20
+ #
21
+ # @param line [Fixnum]
22
+ # @return [Fixnum]
23
+ def line=(line)
24
+ members.each {|m| m.line = line}
25
+ @line = line
26
+ end
27
+
28
+ # Sets the name of the file in which this selector was declared,
29
+ # or `nil` if it was not declared in a file (e.g. on stdin).
30
+ # This also sets the filename for all child selectors.
31
+ #
32
+ # @param filename [String, nil]
33
+ # @return [String, nil]
34
+ def filename=(filename)
35
+ members.each {|m| m.filename = filename}
36
+ @filename = filename
37
+ end
38
+ end
39
+ end
40
+ end