oreorenasass 3.4.4 → 3.4.5

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.
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