sass 3.3.0.rc.2 → 3.3.0.rc.3
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.
- checksums.yaml +15 -0
- data/CONTRIBUTING +1 -1
- data/README.md +7 -7
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/bin/sass +5 -1
- data/bin/sass-convert +5 -1
- data/bin/scss +5 -1
- data/ext/mkrf_conf.rb +23 -0
- data/lib/sass/css.rb +1 -1
- data/lib/sass/engine.rb +19 -9
- data/lib/sass/environment.rb +8 -0
- data/lib/sass/exec.rb +4 -4
- data/lib/sass/features.rb +0 -1
- data/lib/sass/importers/base.rb +13 -6
- data/lib/sass/importers/deprecated_path.rb +8 -2
- data/lib/sass/importers/filesystem.rb +33 -7
- data/lib/sass/logger.rb +1 -4
- data/lib/sass/logger/base.rb +0 -2
- data/lib/sass/logger/log_level.rb +0 -2
- data/lib/sass/plugin.rb +2 -2
- data/lib/sass/plugin/compiler.rb +23 -11
- data/lib/sass/plugin/configuration.rb +0 -1
- data/lib/sass/railtie.rb +1 -0
- data/lib/sass/script/css_lexer.rb +0 -1
- data/lib/sass/script/css_parser.rb +0 -1
- data/lib/sass/script/functions.rb +158 -96
- data/lib/sass/script/lexer.rb +29 -35
- data/lib/sass/script/parser.rb +10 -3
- data/lib/sass/script/tree.rb +0 -1
- data/lib/sass/script/tree/funcall.rb +21 -5
- data/lib/sass/script/tree/list_literal.rb +0 -1
- data/lib/sass/script/value/arg_list.rb +7 -3
- data/lib/sass/script/value/bool.rb +0 -1
- data/lib/sass/script/value/null.rb +0 -1
- data/lib/sass/script/value/number.rb +2 -6
- data/lib/sass/scss/css_parser.rb +0 -1
- data/lib/sass/scss/parser.rb +5 -5
- data/lib/sass/scss/script_lexer.rb +0 -1
- data/lib/sass/scss/script_parser.rb +0 -1
- data/lib/sass/selector.rb +11 -1
- data/lib/sass/selector/comma_sequence.rb +3 -4
- data/lib/sass/selector/sequence.rb +11 -7
- data/lib/sass/selector/simple_sequence.rb +42 -11
- data/lib/sass/source/map.rb +6 -19
- data/lib/sass/tree/at_root_node.rb +1 -1
- data/lib/sass/tree/mixin_node.rb +2 -2
- data/lib/sass/tree/prop_node.rb +0 -1
- data/lib/sass/tree/variable_node.rb +0 -5
- data/lib/sass/tree/visitors/check_nesting.rb +0 -1
- data/lib/sass/tree/visitors/convert.rb +2 -2
- data/lib/sass/tree/visitors/cssize.rb +184 -84
- data/lib/sass/tree/visitors/deep_copy.rb +0 -1
- data/lib/sass/tree/visitors/perform.rb +14 -44
- data/lib/sass/util.rb +59 -12
- data/lib/sass/util/cross_platform_random.rb +19 -0
- data/lib/sass/util/normalized_map.rb +17 -1
- data/test/sass/compiler_test.rb +10 -0
- data/test/sass/conversion_test.rb +36 -0
- data/test/sass/css2sass_test.rb +19 -0
- data/test/sass/engine_test.rb +54 -105
- data/test/sass/functions_test.rb +233 -26
- data/test/sass/importer_test.rb +72 -10
- data/test/sass/plugin_test.rb +14 -0
- data/test/sass/script_conversion_test.rb +4 -4
- data/test/sass/script_test.rb +58 -21
- data/test/sass/scss/css_test.rb +8 -1
- data/test/sass/scss/scss_test.rb +376 -179
- data/test/sass/source_map_test.rb +8 -0
- data/test/sass/templates/subdir/import_up1.scss +1 -0
- data/test/sass/templates/subdir/import_up2.scss +1 -0
- data/test/sass/util_test.rb +16 -0
- data/test/test_helper.rb +12 -4
- metadata +269 -287
- data/lib/sass/script/tree/selector.rb +0 -30
data/lib/sass/source/map.rb
CHANGED
@@ -94,9 +94,9 @@ module Sass::Source
|
|
94
94
|
# rubocop:disable MethodLength
|
95
95
|
def to_json(options)
|
96
96
|
css_uri, css_path, sourcemap_path =
|
97
|
-
[:css_uri, :css_path, :sourcemap_path]
|
97
|
+
options[:css_uri], options[:css_path], options[:sourcemap_path]
|
98
98
|
unless css_uri || (css_path && sourcemap_path)
|
99
|
-
raise ArgumentError.new("Sass::Source::Map#to_json requires either "
|
99
|
+
raise ArgumentError.new("Sass::Source::Map#to_json requires either " \
|
100
100
|
"the :css_uri option or both the :css_path and :soucemap_path options.")
|
101
101
|
end
|
102
102
|
css_path &&= Pathname.pwd.join(Pathname.new(css_path)).cleanpath
|
@@ -111,7 +111,6 @@ module Sass::Source
|
|
111
111
|
next_source_id = 0
|
112
112
|
line_data = []
|
113
113
|
segment_data_for_line = []
|
114
|
-
no_public_url = Set.new
|
115
114
|
|
116
115
|
# These track data necessary for the delta coding.
|
117
116
|
previous_target_line = nil
|
@@ -122,22 +121,9 @@ module Sass::Source
|
|
122
121
|
|
123
122
|
@data.each do |m|
|
124
123
|
file, importer = m.input.file, m.input.importer
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
source_uri = file_path.relative_path_from(sourcemap_path.dirname).to_s
|
129
|
-
elsif no_public_url.include?(file)
|
130
|
-
next
|
131
|
-
else
|
132
|
-
no_public_url << file
|
133
|
-
Sass::Util.sass_warn <<WARNING
|
134
|
-
WARNING: Couldn't determine public URL for "#{file}" while generating sourcemap.
|
135
|
-
Without a public URL, there's nothing for the source map to link to.
|
136
|
-
Custom importers should define the #public_url method.
|
137
|
-
WARNING
|
138
|
-
next
|
139
|
-
end
|
140
|
-
end
|
124
|
+
source_uri = importer &&
|
125
|
+
importer.public_url(file, sourcemap_path && sourcemap_path.dirname.to_s)
|
126
|
+
next unless source_uri
|
141
127
|
|
142
128
|
current_source_id = source_uri_to_id[source_uri]
|
143
129
|
unless current_source_id
|
@@ -190,6 +176,7 @@ WARNING
|
|
190
176
|
source_names = []
|
191
177
|
(0...next_source_id).each {|id| source_names.push(id_to_source_uri[id].to_s)}
|
192
178
|
write_json_field(result, "sources", source_names)
|
179
|
+
write_json_field(result, "names", [])
|
193
180
|
write_json_field(result, "file", css_uri)
|
194
181
|
|
195
182
|
result << "\n}"
|
@@ -70,7 +70,7 @@ module Sass
|
|
70
70
|
# @return [Boolean]
|
71
71
|
def exclude_node?(node)
|
72
72
|
return exclude?(node.name.gsub(/^@/, '')) if node.is_a?(Sass::Tree::DirectiveNode)
|
73
|
-
exclude?(
|
73
|
+
exclude?('rule') && node.is_a?(Sass::Tree::RuleNode)
|
74
74
|
end
|
75
75
|
|
76
76
|
# @see Node#bubbles?
|
data/lib/sass/tree/mixin_node.rb
CHANGED
@@ -16,7 +16,7 @@ module Sass::Tree
|
|
16
16
|
attr_accessor :args
|
17
17
|
|
18
18
|
# A hash from keyword argument names to values.
|
19
|
-
# @return [
|
19
|
+
# @return [Sass::Util::NormalizedMap<Script::Tree::Node>]
|
20
20
|
attr_accessor :keywords
|
21
21
|
|
22
22
|
# The first splat argument for this mixin, if one exists.
|
@@ -39,7 +39,7 @@ module Sass::Tree
|
|
39
39
|
# @param args [Array<Script::Tree::Node>] See \{#args}
|
40
40
|
# @param splat [Script::Tree::Node] See \{#splat}
|
41
41
|
# @param kwarg_splat [Script::Tree::Node] See \{#kwarg_splat}
|
42
|
-
# @param keywords [
|
42
|
+
# @param keywords [Sass::Util::NormalizedMap<Script::Tree::Node>] See \{#keywords}
|
43
43
|
def initialize(name, args, keywords, splat, kwarg_splat)
|
44
44
|
@name = name
|
45
45
|
@args = args
|
data/lib/sass/tree/prop_node.rb
CHANGED
@@ -20,11 +20,6 @@ module Sass
|
|
20
20
|
# @return [Boolean]
|
21
21
|
attr_reader :global
|
22
22
|
|
23
|
-
# Whether we've warned about deprecated global variable
|
24
|
-
# assignment yet for this node.
|
25
|
-
# @return [Boolean]
|
26
|
-
attr_accessor :global_warning_given
|
27
|
-
|
28
23
|
# @param name [String] The name of the variable
|
29
24
|
# @param expr [Script::Tree::Node] See \{#expr}
|
30
25
|
# @param guarded [Boolean] See \{#guarded}
|
@@ -68,7 +68,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
68
68
|
end
|
69
69
|
|
70
70
|
if content.include?("\n")
|
71
|
-
content.gsub!(
|
71
|
+
content.gsub!(/\n \*/, "\n ")
|
72
72
|
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
|
73
73
|
sep = node.type == :silent ? "\n//" : "\n *"
|
74
74
|
if spaces >= 2
|
@@ -207,7 +207,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
207
207
|
|
208
208
|
unless node.args.empty? && node.keywords.empty? && node.splat.nil?
|
209
209
|
args = node.args.map(&arg_to_sass)
|
210
|
-
keywords = Sass::Util.hash_to_a(node.keywords).
|
210
|
+
keywords = Sass::Util.hash_to_a(node.keywords.as_stored).
|
211
211
|
map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}
|
212
212
|
|
213
213
|
if node.splat
|
@@ -9,10 +9,12 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
9
9
|
|
10
10
|
# Returns the immediate parent of the current node.
|
11
11
|
# @return [Tree::Node]
|
12
|
-
|
12
|
+
def parent
|
13
|
+
@parents.last
|
14
|
+
end
|
13
15
|
|
14
16
|
def initialize
|
15
|
-
@
|
17
|
+
@parents = []
|
16
18
|
@extends = Sass::Util::SubsetMap.new
|
17
19
|
end
|
18
20
|
|
@@ -41,8 +43,6 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
41
43
|
node.children.map {|c| visit(c)}.flatten
|
42
44
|
end
|
43
45
|
|
44
|
-
MERGEABLE_DIRECTIVES = [Sass::Tree::MediaNode]
|
45
|
-
|
46
46
|
# Runs a block of code with the current parent node
|
47
47
|
# replaced with the given node.
|
48
48
|
#
|
@@ -50,19 +50,10 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
50
50
|
# @yield A block in which the parent is set to `parent`.
|
51
51
|
# @return [Object] The return value of the block.
|
52
52
|
def with_parent(parent)
|
53
|
-
|
54
|
-
if MERGEABLE_DIRECTIVES.any? {|klass| parent.is_a?(klass)}
|
55
|
-
old_parent_directive = @parent_directives.pop
|
56
|
-
end
|
57
|
-
@parent_directives.push parent
|
58
|
-
end
|
59
|
-
|
60
|
-
old_parent, @parent = @parent, parent
|
53
|
+
@parents.push parent
|
61
54
|
yield
|
62
55
|
ensure
|
63
|
-
@
|
64
|
-
@parent_directives.push old_parent_directive if old_parent_directive
|
65
|
-
@parent = old_parent
|
56
|
+
@parents.pop
|
66
57
|
end
|
67
58
|
|
68
59
|
# In Ruby 1.8, ensures that there's only one `@charset` directive
|
@@ -82,17 +73,29 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
82
73
|
node.children.unshift charset if charset
|
83
74
|
end
|
84
75
|
|
85
|
-
|
86
|
-
|
87
|
-
|
76
|
+
imports_to_move = []
|
77
|
+
import_limit = nil
|
78
|
+
i = -1
|
79
|
+
node.children.reject! do |n|
|
80
|
+
i += 1
|
81
|
+
if import_limit
|
82
|
+
next false unless n.is_a?(Sass::Tree::CssImportNode)
|
83
|
+
imports_to_move << n
|
84
|
+
next true
|
85
|
+
end
|
86
|
+
|
87
|
+
if !n.is_a?(Sass::Tree::CommentNode) &&
|
88
|
+
!n.is_a?(Sass::Tree::CharsetNode) &&
|
89
|
+
!n.is_a?(Sass::Tree::CssImportNode)
|
90
|
+
import_limit = i
|
91
|
+
end
|
92
|
+
|
93
|
+
false
|
88
94
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
node.children = node.children[0..index] + imports + node.children[index + 1..-1]
|
94
|
-
else
|
95
|
-
node.children = imports + node.children
|
95
|
+
|
96
|
+
if import_limit
|
97
|
+
node.children = node.children[0...import_limit] + imports_to_move +
|
98
|
+
node.children[import_limit..-1]
|
96
99
|
end
|
97
100
|
end
|
98
101
|
|
@@ -138,7 +141,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
138
141
|
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
|
139
142
|
end
|
140
143
|
|
141
|
-
|
144
|
+
parent_directives = @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)}
|
145
|
+
@extends[sel] = Extend.new(member, sel, node, parent_directives, :not_found)
|
142
146
|
end
|
143
147
|
end
|
144
148
|
|
@@ -154,31 +158,6 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
154
158
|
raise e
|
155
159
|
end
|
156
160
|
|
157
|
-
# Bubbles the `@media` directive up through RuleNodes
|
158
|
-
# and merges it with other `@media` directives.
|
159
|
-
def visit_media(node)
|
160
|
-
yield unless bubble(node)
|
161
|
-
|
162
|
-
bubbled = node.children.select do |n|
|
163
|
-
n.is_a?(Sass::Tree::AtRootNode) || n.is_a?(Sass::Tree::MediaNode)
|
164
|
-
end
|
165
|
-
node.children -= bubbled
|
166
|
-
|
167
|
-
bubbled = bubbled.map do |n|
|
168
|
-
next visit(n) if n.is_a?(Sass::Tree::AtRootNode)
|
169
|
-
# Otherwise, n should be a MediaNode.
|
170
|
-
next [] unless n.resolved_query = n.resolved_query.merge(node.resolved_query)
|
171
|
-
n
|
172
|
-
end.flatten
|
173
|
-
|
174
|
-
(node.children.empty? ? [] : [node]) + bubbled
|
175
|
-
end
|
176
|
-
|
177
|
-
# Bubbles the `@supports` directive up through RuleNodes.
|
178
|
-
def visit_supports(node)
|
179
|
-
visit_directive(node) {yield}
|
180
|
-
end
|
181
|
-
|
182
161
|
# Asserts that all the traced children are valid in their new location.
|
183
162
|
def visit_trace(node)
|
184
163
|
visit_children_without_parent(node)
|
@@ -188,16 +167,6 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
188
167
|
raise e
|
189
168
|
end
|
190
169
|
|
191
|
-
# Bubbles a directive up through RuleNodes.
|
192
|
-
def visit_directive(node)
|
193
|
-
return yield unless node.has_children
|
194
|
-
yield unless (bubbled = bubble(node))
|
195
|
-
at_roots = node.children.select {|n| n.is_a?(Sass::Tree::AtRootNode)}
|
196
|
-
node.children -= at_roots
|
197
|
-
at_roots.map! {|n| visit(n)}.flatten
|
198
|
-
(bubbled && node.children.empty? ? [] : [node]) + at_roots
|
199
|
-
end
|
200
|
-
|
201
170
|
# Converts nested properties into flat properties
|
202
171
|
# and updates the indentation of the prop node based on the nesting level.
|
203
172
|
def visit_prop(node)
|
@@ -217,15 +186,38 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
217
186
|
result
|
218
187
|
end
|
219
188
|
|
189
|
+
def visit_atroot(node)
|
190
|
+
# If there aren't any more directives or rules that this @at-root needs to
|
191
|
+
# exclude, we can get rid of it and just evaluate the children.
|
192
|
+
if @parents.none? {|n| node.exclude_node?(n)}
|
193
|
+
results = visit_children_without_parent(node)
|
194
|
+
results.each {|c| c.tabs += node.tabs if bubblable?(c)}
|
195
|
+
if !results.empty? && bubblable?(results.last)
|
196
|
+
results.last.group_end = node.group_end
|
197
|
+
end
|
198
|
+
return results
|
199
|
+
end
|
200
|
+
|
201
|
+
# If this @at-root excludes the immediate parent, return it as-is so that it
|
202
|
+
# can be bubbled up by the parent node.
|
203
|
+
return Bubble.new(node) if node.exclude_node?(parent)
|
204
|
+
|
205
|
+
# Otherwise, duplicate the current parent and move it into the @at-root
|
206
|
+
# node. As above, returning an @at-root node signals to the parent directive
|
207
|
+
# that it should be bubbled upwards.
|
208
|
+
bubble(node)
|
209
|
+
end
|
210
|
+
|
211
|
+
# The following directives are visible and have children. This means they need
|
212
|
+
# to be able to handle bubbling up nodes such as @at-root and @media.
|
213
|
+
|
220
214
|
# Updates the indentation of the rule node based on the nesting
|
221
215
|
# level. The selectors were resolved in {Perform}.
|
222
216
|
def visit_rule(node)
|
223
217
|
yield
|
224
218
|
|
225
|
-
rules = node.children.select {|c|
|
226
|
-
props = node.children.reject {|c|
|
227
|
-
|
228
|
-
rules.map {|c| c.is_a?(Sass::Tree::AtRootNode) ? visit(c) : c}.flatten
|
219
|
+
rules = node.children.select {|c| bubblable?(c)}
|
220
|
+
props = node.children.reject {|c| bubblable?(c) || c.invisible?}
|
229
221
|
|
230
222
|
unless props.empty?
|
231
223
|
node.children = props
|
@@ -233,37 +225,145 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
233
225
|
rules.unshift(node)
|
234
226
|
end
|
235
227
|
|
236
|
-
rules
|
237
|
-
|
228
|
+
rules = debubble(rules)
|
229
|
+
unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty? || !bubblable?(rules.last)
|
230
|
+
rules.last.group_end = true
|
231
|
+
end
|
238
232
|
rules
|
239
233
|
end
|
240
234
|
|
241
|
-
|
242
|
-
|
243
|
-
|
235
|
+
# Bubbles a directive up through RuleNodes.
|
236
|
+
def visit_directive(node)
|
237
|
+
return node unless node.has_children
|
238
|
+
return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
|
239
|
+
|
240
|
+
yield
|
241
|
+
|
242
|
+
# Since we don't know if the mere presence of an unknown directive may be
|
243
|
+
# important, we should keep an empty version around even if all the contents
|
244
|
+
# are removed via @at-root. However, if the contents are just bubbled out,
|
245
|
+
# we don't need to do so.
|
246
|
+
directive_exists = node.children.any? do |child|
|
247
|
+
next true unless child.is_a?(Bubble)
|
248
|
+
next false unless child.node.is_a?(Sass::Tree::DirectiveNode)
|
249
|
+
child.node.resolved_value == node.resolved_value
|
250
|
+
end
|
251
|
+
|
252
|
+
if directive_exists
|
253
|
+
[]
|
254
|
+
else
|
255
|
+
empty_node = node.dup
|
256
|
+
empty_node.children = []
|
257
|
+
[empty_node]
|
258
|
+
end + debubble(node.children, node)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Bubbles the `@media` directive up through RuleNodes
|
262
|
+
# and merges it with other `@media` directives.
|
263
|
+
def visit_media(node)
|
264
|
+
return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
|
265
|
+
return Bubble.new(node) if parent.is_a?(Sass::Tree::MediaNode)
|
266
|
+
|
267
|
+
yield
|
244
268
|
|
245
|
-
|
246
|
-
|
247
|
-
node
|
248
|
-
|
269
|
+
debubble(node.children, node) do |child|
|
270
|
+
next child unless child.is_a?(Sass::Tree::MediaNode)
|
271
|
+
# Copies of `node` can be bubbled, and we don't want to merge it with its
|
272
|
+
# own query.
|
273
|
+
next child if child.resolved_query == node.resolved_query
|
274
|
+
next child if child.resolved_query = child.resolved_query.merge(node.resolved_query)
|
249
275
|
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Bubbles the `@supports` directive up through RuleNodes.
|
279
|
+
def visit_supports(node)
|
280
|
+
return node unless node.has_children
|
281
|
+
return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
|
250
282
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
results
|
283
|
+
yield
|
284
|
+
|
285
|
+
debubble(node.children, node)
|
255
286
|
end
|
256
287
|
|
257
288
|
private
|
258
289
|
|
290
|
+
# "Bubbles" `node` one level by copying the parent and wrapping `node`'s
|
291
|
+
# children with it.
|
292
|
+
#
|
293
|
+
# @param node [Sass::Tree::Node].
|
294
|
+
# @return [Bubble]
|
259
295
|
def bubble(node)
|
260
|
-
return unless parent.is_a?(Sass::Tree::RuleNode)
|
261
296
|
new_rule = parent.dup
|
262
297
|
new_rule.children = node.children
|
263
|
-
node.children =
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
298
|
+
node.children = [new_rule]
|
299
|
+
Bubble.new(node)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Pops all bubbles in `children` and intersperses the results with the other
|
303
|
+
# values.
|
304
|
+
#
|
305
|
+
# If `parent` is passed, it's copied and used as the parent node for the
|
306
|
+
# nested portions of `children`.
|
307
|
+
#
|
308
|
+
# @param children [List<Sass::Tree::Node, Bubble>]
|
309
|
+
# @param parent [Sass::Tree::Node]
|
310
|
+
# @yield [node] An optional block for processing bubbled nodes. Each bubbled
|
311
|
+
# node will be passed to this block.
|
312
|
+
# @yieldparam node [Sass::Tree::Node] A bubbled node.
|
313
|
+
# @yieldreturn [Sass::Tree::Node?] A node to use in place of the bubbled node.
|
314
|
+
# This can be the node itself, or `nil` to indicate that the node should be
|
315
|
+
# omitted.
|
316
|
+
# @return [List<Sass::Tree::Node, Bubble>]
|
317
|
+
def debubble(children, parent = nil)
|
318
|
+
Sass::Util.slice_by(children) {|c| c.is_a?(Bubble)}.map do |(is_bubble, slice)|
|
319
|
+
unless is_bubble
|
320
|
+
next slice unless parent
|
321
|
+
new_parent = parent.dup
|
322
|
+
new_parent.children = slice
|
323
|
+
next new_parent
|
324
|
+
end
|
325
|
+
|
326
|
+
next slice.map do |bubble|
|
327
|
+
next unless (node = block_given? ? yield(bubble.node) : bubble.node)
|
328
|
+
node.tabs += bubble.tabs
|
329
|
+
node.group_end = bubble.group_end
|
330
|
+
[visit(node)].flatten
|
331
|
+
end.compact
|
332
|
+
end.flatten
|
333
|
+
end
|
334
|
+
|
335
|
+
# Returns whether or not a node can be bubbled up through the syntax tree.
|
336
|
+
#
|
337
|
+
# @param node [Sass::Tree::Node]
|
338
|
+
# @return [Boolean]
|
339
|
+
def bubblable?(node)
|
340
|
+
node.is_a?(Sass::Tree::RuleNode) || node.bubbles?
|
341
|
+
end
|
342
|
+
|
343
|
+
# A wrapper class for a node that indicates to the parent that it should
|
344
|
+
# treat the wrapped node as a sibling rather than a child.
|
345
|
+
#
|
346
|
+
# Nodes should be wrapped before they're passed to \{Cssize.visit}. They will
|
347
|
+
# be automatically visited upon calling \{#pop}.
|
348
|
+
#
|
349
|
+
# This duck types as a [Sass::Tree::Node] for the purposes of
|
350
|
+
# tree-manipulation operations.
|
351
|
+
class Bubble
|
352
|
+
attr_accessor :node
|
353
|
+
attr_accessor :tabs
|
354
|
+
attr_accessor :group_end
|
355
|
+
|
356
|
+
def initialize(node)
|
357
|
+
@node = node
|
358
|
+
@tabs = 0
|
359
|
+
end
|
360
|
+
|
361
|
+
def bubbles?
|
362
|
+
true
|
363
|
+
end
|
364
|
+
|
365
|
+
def inspect
|
366
|
+
"(Bubble #{node.inspect})"
|
367
|
+
end
|
268
368
|
end
|
269
369
|
end
|