oreorenasass 3.4.4 → 3.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +50 -70
  4. data/Rakefile +5 -26
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/sass +1 -1
  8. data/bin/scss +1 -1
  9. data/lib/sass.rb +12 -19
  10. data/lib/sass/cache_stores/base.rb +2 -2
  11. data/lib/sass/cache_stores/chain.rb +1 -2
  12. data/lib/sass/cache_stores/filesystem.rb +5 -1
  13. data/lib/sass/cache_stores/memory.rb +1 -1
  14. data/lib/sass/cache_stores/null.rb +2 -2
  15. data/lib/sass/callbacks.rb +0 -1
  16. data/lib/sass/css.rb +13 -11
  17. data/lib/sass/engine.rb +173 -424
  18. data/lib/sass/environment.rb +58 -148
  19. data/lib/sass/error.rb +14 -11
  20. data/lib/sass/exec.rb +703 -5
  21. data/lib/sass/importers/base.rb +6 -49
  22. data/lib/sass/importers/filesystem.rb +19 -44
  23. data/lib/sass/logger.rb +4 -1
  24. data/lib/sass/logger/base.rb +4 -2
  25. data/lib/sass/logger/log_level.rb +7 -3
  26. data/lib/sass/media.rb +23 -20
  27. data/lib/sass/plugin.rb +7 -7
  28. data/lib/sass/plugin/compiler.rb +145 -304
  29. data/lib/sass/plugin/configuration.rb +23 -18
  30. data/lib/sass/plugin/merb.rb +1 -1
  31. data/lib/sass/plugin/staleness_checker.rb +3 -3
  32. data/lib/sass/repl.rb +3 -3
  33. data/lib/sass/script.rb +8 -35
  34. data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
  35. data/lib/sass/script/bool.rb +18 -0
  36. data/lib/sass/script/color.rb +606 -0
  37. data/lib/sass/script/css_lexer.rb +4 -8
  38. data/lib/sass/script/css_parser.rb +2 -5
  39. data/lib/sass/script/funcall.rb +245 -0
  40. data/lib/sass/script/functions.rb +408 -1491
  41. data/lib/sass/script/interpolation.rb +79 -0
  42. data/lib/sass/script/lexer.rb +68 -172
  43. data/lib/sass/script/list.rb +85 -0
  44. data/lib/sass/script/literal.rb +221 -0
  45. data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
  46. data/lib/sass/script/{value/null.rb → null.rb} +7 -14
  47. data/lib/sass/script/{value/number.rb → number.rb} +75 -152
  48. data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
  49. data/lib/sass/script/parser.rb +110 -245
  50. data/lib/sass/script/string.rb +51 -0
  51. data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
  52. data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
  53. data/lib/sass/script/variable.rb +58 -0
  54. data/lib/sass/scss/css_parser.rb +3 -9
  55. data/lib/sass/scss/parser.rb +421 -450
  56. data/lib/sass/scss/rx.rb +11 -19
  57. data/lib/sass/scss/static_parser.rb +7 -321
  58. data/lib/sass/selector.rb +194 -68
  59. data/lib/sass/selector/abstract_sequence.rb +14 -29
  60. data/lib/sass/selector/comma_sequence.rb +25 -108
  61. data/lib/sass/selector/sequence.rb +66 -159
  62. data/lib/sass/selector/simple.rb +25 -23
  63. data/lib/sass/selector/simple_sequence.rb +63 -173
  64. data/lib/sass/shared.rb +1 -1
  65. data/lib/sass/supports.rb +15 -13
  66. data/lib/sass/tree/charset_node.rb +1 -1
  67. data/lib/sass/tree/comment_node.rb +3 -3
  68. data/lib/sass/tree/css_import_node.rb +11 -11
  69. data/lib/sass/tree/debug_node.rb +2 -2
  70. data/lib/sass/tree/directive_node.rb +4 -21
  71. data/lib/sass/tree/each_node.rb +8 -8
  72. data/lib/sass/tree/extend_node.rb +7 -14
  73. data/lib/sass/tree/for_node.rb +4 -4
  74. data/lib/sass/tree/function_node.rb +4 -9
  75. data/lib/sass/tree/if_node.rb +1 -1
  76. data/lib/sass/tree/import_node.rb +5 -4
  77. data/lib/sass/tree/media_node.rb +14 -4
  78. data/lib/sass/tree/mixin_def_node.rb +4 -4
  79. data/lib/sass/tree/mixin_node.rb +8 -21
  80. data/lib/sass/tree/node.rb +12 -54
  81. data/lib/sass/tree/prop_node.rb +20 -39
  82. data/lib/sass/tree/return_node.rb +2 -3
  83. data/lib/sass/tree/root_node.rb +3 -19
  84. data/lib/sass/tree/rule_node.rb +22 -35
  85. data/lib/sass/tree/supports_node.rb +13 -0
  86. data/lib/sass/tree/trace_node.rb +1 -2
  87. data/lib/sass/tree/variable_node.rb +3 -9
  88. data/lib/sass/tree/visitors/base.rb +8 -5
  89. data/lib/sass/tree/visitors/check_nesting.rb +19 -49
  90. data/lib/sass/tree/visitors/convert.rb +56 -74
  91. data/lib/sass/tree/visitors/cssize.rb +74 -202
  92. data/lib/sass/tree/visitors/deep_copy.rb +5 -10
  93. data/lib/sass/tree/visitors/extend.rb +7 -7
  94. data/lib/sass/tree/visitors/perform.rb +185 -278
  95. data/lib/sass/tree/visitors/set_options.rb +6 -20
  96. data/lib/sass/tree/visitors/to_css.rb +81 -234
  97. data/lib/sass/tree/warn_node.rb +2 -2
  98. data/lib/sass/tree/while_node.rb +2 -2
  99. data/lib/sass/util.rb +152 -522
  100. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  101. data/lib/sass/util/subset_map.rb +3 -4
  102. data/lib/sass/util/test.rb +1 -0
  103. data/lib/sass/version.rb +22 -20
  104. data/test/Gemfile +3 -0
  105. data/test/Gemfile.lock +10 -0
  106. data/test/sass/cache_test.rb +20 -62
  107. data/test/sass/callbacks_test.rb +1 -1
  108. data/test/sass/conversion_test.rb +2 -296
  109. data/test/sass/css2sass_test.rb +4 -23
  110. data/test/sass/engine_test.rb +354 -411
  111. data/test/sass/exec_test.rb +2 -2
  112. data/test/sass/extend_test.rb +145 -324
  113. data/test/sass/functions_test.rb +86 -873
  114. data/test/sass/importer_test.rb +21 -241
  115. data/test/sass/logger_test.rb +1 -1
  116. data/test/sass/more_results/more_import.css +1 -1
  117. data/test/sass/plugin_test.rb +26 -16
  118. data/test/sass/results/compact.css +1 -1
  119. data/test/sass/results/complex.css +4 -4
  120. data/test/sass/results/expanded.css +1 -1
  121. data/test/sass/results/import.css +1 -1
  122. data/test/sass/results/import_charset_ibm866.css +2 -2
  123. data/test/sass/results/mixins.css +17 -17
  124. data/test/sass/results/nested.css +1 -1
  125. data/test/sass/results/parent_ref.css +2 -2
  126. data/test/sass/results/script.css +3 -3
  127. data/test/sass/results/scss_import.css +1 -1
  128. data/test/sass/script_conversion_test.rb +7 -36
  129. data/test/sass/script_test.rb +53 -485
  130. data/test/sass/scss/css_test.rb +28 -143
  131. data/test/sass/scss/rx_test.rb +4 -4
  132. data/test/sass/scss/scss_test.rb +325 -2119
  133. data/test/sass/templates/scss_import.scss +1 -2
  134. data/test/sass/test_helper.rb +1 -1
  135. data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
  136. data/test/sass/util/subset_map_test.rb +2 -2
  137. data/test/sass/util_test.rb +1 -86
  138. data/test/test_helper.rb +8 -37
  139. metadata +19 -66
  140. data/lib/sass/exec/base.rb +0 -187
  141. data/lib/sass/exec/sass_convert.rb +0 -264
  142. data/lib/sass/exec/sass_scss.rb +0 -424
  143. data/lib/sass/features.rb +0 -47
  144. data/lib/sass/script/tree.rb +0 -16
  145. data/lib/sass/script/tree/funcall.rb +0 -306
  146. data/lib/sass/script/tree/interpolation.rb +0 -118
  147. data/lib/sass/script/tree/list_literal.rb +0 -77
  148. data/lib/sass/script/tree/literal.rb +0 -45
  149. data/lib/sass/script/tree/map_literal.rb +0 -64
  150. data/lib/sass/script/tree/selector.rb +0 -26
  151. data/lib/sass/script/tree/variable.rb +0 -57
  152. data/lib/sass/script/value.rb +0 -11
  153. data/lib/sass/script/value/base.rb +0 -240
  154. data/lib/sass/script/value/bool.rb +0 -35
  155. data/lib/sass/script/value/color.rb +0 -680
  156. data/lib/sass/script/value/helpers.rb +0 -262
  157. data/lib/sass/script/value/list.rb +0 -113
  158. data/lib/sass/script/value/map.rb +0 -70
  159. data/lib/sass/script/value/string.rb +0 -97
  160. data/lib/sass/selector/pseudo.rb +0 -256
  161. data/lib/sass/source/map.rb +0 -210
  162. data/lib/sass/source/position.rb +0 -39
  163. data/lib/sass/source/range.rb +0 -41
  164. data/lib/sass/stack.rb +0 -120
  165. data/lib/sass/tree/at_root_node.rb +0 -83
  166. data/lib/sass/tree/error_node.rb +0 -18
  167. data/lib/sass/tree/keyframe_rule_node.rb +0 -15
  168. data/lib/sass/util/cross_platform_random.rb +0 -19
  169. data/lib/sass/util/normalized_map.rb +0 -130
  170. data/lib/sass/util/ordered_hash.rb +0 -192
  171. data/test/sass/compiler_test.rb +0 -232
  172. data/test/sass/encoding_test.rb +0 -219
  173. data/test/sass/source_map_test.rb +0 -977
  174. data/test/sass/superselector_test.rb +0 -191
  175. data/test/sass/util/normalized_map_test.rb +0 -51
  176. data/test/sass/value_helpers_test.rb +0 -179
@@ -23,11 +23,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
23
23
  def visit_children(parent)
24
24
  @tabs += 1
25
25
  return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
26
- if @format == :sass
27
- "\n" + super.join.rstrip + "\n"
28
- else
29
- " {\n" + super.join.rstrip + "\n#{ @tab_chars * (@tabs - 1)}}\n"
30
- end
26
+ (@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : "\n#{ @tab_chars * (@tabs-1)}}\n")
31
27
  ensure
32
28
  @tabs -= 1
33
29
  end
@@ -56,7 +52,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
56
52
 
57
53
  def visit_comment(node)
58
54
  value = interp_to_src(node.value)
59
- if @format == :sass
55
+ content = if @format == :sass
60
56
  content = value.gsub(/\*\/$/, '').rstrip
61
57
  if content =~ /\A[ \t]/
62
58
  # Re-indent SCSS comments like this:
@@ -67,27 +63,31 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
67
63
  content.sub!(/\A([ \t]*)\/\*/, '/*\1')
68
64
  end
69
65
 
70
- if content.include?("\n")
71
- content.gsub!(/\n \*/, "\n ")
72
- spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
73
- sep = node.type == :silent ? "\n//" : "\n *"
74
- if spaces >= 2
75
- content.gsub!(/\n /, sep)
66
+ content =
67
+ unless content.include?("\n")
68
+ content
76
69
  else
77
- content.gsub!(/\n#{' ' * spaces}/, sep)
70
+ content.gsub!(/\n( \*|\/\/)/, "\n ")
71
+ spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
72
+ sep = node.type == :silent ? "\n//" : "\n *"
73
+ if spaces >= 2
74
+ content.gsub(/\n /, sep)
75
+ else
76
+ content.gsub(/\n#{' ' * spaces}/, sep)
77
+ end
78
78
  end
79
- end
80
79
 
81
80
  content.gsub!(/\A\/\*/, '//') if node.type == :silent
82
81
  content.gsub!(/^/, tab_str)
83
- content = content.rstrip + "\n"
82
+ content.rstrip + "\n"
84
83
  else
85
84
  spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
86
85
  content = if node.type == :silent
87
- value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
88
- else
89
- value
90
- end.gsub(/^/, spaces) + "\n"
86
+ value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
87
+ else
88
+ value
89
+ end.gsub(/^/, spaces) + "\n"
90
+ content
91
91
  end
92
92
  content
93
93
  end
@@ -96,25 +96,19 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
96
96
  "#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
97
97
  end
98
98
 
99
- def visit_error(node)
100
- "#{tab_str}@error #{node.expr.to_sass(@options)}#{semi}\n"
101
- end
102
-
103
99
  def visit_directive(node)
104
100
  res = "#{tab_str}#{interp_to_src(node.value)}"
105
- res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
101
+ res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2');
106
102
  return res + "#{semi}\n" unless node.has_children
107
103
  res + yield + "\n"
108
104
  end
109
105
 
110
106
  def visit_each(node)
111
- vars = node.vars.map {|var| "$#{dasherize(var)}"}.join(", ")
112
- "#{tab_str}@each #{vars} in #{node.list.to_sass(@options)}#{yield}"
107
+ "#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
113
108
  end
114
109
 
115
110
  def visit_extend(node)
116
- "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}" +
117
- "#{" !optional" if node.optional?}\n"
111
+ "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}#{" !optional" if node.optional?}\n"
118
112
  end
119
113
 
120
114
  def visit_for(node)
@@ -136,12 +130,9 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
136
130
 
137
131
  def visit_if(node)
138
132
  name =
139
- if !@is_else
140
- "if"
141
- elsif node.expr
142
- "else if"
143
- else
144
- "else"
133
+ if !@is_else; "if"
134
+ elsif node.expr; "else if"
135
+ else; "else"
145
136
  end
146
137
  @is_else = false
147
138
  str = "#{tab_str}@#{name}"
@@ -160,7 +151,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
160
151
  end
161
152
 
162
153
  def visit_media(node)
163
- "#{tab_str}@media #{query_interp_to_src(node.query)}#{yield}"
154
+ "#{tab_str}@media #{media_interp_to_src(node.query)}#{yield}"
164
155
  end
165
156
 
166
157
  def visit_supports(node)
@@ -168,7 +159,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
168
159
  end
169
160
 
170
161
  def visit_cssimport(node)
171
- if node.uri.is_a?(Sass::Script::Tree::Node)
162
+ if node.uri.is_a?(Sass::Script::Node)
172
163
  str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
173
164
  else
174
165
  str = "#{tab_str}@import #{node.uri}"
@@ -205,24 +196,21 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
205
196
  def visit_mixin(node)
206
197
  arg_to_sass = lambda do |arg|
207
198
  sass = arg.to_sass(@options)
208
- sass = "(#{sass})" if arg.is_a?(Sass::Script::Tree::ListLiteral) && arg.separator == :comma
199
+ sass = "(#{sass})" if arg.is_a?(Sass::Script::List) && arg.separator == :comma
209
200
  sass
210
201
  end
211
202
 
212
203
  unless node.args.empty? && node.keywords.empty? && node.splat.nil?
213
- args = node.args.map(&arg_to_sass)
214
- keywords = Sass::Util.hash_to_a(node.keywords.as_stored).
215
- map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}
216
-
204
+ args = node.args.map(&arg_to_sass).join(", ")
205
+ keywords = Sass::Util.hash_to_a(node.keywords).
206
+ map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}.join(', ')
217
207
  if node.splat
218
- splat = "#{arg_to_sass[node.splat]}..."
219
- kwarg_splat = "#{arg_to_sass[node.kwarg_splat]}..." if node.kwarg_splat
208
+ splat = (args.empty? && keywords.empty?) ? "" : ", "
209
+ splat = "#{splat}#{arg_to_sass[node.splat]}..."
220
210
  end
221
-
222
- arglist = "(#{[args, splat, keywords, kwarg_splat].flatten.compact.join(', ')})"
211
+ arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
223
212
  end
224
- "#{tab_str}#{@format == :sass ? '+' : '@include '}" +
225
- "#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
213
+ "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
226
214
  end
227
215
 
228
216
  def visit_content(node)
@@ -240,13 +228,12 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
240
228
  end
241
229
 
242
230
  def visit_rule(node)
243
- rule = node.parsed_rules ? [node.parsed_rules.to_s] : node.rule
244
231
  if @format == :sass
245
- name = selector_to_sass(rule)
232
+ name = selector_to_sass(node.rule)
246
233
  name = "\\" + name if name[0] == ?:
247
234
  name.gsub(/^/, tab_str) + yield
248
235
  elsif @format == :scss
249
- name = selector_to_scss(rule)
236
+ name = selector_to_scss(node.rule)
250
237
  res = name + yield
251
238
  if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent
252
239
  res.slice!(-3..-1)
@@ -257,8 +244,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
257
244
  end
258
245
 
259
246
  def visit_variable(node)
260
- "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}" +
261
- "#{' !global' if node.global}#{' !default' if node.guarded}#{semi}\n"
247
+ "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
262
248
  end
263
249
 
264
250
  def visit_warn(node)
@@ -269,33 +255,29 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
269
255
  "#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
270
256
  end
271
257
 
272
- def visit_atroot(node)
273
- if node.query
274
- "#{tab_str}@at-root #{query_interp_to_src(node.query)}#{yield}"
275
- elsif node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode)
276
- rule = node.children.first
277
- "#{tab_str}@at-root #{selector_to_src(rule.rule)}#{visit_children(rule)}"
278
- else
279
- "#{tab_str}@at-root#{yield}"
280
- end
281
- end
282
-
283
258
  private
284
259
 
285
260
  def interp_to_src(interp)
286
- interp.map {|r| r.is_a?(String) ? r : r.to_sass(@options)}.join
261
+ interp.map do |r|
262
+ next r if r.is_a?(String)
263
+ "\#{#{r.to_sass(@options)}}"
264
+ end.join
287
265
  end
288
266
 
289
267
  # Like interp_to_src, but removes the unnecessary `#{}` around the keys and
290
- # values in query expressions.
291
- def query_interp_to_src(interp)
292
- interp = interp.map do |e|
293
- next e unless e.is_a?(Sass::Script::Tree::Literal)
294
- next e unless e.value.is_a?(Sass::Script::Value::String)
295
- e.value.value
296
- end
297
-
298
- interp_to_src(interp)
268
+ # values in media expressions.
269
+ def media_interp_to_src(interp)
270
+ Sass::Util.enum_with_index(interp).map do |r, i|
271
+ next r if r.is_a?(String)
272
+ before, after = interp[i-1], interp[i+1]
273
+ if before.is_a?(String) && after.is_a?(String) &&
274
+ ((before[-1] == ?( && after[0] == ?:) ||
275
+ (before =~ /:\s*/ && after[0] == ?)))
276
+ r.to_sass(@options)
277
+ else
278
+ "\#{#{r.to_sass(@options)}}"
279
+ end
280
+ end.join
299
281
  end
300
282
 
301
283
  def selector_to_src(sel)
@@ -307,7 +289,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
307
289
  if r.is_a?(String)
308
290
  r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
309
291
  else
310
- r.to_sass(@options)
292
+ "\#{#{r.to_sass(@options)}}"
311
293
  end
312
294
  end.join
313
295
  end
@@ -9,12 +9,10 @@ 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
- def parent
13
- @parents.last
14
- end
12
+ attr_reader :parent
15
13
 
16
14
  def initialize
17
- @parents = []
15
+ @parent_directives = []
18
16
  @extends = Sass::Util::SubsetMap.new
19
17
  end
20
18
 
@@ -29,19 +27,12 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
29
27
  # Keeps track of the current parent node.
30
28
  def visit_children(parent)
31
29
  with_parent parent do
32
- parent.children = visit_children_without_parent(parent)
30
+ parent.children = super.flatten
33
31
  parent
34
32
  end
35
33
  end
36
34
 
37
- # Like {#visit\_children}, but doesn't set {#parent}.
38
- #
39
- # @param node [Sass::Tree::Node]
40
- # @return [Array<Sass::Tree::Node>] the flattened results of
41
- # visiting all the children of `node`
42
- def visit_children_without_parent(node)
43
- node.children.map {|c| visit(c)}.flatten
44
- end
35
+ MERGEABLE_DIRECTIVES = [Sass::Tree::MediaNode]
45
36
 
46
37
  # Runs a block of code with the current parent node
47
38
  # replaced with the given node.
@@ -50,10 +41,19 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
50
41
  # @yield A block in which the parent is set to `parent`.
51
42
  # @return [Object] The return value of the block.
52
43
  def with_parent(parent)
53
- @parents.push parent
44
+ if parent.is_a?(Sass::Tree::DirectiveNode)
45
+ if MERGEABLE_DIRECTIVES.any? {|klass| parent.is_a?(klass)}
46
+ old_parent_directive = @parent_directives.pop
47
+ end
48
+ @parent_directives.push parent
49
+ end
50
+
51
+ old_parent, @parent = @parent, parent
54
52
  yield
55
53
  ensure
56
- @parents.pop
54
+ @parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode)
55
+ @parent_directives.push old_parent_directive if old_parent_directive
56
+ @parent = old_parent
57
57
  end
58
58
 
59
59
  # In Ruby 1.8, ensures that there's only one `@charset` directive
@@ -106,7 +106,7 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
106
106
  end
107
107
 
108
108
  # A simple struct wrapping up information about a single `@extend` instance. A
109
- # single {ExtendNode} can have multiple Extends if either the parent node or
109
+ # single [ExtendNode] can have multiple Extends if either the parent node or
110
110
  # the extended selector is a comma sequence.
111
111
  #
112
112
  # @attr extender [Sass::Selector::Sequence]
@@ -123,23 +123,61 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
123
123
 
124
124
  # Registers an extension in the `@extends` subset map.
125
125
  def visit_extend(node)
126
- parent.resolved_rules.populate_extends(@extends, node.resolved_selector, node,
127
- @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)})
126
+ node.resolved_selector.members.each do |seq|
127
+ if seq.members.size > 1
128
+ raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
129
+ end
130
+
131
+ sseq = seq.members.first
132
+ if !sseq.is_a?(Sass::Selector::SimpleSequence)
133
+ raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
134
+ elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
135
+ raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend parent selectors")
136
+ end
137
+
138
+ sel = sseq.members
139
+ parent.resolved_rules.members.each do |member|
140
+ if !member.members.last.is_a?(Sass::Selector::SimpleSequence)
141
+ raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
142
+ end
143
+
144
+ @extends[sel] = Extend.new(member, sel, node, @parent_directives.dup, :not_found)
145
+ end
146
+ end
147
+
128
148
  []
129
149
  end
130
150
 
131
151
  # Modifies exception backtraces to include the imported file.
132
152
  def visit_import(node)
133
- visit_children_without_parent(node)
153
+ # Don't use #visit_children to avoid adding the import node to the list of parents.
154
+ node.children.map {|c| visit(c)}.flatten
134
155
  rescue Sass::SyntaxError => e
135
156
  e.modify_backtrace(:filename => node.children.first.filename)
136
157
  e.add_backtrace(:filename => node.filename, :line => node.line)
137
158
  raise e
138
159
  end
139
160
 
161
+ # Bubbles the `@media` directive up through RuleNodes
162
+ # and merges it with other `@media` directives.
163
+ def visit_media(node)
164
+ yield unless bubble(node)
165
+ media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
166
+ node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
167
+ media = media.select {|n| n.resolved_query = n.resolved_query.merge(node.resolved_query)}
168
+ (node.children.empty? ? [] : [node]) + media
169
+ end
170
+
171
+ # Bubbles the `@supports` directive up through RuleNodes.
172
+ def visit_supports(node)
173
+ yield unless bubble(node)
174
+ node
175
+ end
176
+
140
177
  # Asserts that all the traced children are valid in their new location.
141
178
  def visit_trace(node)
142
- visit_children_without_parent(node)
179
+ # Don't use #visit_children to avoid adding the trace node to the list of parents.
180
+ node.children.map {|c| visit(c)}.flatten
143
181
  rescue Sass::SyntaxError => e
144
182
  e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
145
183
  e.add_backtrace(:filename => node.filename, :line => node.line)
@@ -165,38 +203,17 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
165
203
  result
166
204
  end
167
205
 
168
- def visit_atroot(node)
169
- # If there aren't any more directives or rules that this @at-root needs to
170
- # exclude, we can get rid of it and just evaluate the children.
171
- if @parents.none? {|n| node.exclude_node?(n)}
172
- results = visit_children_without_parent(node)
173
- results.each {|c| c.tabs += node.tabs if bubblable?(c)}
174
- if !results.empty? && bubblable?(results.last)
175
- results.last.group_end = node.group_end
176
- end
177
- return results
178
- end
179
-
180
- # If this @at-root excludes the immediate parent, return it as-is so that it
181
- # can be bubbled up by the parent node.
182
- return Bubble.new(node) if node.exclude_node?(parent)
183
-
184
- # Otherwise, duplicate the current parent and move it into the @at-root
185
- # node. As above, returning an @at-root node signals to the parent directive
186
- # that it should be bubbled upwards.
187
- bubble(node)
188
- end
189
-
190
- # The following directives are visible and have children. This means they need
191
- # to be able to handle bubbling up nodes such as @at-root and @media.
192
-
193
- # Updates the indentation of the rule node based on the nesting
194
- # level. The selectors were resolved in {Perform}.
206
+ # Resolves parent references and nested selectors,
207
+ # and updates the indentation of the rule node based on the nesting level.
195
208
  def visit_rule(node)
209
+ parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
210
+ # It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
211
+ node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)
212
+
196
213
  yield
197
214
 
198
- rules = node.children.select {|c| bubblable?(c)}
199
- props = node.children.reject {|c| bubblable?(c) || c.invisible?}
215
+ rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles?}
216
+ props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles? || c.invisible?}
200
217
 
201
218
  unless props.empty?
202
219
  node.children = props
@@ -204,166 +221,21 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
204
221
  rules.unshift(node)
205
222
  end
206
223
 
207
- rules = debubble(rules)
208
- unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty? || !bubblable?(rules.last)
209
- rules.last.group_end = true
210
- end
211
- rules
212
- end
213
-
214
- def visit_keyframerule(node)
215
- return node unless node.has_children
216
-
217
- yield
218
-
219
- debubble(node.children, node)
220
- end
224
+ rules.last.group_end = true unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty?
221
225
 
222
- # Bubbles a directive up through RuleNodes.
223
- def visit_directive(node)
224
- return node unless node.has_children
225
- return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
226
-
227
- yield
228
-
229
- # Since we don't know if the mere presence of an unknown directive may be
230
- # important, we should keep an empty version around even if all the contents
231
- # are removed via @at-root. However, if the contents are just bubbled out,
232
- # we don't need to do so.
233
- directive_exists = node.children.any? do |child|
234
- next true unless child.is_a?(Bubble)
235
- next false unless child.node.is_a?(Sass::Tree::DirectiveNode)
236
- child.node.resolved_value == node.resolved_value
237
- end
238
-
239
- # We know empty @keyframes directives do nothing.
240
- if directive_exists || node.name == '@keyframes'
241
- []
242
- else
243
- empty_node = node.dup
244
- empty_node.children = []
245
- [empty_node]
246
- end + debubble(node.children, node)
247
- end
248
-
249
- # Bubbles the `@media` directive up through RuleNodes
250
- # and merges it with other `@media` directives.
251
- def visit_media(node)
252
- return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
253
- return Bubble.new(node) if parent.is_a?(Sass::Tree::MediaNode)
254
-
255
- yield
256
-
257
- debubble(node.children, node) do |child|
258
- next child unless child.is_a?(Sass::Tree::MediaNode)
259
- # Copies of `node` can be bubbled, and we don't want to merge it with its
260
- # own query.
261
- next child if child.resolved_query == node.resolved_query
262
- next child if child.resolved_query = child.resolved_query.merge(node.resolved_query)
263
- end
264
- end
265
-
266
- # Bubbles the `@supports` directive up through RuleNodes.
267
- def visit_supports(node)
268
- return node unless node.has_children
269
- return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
270
-
271
- yield
272
-
273
- debubble(node.children, node)
226
+ rules
274
227
  end
275
228
 
276
229
  private
277
230
 
278
- # "Bubbles" `node` one level by copying the parent and wrapping `node`'s
279
- # children with it.
280
- #
281
- # @param node [Sass::Tree::Node].
282
- # @return [Bubble]
283
231
  def bubble(node)
232
+ return unless parent.is_a?(Sass::Tree::RuleNode)
284
233
  new_rule = parent.dup
285
234
  new_rule.children = node.children
286
- node.children = [new_rule]
287
- Bubble.new(node)
288
- end
289
-
290
- # Pops all bubbles in `children` and intersperses the results with the other
291
- # values.
292
- #
293
- # If `parent` is passed, it's copied and used as the parent node for the
294
- # nested portions of `children`.
295
- #
296
- # @param children [List<Sass::Tree::Node, Bubble>]
297
- # @param parent [Sass::Tree::Node]
298
- # @yield [node] An optional block for processing bubbled nodes. Each bubbled
299
- # node will be passed to this block.
300
- # @yieldparam node [Sass::Tree::Node] A bubbled node.
301
- # @yieldreturn [Sass::Tree::Node?] A node to use in place of the bubbled node.
302
- # This can be the node itself, or `nil` to indicate that the node should be
303
- # omitted.
304
- # @return [List<Sass::Tree::Node, Bubble>]
305
- def debubble(children, parent = nil)
306
- # Keep track of the previous parent so that we don't divide `parent`
307
- # unnecessarily if the `@at-root` doesn't produce any new nodes (e.g.
308
- # `@at-root {@extend %foo}`).
309
- previous_parent = nil
310
-
311
- Sass::Util.slice_by(children) {|c| c.is_a?(Bubble)}.map do |(is_bubble, slice)|
312
- unless is_bubble
313
- next slice unless parent
314
- if previous_parent
315
- previous_parent.children.push(*slice)
316
- next []
317
- else
318
- previous_parent = new_parent = parent.dup
319
- new_parent.children = slice
320
- next new_parent
321
- end
322
- end
323
-
324
- slice.map do |bubble|
325
- next unless (node = block_given? ? yield(bubble.node) : bubble.node)
326
- node.tabs += bubble.tabs
327
- node.group_end = bubble.group_end
328
- results = [visit(node)].flatten
329
- previous_parent = nil unless results.empty?
330
- results
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
235
+ node.children = with_parent(node) {Array(visit(new_rule))}
236
+ # If the last child is actually the end of the group,
237
+ # the parent's cssize will set it properly
238
+ node.children.last.group_end = false unless node.children.empty?
239
+ true
368
240
  end
369
241
  end