haml-edge 2.3.209 → 2.3.210
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.
- data/.yardopts +2 -0
- data/EDGE_GEM_VERSION +1 -1
- data/Rakefile +24 -2
- data/VERSION +1 -1
- data/lib/haml/exec.rb +11 -4
- data/lib/haml/filters.rb +3 -0
- data/lib/haml/helpers/action_view_extensions.rb +4 -2
- data/lib/haml/helpers/action_view_mods.rb +6 -4
- data/lib/haml/helpers.rb +2 -10
- data/lib/haml/html.rb +0 -1
- data/lib/haml/precompiler.rb +37 -30
- data/lib/haml/railtie.rb +6 -2
- data/lib/haml/root.rb +4 -0
- data/lib/haml/template.rb +2 -0
- data/lib/haml/util/subset_map.rb +101 -0
- data/lib/haml/util.rb +74 -0
- data/lib/haml.rb +5 -2
- data/lib/sass/engine.rb +36 -31
- data/lib/sass/files.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +9 -9
- data/lib/sass/plugin.rb +21 -0
- data/lib/sass/script/color.rb +4 -3
- data/lib/sass/script/css_lexer.rb +11 -1
- data/lib/sass/script/css_parser.rb +4 -1
- data/lib/sass/script/funcall.rb +9 -0
- data/lib/sass/script/interpolation.rb +21 -0
- data/lib/sass/script/lexer.rb +30 -13
- data/lib/sass/script/node.rb +1 -1
- data/lib/sass/script/number.rb +4 -5
- data/lib/sass/script/parser.rb +13 -14
- data/lib/sass/script/string.rb +8 -2
- data/lib/sass/script/string_interpolation.rb +27 -4
- data/lib/sass/script.rb +1 -2
- data/lib/sass/scss/css_parser.rb +5 -3
- data/lib/sass/scss/parser.rb +146 -64
- data/lib/sass/scss/rx.rb +9 -1
- data/lib/sass/scss/sass_parser.rb +11 -0
- data/lib/sass/scss/script_lexer.rb +2 -0
- data/lib/sass/scss/static_parser.rb +48 -0
- data/lib/sass/scss.rb +3 -0
- data/lib/sass/selector/abstract_sequence.rb +40 -0
- data/lib/sass/selector/comma_sequence.rb +80 -0
- data/lib/sass/selector/sequence.rb +194 -0
- data/lib/sass/selector/simple.rb +107 -0
- data/lib/sass/selector/simple_sequence.rb +161 -0
- data/lib/sass/selector.rb +353 -0
- data/lib/sass/tree/comment_node.rb +1 -0
- data/lib/sass/tree/debug_node.rb +1 -0
- data/lib/sass/tree/directive_node.rb +1 -0
- data/lib/sass/tree/extend_node.rb +60 -0
- data/lib/sass/tree/for_node.rb +1 -0
- data/lib/sass/tree/if_node.rb +2 -0
- data/lib/sass/tree/import_node.rb +2 -0
- data/lib/sass/tree/mixin_def_node.rb +1 -0
- data/lib/sass/tree/mixin_node.rb +21 -5
- data/lib/sass/tree/node.rb +59 -12
- data/lib/sass/tree/prop_node.rb +20 -21
- data/lib/sass/tree/root_node.rb +8 -17
- data/lib/sass/tree/rule_node.rb +49 -100
- data/lib/sass/tree/variable_node.rb +1 -0
- data/lib/sass/tree/warn_node.rb +1 -0
- data/lib/sass/tree/while_node.rb +1 -0
- data/lib/sass.rb +1 -0
- data/test/haml/engine_test.rb +185 -3
- data/test/haml/helper_test.rb +25 -2
- data/test/haml/template_test.rb +2 -2
- data/test/haml/templates/helpers.haml +13 -0
- data/test/haml/util/subset_map_test.rb +91 -0
- data/test/haml/util_test.rb +25 -0
- data/test/sass/conversion_test.rb +23 -3
- data/test/sass/engine_test.rb +50 -7
- data/test/sass/extend_test.rb +1045 -0
- data/test/sass/results/complex.css +0 -1
- data/test/sass/results/script.css +1 -1
- data/test/sass/script_conversion_test.rb +16 -0
- data/test/sass/script_test.rb +37 -4
- data/test/sass/scss/css_test.rb +17 -3
- data/test/sass/scss/rx_test.rb +1 -1
- data/test/sass/scss/scss_test.rb +30 -0
- data/test/sass/templates/complex.sass +0 -2
- data/test/test_helper.rb +5 -0
- metadata +17 -3
@@ -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
|
data/lib/sass/tree/debug_node.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'sass/tree/node'
|
2
|
+
|
3
|
+
module Sass::Tree
|
4
|
+
# A static node reprenting an `@extend` directive.
|
5
|
+
#
|
6
|
+
# @see Sass::Tree
|
7
|
+
class ExtendNode < Node
|
8
|
+
# @param selector [Array<String, Sass::Script::Node>]
|
9
|
+
# The CSS selector to extend,
|
10
|
+
# interspersed with {Sass::Script::Node}s
|
11
|
+
# representing `#{}`-interpolation.
|
12
|
+
def initialize(selector)
|
13
|
+
@selector = selector
|
14
|
+
super()
|
15
|
+
end
|
16
|
+
|
17
|
+
# Registers this extension in the `extends` subset map.
|
18
|
+
#
|
19
|
+
# @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
|
20
|
+
# The extensions defined for this tree
|
21
|
+
# @param parent [RuleNode] The parent node of this node
|
22
|
+
# @see Node#cssize
|
23
|
+
def cssize(extends, parent)
|
24
|
+
@resolved_selector.members.each do |seq|
|
25
|
+
if seq.members.size > 1
|
26
|
+
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
|
27
|
+
end
|
28
|
+
|
29
|
+
sseq = seq.members.first
|
30
|
+
if !sseq.is_a?(Sass::Selector::SimpleSequence)
|
31
|
+
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
|
32
|
+
end
|
33
|
+
|
34
|
+
sel = sseq.members
|
35
|
+
parent.resolved_rules.members.each do |seq|
|
36
|
+
if !seq.members.last.is_a?(Sass::Selector::SimpleSequence)
|
37
|
+
raise Sass::SyntaxError.new("#{seq} can't extend: invalid selector")
|
38
|
+
end
|
39
|
+
|
40
|
+
extends[sel] = seq
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
[]
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
# Runs SassScript interpolation in the selector,
|
50
|
+
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
51
|
+
#
|
52
|
+
# @param environment [Sass::Environment] The lexical environment containing
|
53
|
+
# variable and mixin values
|
54
|
+
def perform!(environment)
|
55
|
+
@resolved_selector = Sass::SCSS::CssParser.new(run_interp(@selector, environment)).
|
56
|
+
parse_selector(self.line, self.filename)
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/sass/tree/for_node.rb
CHANGED
data/lib/sass/tree/if_node.rb
CHANGED
@@ -30,6 +30,7 @@ module Sass::Tree
|
|
30
30
|
@last_else = node
|
31
31
|
end
|
32
32
|
|
33
|
+
# @see Node#options=
|
33
34
|
def options=(options)
|
34
35
|
super
|
35
36
|
self.else.options = options if self.else
|
@@ -37,6 +38,7 @@ module Sass::Tree
|
|
37
38
|
|
38
39
|
protected
|
39
40
|
|
41
|
+
# @see Node#to_src
|
40
42
|
def to_src(tabs, opts, fmt, is_else = false)
|
41
43
|
name =
|
42
44
|
if !is_else; "if"
|
@@ -28,10 +28,12 @@ module Sass
|
|
28
28
|
@full_filename ||= import
|
29
29
|
end
|
30
30
|
|
31
|
+
# @see Node#to_sass
|
31
32
|
def to_sass(tabs = 0, opts = {})
|
32
33
|
"#{' ' * tabs}@import #{@imported_filename}\n"
|
33
34
|
end
|
34
35
|
|
36
|
+
# @see Node#to_scss
|
35
37
|
def to_scss(tabs = 0, opts = {})
|
36
38
|
"#{' ' * tabs}@import \"#{@imported_filename}\";\n"
|
37
39
|
end
|
data/lib/sass/tree/mixin_node.rb
CHANGED
@@ -22,22 +22,38 @@ module Sass::Tree
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# @see Node#cssize
|
25
|
-
def cssize(parent = nil)
|
26
|
-
_cssize(parent) # Pass on the parent even if it's not a MixinNode
|
25
|
+
def cssize(extends, parent = nil)
|
26
|
+
_cssize(extends, parent) # Pass on the parent even if it's not a MixinNode
|
27
27
|
end
|
28
28
|
|
29
29
|
protected
|
30
30
|
|
31
|
+
# Returns an error message if the given child node is invalid,
|
32
|
+
# and false otherwise.
|
33
|
+
#
|
34
|
+
# {ExtendNode}s are valid within {MixinNode}s.
|
35
|
+
#
|
36
|
+
# @param child [Tree::Node] A potential child node
|
37
|
+
# @return [Boolean, String] Whether or not the child node is valid,
|
38
|
+
# as well as the error message to display if it is invalid
|
39
|
+
def invalid_child?(child)
|
40
|
+
super unless child.is_a?(ExtendNode)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @see Node#to_src
|
31
44
|
def to_src(tabs, opts, fmt)
|
32
45
|
args = '(' + @args.map {|a| a.to_sass(opts)}.join(", ") + ')' unless @args.empty?
|
33
46
|
"#{' ' * tabs}#{fmt == :sass ? '+' : '@include '}#{dasherize(@name, opts)}#{args}#{semi fmt}\n"
|
34
47
|
end
|
35
48
|
|
36
49
|
# @see Node#_cssize
|
37
|
-
def _cssize(parent)
|
38
|
-
children.map
|
50
|
+
def _cssize(extends, parent)
|
51
|
+
children.map do |c|
|
52
|
+
parent.check_child! c
|
53
|
+
c.cssize(extends, parent)
|
54
|
+
end.flatten
|
39
55
|
rescue Sass::SyntaxError => e
|
40
|
-
e.modify_backtrace(:mixin => @name, :line => line)
|
56
|
+
e.modify_backtrace(:mixin => @name, :filename => filename, :line => line)
|
41
57
|
e.add_backtrace(:filename => filename, :line => line)
|
42
58
|
raise e
|
43
59
|
end
|
data/lib/sass/tree/node.rb
CHANGED
@@ -89,11 +89,20 @@ module Sass
|
|
89
89
|
# @see #invalid_child?
|
90
90
|
def <<(child)
|
91
91
|
return if child.nil?
|
92
|
+
check_child! child
|
93
|
+
self.has_children = true
|
94
|
+
@children << child
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raises an error if the given child node is invalid.
|
98
|
+
#
|
99
|
+
# @param child [Tree::Node] The child node
|
100
|
+
# @raise [Sass::SyntaxError] if `child` is invalid
|
101
|
+
# @see #invalid_child?
|
102
|
+
def check_child!(child)
|
92
103
|
if msg = invalid_child?(child)
|
93
104
|
raise Sass::SyntaxError.new(msg, :line => child.line)
|
94
105
|
end
|
95
|
-
self.has_children = true
|
96
|
-
@children << child
|
97
106
|
end
|
98
107
|
|
99
108
|
# Compares this node and another object (only other {Tree::Node}s will be equal).
|
@@ -116,7 +125,10 @@ module Sass
|
|
116
125
|
# @see #perform
|
117
126
|
# @see #to_s
|
118
127
|
def render
|
119
|
-
|
128
|
+
extends = Haml::Util::SubsetMap.new
|
129
|
+
result = perform(Environment.new).cssize(extends)
|
130
|
+
result = result.do_extend(extends) unless extends.empty?
|
131
|
+
result.to_s
|
120
132
|
end
|
121
133
|
|
122
134
|
# True if \{#to\_s} will return `nil`;
|
@@ -150,19 +162,42 @@ module Sass
|
|
150
162
|
raise e
|
151
163
|
end
|
152
164
|
|
165
|
+
# Converts a static CSS tree (e.g. the output of \{#cssize})
|
166
|
+
# into another static CSS tree,
|
167
|
+
# with the given extensions applied to all relevant {RuleNode}s.
|
168
|
+
#
|
169
|
+
# @todo Link this to the reference documentation on `@extend`
|
170
|
+
# when such a thing exists.
|
171
|
+
#
|
172
|
+
# @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
|
173
|
+
# The extensions to perform on this tree
|
174
|
+
# @return [Tree::Node] The resulting tree of static CSS nodes.
|
175
|
+
# @raise [Sass::SyntaxError] Only if there's a programmer error
|
176
|
+
# and this is not a static CSS tree
|
177
|
+
def do_extend(extends)
|
178
|
+
node = dup
|
179
|
+
node.children = children.map {|c| c.do_extend(extends)}
|
180
|
+
node
|
181
|
+
rescue Sass::SyntaxError => e
|
182
|
+
e.modify_backtrace(:filename => filename, :line => line)
|
183
|
+
raise e
|
184
|
+
end
|
185
|
+
|
153
186
|
# Converts a static Sass tree (e.g. the output of \{#perform})
|
154
187
|
# into a static CSS tree.
|
155
188
|
#
|
156
189
|
# \{#cssize} shouldn't be overridden directly;
|
157
190
|
# instead, override \{#\_cssize} or \{#cssize!}.
|
158
191
|
#
|
192
|
+
# @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
|
193
|
+
# The extensions defined for this tree
|
159
194
|
# @param parent [Node, nil] The parent node of this node.
|
160
195
|
# This should only be non-nil if the parent is the same class as this node
|
161
196
|
# @return [Tree::Node] The resulting tree of static nodes
|
162
197
|
# @raise [Sass::SyntaxError] if some element of the tree is invalid
|
163
198
|
# @see Sass::Tree
|
164
|
-
def cssize(parent = nil)
|
165
|
-
_cssize((parent if parent.class == self.class))
|
199
|
+
def cssize(extends, parent = nil)
|
200
|
+
_cssize(extends, (parent if parent.class == self.class))
|
166
201
|
rescue Sass::SyntaxError => e
|
167
202
|
e.modify_backtrace(:filename => filename, :line => line)
|
168
203
|
raise e
|
@@ -237,15 +272,17 @@ module Sass
|
|
237
272
|
# returning the new node.
|
238
273
|
# This doesn't modify this node or any of its children.
|
239
274
|
#
|
275
|
+
# @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
|
276
|
+
# The extensions defined for this tree
|
240
277
|
# @param parent [Node, nil] The parent node of this node.
|
241
278
|
# This should only be non-nil if the parent is the same class as this node
|
242
279
|
# @return [Tree::Node, Array<Tree::Node>] The resulting static CSS nodes
|
243
280
|
# @raise [Sass::SyntaxError] if some element of the tree is invalid
|
244
281
|
# @see #cssize
|
245
282
|
# @see Sass::Tree
|
246
|
-
def _cssize(parent)
|
283
|
+
def _cssize(extends, parent)
|
247
284
|
node = dup
|
248
|
-
node.cssize!(parent)
|
285
|
+
node.cssize!(extends, parent)
|
249
286
|
node
|
250
287
|
end
|
251
288
|
|
@@ -253,11 +290,13 @@ module Sass
|
|
253
290
|
# This *does* modify this node,
|
254
291
|
# but will be run non-destructively by \{#\_cssize\}.
|
255
292
|
#
|
293
|
+
# @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
|
294
|
+
# The extensions defined for this tree
|
256
295
|
# @param parent [Node, nil] The parent node of this node.
|
257
296
|
# This should only be non-nil if the parent is the same class as this node
|
258
297
|
# @see #cssize
|
259
|
-
def cssize!(parent)
|
260
|
-
self.children = children.map {|c| c.cssize(self)}.flatten
|
298
|
+
def cssize!(extends, parent)
|
299
|
+
self.children = children.map {|c| c.cssize(extends, self)}.flatten
|
261
300
|
end
|
262
301
|
|
263
302
|
# Runs any dynamic Sass code in this particular node.
|
@@ -322,8 +361,8 @@ module Sass
|
|
322
361
|
# Returns an error message if the given child node is invalid,
|
323
362
|
# and false otherwise.
|
324
363
|
#
|
325
|
-
# By default, all child nodes except those only allowed
|
326
|
-
# ({Tree::MixinDefNode}, {Tree::ImportNode}) are valid.
|
364
|
+
# By default, all child nodes except those only allowed under specific nodes
|
365
|
+
# ({Tree::MixinDefNode}, {Tree::ImportNode}, {Tree::ExtendNode}) are valid.
|
327
366
|
# This is expected to be overriden by subclasses
|
328
367
|
# for which some children are invalid.
|
329
368
|
#
|
@@ -336,6 +375,8 @@ module Sass
|
|
336
375
|
"Mixins may only be defined at the root of a document."
|
337
376
|
when Tree::ImportNode
|
338
377
|
"Import directives may only be used at the root of a document."
|
378
|
+
when Tree::ExtendNode
|
379
|
+
"Extend directives may only be used within rules."
|
339
380
|
end
|
340
381
|
end
|
341
382
|
|
@@ -366,9 +407,15 @@ module Sass
|
|
366
407
|
(fmt == :sass ? "\n" : " }\n")
|
367
408
|
end
|
368
409
|
|
410
|
+
# Convert any underscores in a string into hyphens,
|
411
|
+
# but only if the `:dasherize` option is set.
|
412
|
+
#
|
413
|
+
# @param s [String] The string to convert
|
414
|
+
# @param opts [{Symbol => Object}] The options hash
|
415
|
+
# @return [String] The converted string
|
369
416
|
def dasherize(s, opts)
|
370
417
|
if opts[:dasherize]
|
371
|
-
s.gsub(
|
418
|
+
s.gsub('_', '-')
|
372
419
|
else
|
373
420
|
s
|
374
421
|
end
|