sass 3.2.0.alpha.63 → 3.2.0.alpha.64

Sign up to get free protection for your applications and to get access to all the features.
data/REVISION CHANGED
@@ -1 +1 @@
1
- 17bc035c572a173b3018e70df273c9776a8f76ee
1
+ 13aa911b7cb8d035e5a613f7d94d9b08241a46cd
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0.alpha.63
1
+ 3.2.0.alpha.64
@@ -53,6 +53,7 @@ module Sass
53
53
  # @param key [String]
54
54
  # @return [String] The path to the cache file.
55
55
  def path_to(key)
56
+ key = key.gsub(/[<>:\\|?*%]/) {|c| "%%%03d" % Sass::Util.ord(c)}
56
57
  File.join(cache_location, key)
57
58
  end
58
59
  end
@@ -36,7 +36,7 @@ module Sass
36
36
  # @return [String] A string representation of the function call
37
37
  def inspect
38
38
  args = @args.map {|a| a.inspect}.join(', ')
39
- keywords = @keywords.sort_by {|k, v| k}.
39
+ keywords = Sass::Util.hash_to_a(@keywords).
40
40
  map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
41
41
  "#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
42
42
  end
@@ -44,7 +44,7 @@ module Sass
44
44
  # @see Node#to_sass
45
45
  def to_sass(opts = {})
46
46
  args = @args.map {|a| a.to_sass(opts)}.join(', ')
47
- keywords = @keywords.sort_by {|k, v| k}.
47
+ keywords = Sass::Util.hash_to_a(@keywords).
48
48
  map {|k, v| "$#{dasherize(k, opts)}: #{v.to_sass(opts)}"}.join(', ')
49
49
  "#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
50
50
  end
@@ -55,26 +55,6 @@ The #options attribute is not set on this #{self.class}.
55
55
  MSG
56
56
  end
57
57
 
58
- # The SassScript `and` operation.
59
- #
60
- # @param other [Literal] The right-hand side of the operator
61
- # @return [Literal] The result of a logical and:
62
- # `other` if this literal isn't a false {Bool},
63
- # and this literal otherwise
64
- def and(other)
65
- to_bool ? other : self
66
- end
67
-
68
- # The SassScript `or` operation.
69
- #
70
- # @param other [Literal] The right-hand side of the operator
71
- # @return [Literal] The result of the logical or:
72
- # this literal if it isn't a false {Bool},
73
- # and `other` otherwise
74
- def or(other)
75
- to_bool ? self : other
76
- end
77
-
78
58
  # The SassScript `==` operation.
79
59
  # **Note that this returns a {Sass::Script::Bool} object,
80
60
  # not a Ruby boolean**.
@@ -72,6 +72,14 @@ module Sass::Script
72
72
  # @raise [Sass::SyntaxError] if the operation is undefined for the operands
73
73
  def _perform(environment)
74
74
  literal1 = @operand1.perform(environment)
75
+
76
+ # Special-case :and and :or to support short-circuiting.
77
+ if @operator == :and
78
+ return literal1.to_bool ? @operand2.perform(environment) : literal1
79
+ elsif @operator == :or
80
+ return literal1.to_bool ? literal1 : @operand2.perform(environment)
81
+ end
82
+
75
83
  literal2 = @operand2.perform(environment)
76
84
 
77
85
  begin
@@ -379,7 +379,7 @@ module Sass
379
379
  attr_reader :selector
380
380
 
381
381
  # @param [String] The name of the pseudoclass
382
- # @param [Selector::Sequence] The selector argument
382
+ # @param [Selector::CommaSequence] The selector argument
383
383
  def initialize(name, selector)
384
384
  @name = name
385
385
  @selector = selector
@@ -124,15 +124,15 @@ module Sass
124
124
  # @param path [Array<Array<SimpleSequence or String>>] A list of parenthesized selector groups.
125
125
  # @return [Array<Array<SimpleSequence or String>>] A list of fully-expanded selectors.
126
126
  def weave(path)
127
+ # This function works by moving through the selector path left-to-right,
128
+ # building all possible prefixes simultaneously. These prefixes are
129
+ # `befores`, while the remaining parenthesized suffixes is `afters`.
127
130
  befores = [[]]
128
131
  afters = path.dup
129
132
 
130
133
  until afters.empty?
131
134
  current = afters.shift.dup
132
135
  last_current = [current.pop]
133
- while !current.empty? && last_current.first.is_a?(String) || current.last.is_a?(String)
134
- last_current.unshift(current.pop)
135
- end
136
136
  befores = Sass::Util.flatten(befores.map do |before|
137
137
  next [] unless sub = subweave(before, current)
138
138
  sub.map {|seqs| seqs + last_current}
@@ -150,6 +150,12 @@ module Sass
150
150
  # `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,
151
151
  # and so on until `.baz .bang .foo .bar`.
152
152
  #
153
+ # Semantically, for selectors A and B, this returns all selectors `AB_i`
154
+ # such that the union over all i of elements matched by `AB_i X` is
155
+ # identical to the intersection of all elements matched by `A X` and all
156
+ # elements matched by `B X`. Some `AB_i` are elided to reduce the size of
157
+ # the output.
158
+ #
153
159
  # @param seq1 [Array<SimpleSequence or String>]
154
160
  # @param seq2 [Array<SimpleSequence or String>]
155
161
  # @return [Array<Array<SimpleSequence or String>>]
@@ -157,7 +163,9 @@ module Sass
157
163
  return [seq2] if seq1.empty?
158
164
  return [seq1] if seq2.empty?
159
165
 
166
+ seq1, seq2 = seq1.dup, seq2.dup
160
167
  return unless init = merge_initial_ops(seq1, seq2)
168
+ return unless fin = merge_final_ops(seq1, seq2)
161
169
  seq1 = group_selectors(seq1)
162
170
  seq2 = group_selectors(seq2)
163
171
  lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
@@ -174,14 +182,15 @@ module Sass
174
182
  seq2.shift
175
183
  end
176
184
  diff << chunks(seq1, seq2) {|s| s.empty?}
185
+ diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}
177
186
  diff.reject! {|c| c.empty?}
178
187
 
179
188
  Sass::Util.paths(diff).map {|p| p.flatten}
180
189
  end
181
190
 
182
- # Extracts initial selector operators (`"+"`, `">"`, `"~"`, and `"\n"`)
191
+ # Extracts initial selector combinators (`"+"`, `">"`, `"~"`, and `"\n"`)
183
192
  # from two sequences and merges them together into a single array of
184
- # selector operators.
193
+ # selector combinators.
185
194
  #
186
195
  # @param seq1 [Array<SimpleSequence or String>]
187
196
  # @param seq2 [Array<SimpleSequence or String>]
@@ -204,6 +213,100 @@ module Sass
204
213
  return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
205
214
  end
206
215
 
216
+ # Extracts final selector combinators (`"+"`, `">"`, `"~"`) and the
217
+ # selectors to which they apply from two sequences and merges them
218
+ # together into a single array.
219
+ #
220
+ # @param seq1 [Array<SimpleSequence or String>]
221
+ # @param seq2 [Array<SimpleSequence or String>]
222
+ # @return [Array<SimpleSequence or String or
223
+ # Array<Array<SimpleSequence or String>>]
224
+ # If there are no trailing combinators to be merged, this will be the
225
+ # empty array. If the trailing combinators cannot be merged, this will
226
+ # be nil. Otherwise, this will contained the merged selector. Array
227
+ # elements are [Sass::Util#paths]-style options; conceptually, an "or"
228
+ # of multiple selectors.
229
+ def merge_final_ops(seq1, seq2, res = [])
230
+ ops1, ops2 = [], []
231
+ ops1 << seq1.pop while seq1.last.is_a?(String)
232
+ ops2 << seq2.pop while seq2.last.is_a?(String)
233
+
234
+ # Not worth the headache of trying to preserve newlines here. The most
235
+ # important use of newlines is at the beginning of the selector to wrap
236
+ # across lines anyway.
237
+ ops1.reject! {|o| o == "\n"}
238
+ ops2.reject! {|o| o == "\n"}
239
+
240
+ return res if ops1.empty? && ops2.empty?
241
+ if ops1.size > 1 || ops2.size > 1
242
+ # If there are multiple operators, something hacky's going on. If one
243
+ # is a supersequence of the other, use that, otherwise give up.
244
+ lcs = Sass::Util.lcs(ops1, ops2)
245
+ return unless lcs == ops1 || lcs == ops2
246
+ res.unshift *(ops1.size > ops2.size ? ops1 : ops2).reverse
247
+ return res
248
+ end
249
+
250
+ # This code looks complicated, but it's actually just a bunch of special
251
+ # cases for interactions between different combinators.
252
+ op1, op2 = ops1.first, ops2.first
253
+ if op1 && op2
254
+ sel1 = seq1.pop
255
+ sel2 = seq2.pop
256
+ if op1 == '~' && op2 == '~'
257
+ if subweave_superselector?([sel1], [sel2])
258
+ res.unshift sel2, '~'
259
+ elsif subweave_superselector?([sel2], [sel1])
260
+ res.unshift sel1, '~'
261
+ else
262
+ merged = sel1.unify(sel2.members)
263
+ res.unshift [
264
+ [sel1, '~', sel2, '~'],
265
+ [sel2, '~', sel1, '~'],
266
+ ([merged, '~'] if merged)
267
+ ].compact
268
+ end
269
+ elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~')
270
+ if op1 == '~'
271
+ tilde_sel, plus_sel = sel1, sel2
272
+ else
273
+ tilde_sel, plus_sel = sel2, sel1
274
+ end
275
+
276
+ if subweave_superselector?([tilde_sel], [plus_sel])
277
+ res.unshift plus_sel, '+'
278
+ else
279
+ merged = plus_sel.unify(tilde_sel.members)
280
+ res.unshift [
281
+ [tilde_sel, '~', plus_sel, '+'],
282
+ ([merged, '+'] if merged)
283
+ ].compact
284
+ end
285
+ elsif op1 == '>' && %w[~ +].include?(op2)
286
+ res.unshift sel2, op2
287
+ seq1.push sel1, op1
288
+ elsif op2 == '>' && %w[~ +].include?(op1)
289
+ res.unshift sel1, op1
290
+ seq2.push sel2, op2
291
+ elsif op1 == op2
292
+ return unless merged = sel1.unify(sel2.members)
293
+ res.unshift merged, op1
294
+ else
295
+ # Unknown selector combinators can't be unified
296
+ return
297
+ end
298
+ return merge_final_ops(seq1, seq2, res)
299
+ elsif op1
300
+ seq2.pop if op1 == '>' && seq2.last && subweave_superselector?([seq2.last], [seq1.last])
301
+ res.unshift seq1.pop, op1
302
+ return merge_final_ops(seq1, seq2, res)
303
+ else # op2
304
+ seq1.pop if op2 == '>' && seq1.last && subweave_superselector?([seq1.last], [seq2.last])
305
+ res.unshift seq2.pop, op2
306
+ return merge_final_ops(seq1, seq2, res)
307
+ end
308
+ end
309
+
207
310
  # Takes initial subsequences of `seq1` and `seq2` and returns all
208
311
  # orderings of those subsequences. The initial subsequences are determined
209
312
  # by a block.
@@ -223,6 +326,7 @@ module Sass
223
326
  # cutting off some initial subsequence.
224
327
  # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence
225
328
  # here.
329
+ # @return [Array<Array>] All possible orderings of the initial subsequences.
226
330
  def chunks(seq1, seq2)
227
331
  chunk1 = []
228
332
  chunk1 << seq1.shift until yield seq1
@@ -257,24 +361,32 @@ module Sass
257
361
  end
258
362
 
259
363
  # Given two sequences of simple selectors, returns whether `sseq1` is a
260
- # superselector of `sseq2`.
364
+ # superselector of `sseq2`; that is, whether `sseq1` matches every element
365
+ # `sseq2` matches.
366
+ #
367
+ # Both `sseq1` and `sseq2` are of the form
368
+ # `SimpleSelector (String SimpleSelector)* String*`, although selectors
369
+ # with a trailing operator are considered to be neither superselectors nor
370
+ # subselectors.
261
371
  #
262
- # @param sseq1 [Array<SimpleSelector or String>]
263
- # @param sseq2 [Array<SimpleSelector or String>]
372
+ # @param sseq1 [Array<SimpleSequence or String>]
373
+ # @param sseq2 [Array<SimpleSequence or String>]
264
374
  # @return [Boolean]
265
375
  def subweave_superselector?(sseq1, sseq2)
376
+ sseq1 = sseq1.reject {|e| e == "\n"}
377
+ sseq2 = sseq2.reject {|e| e == "\n"}
378
+ # Selectors with trailing operators are neither superselectors nor
379
+ # subselectors.
380
+ return if sseq1.last.is_a?(String) || sseq2.last.is_a?(String)
266
381
  if sseq1.size > 1
267
382
  # More complex selectors are never superselectors of less complex ones
268
383
  return unless sseq2.size > 1
269
384
  # .foo ~ .bar is a superselector of .foo + .bar
270
385
  return unless sseq1[1] == "~" ? sseq2[1] != ">" : sseq2[1] == sseq1[1]
271
386
  return unless sseq1.first.superselector?(sseq2.first)
272
- return true if sseq1.size == 2
273
- return false if sseq2.size == 2
274
387
  return subweave_superselector?(sseq1[2..-1], sseq2[2..-1])
275
388
  elsif sseq2.size > 1
276
389
  return true if sseq2[1] == ">" && sseq1.first.superselector?(sseq2.first)
277
- return false if sseq2.size == 2
278
390
  return subweave_superselector?(sseq1, sseq2[2..-1])
279
391
  else
280
392
  sseq1.first.superselector?(sseq2.first)
@@ -168,7 +168,8 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
168
168
  def visit_mixin(node)
169
169
  unless node.args.empty? && node.keywords.empty?
170
170
  args = node.args.map {|a| a.to_sass(@options)}.join(", ")
171
- keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
171
+ keywords = Sass::Util.hash_to_a(node.keywords).
172
+ map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
172
173
  arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
173
174
  end
174
175
  "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
@@ -52,12 +52,27 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
52
52
  def visit_root(node)
53
53
  yield
54
54
 
55
- # In Ruby 1.9 we can make all @charset nodes invisible
56
- # and infer the final @charset from the encoding of the final string.
57
- if Sass::Util.ruby1_8? && parent.nil?
58
- charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
59
- node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
60
- node.children.unshift charset if charset
55
+ if parent.nil?
56
+ # In Ruby 1.9 we can make all @charset nodes invisible
57
+ # and infer the final @charset from the encoding of the final string.
58
+ if Sass::Util.ruby1_8?
59
+ charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
60
+ node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
61
+ node.children.unshift charset if charset
62
+ end
63
+
64
+ imports = Sass::Util.extract!(node.children) do |c|
65
+ c.is_a?(Sass::Tree::DirectiveNode) && !c.is_a?(Sass::Tree::MediaNode) &&
66
+ c.resolved_value =~ /^@import /i
67
+ end
68
+ charset_and_index = Sass::Util.ruby1_8? &&
69
+ node.children.each_with_index.find {|c, _| c.is_a?(Sass::Tree::CharsetNode)}
70
+ if charset_and_index
71
+ index = charset_and_index.last
72
+ node.children = node.children[0..index] + imports + node.children[index+1..-1]
73
+ else
74
+ node.children = imports + node.children
75
+ end
61
76
  end
62
77
 
63
78
  return node, @extends
@@ -247,6 +247,8 @@ END
247
247
  # Runs SassScript interpolation in the selector,
248
248
  # and then parses the result into a {Sass::Selector::CommaSequence}.
249
249
  def visit_rule(node)
250
+ rule = node.rule
251
+ rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
250
252
  parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
251
253
  node.parsed_rules ||= parser.parse_selector
252
254
  if node.options[:trace_selectors]
@@ -67,8 +67,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
67
67
  end
68
68
 
69
69
  def visit_directive(node)
70
+ was_in_directive = @in_directive
70
71
  return node.resolved_value + ";" unless node.has_children
71
72
  return node.resolved_value + " {}" if node.children.empty?
73
+ @in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
72
74
  result = if node.style == :compressed
73
75
  "#{node.resolved_value}{"
74
76
  else
@@ -101,6 +103,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
101
103
  else
102
104
  (node.style == :expanded ? "\n" : " ") + "}\n"
103
105
  end
106
+ ensure
107
+ @in_directive = was_in_directive
104
108
  end
105
109
 
106
110
  def visit_media(node)
@@ -133,7 +137,11 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
133
137
  joined_rules = node.resolved_rules.members.map do |seq|
134
138
  next if seq.has_placeholder?
135
139
  rule_part = seq.to_a.join
136
- rule_part.gsub!(/\s*([^,])\s*\n\s*/m, '\1 ') if node.style == :compressed
140
+ if node.style == :compressed
141
+ rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
142
+ rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
143
+ rule_part.strip!
144
+ end
137
145
  rule_part
138
146
  end.compact.join(rule_separator)
139
147
 
@@ -145,7 +153,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
145
153
  old_spaces = ' ' * @tabs
146
154
  spaces = ' ' * (@tabs + 1)
147
155
  if node.style != :compressed
148
- if node.options[:debug_info]
156
+ if node.options[:debug_info] && !@in_directive
149
157
  to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
150
158
  elsif node.options[:trace_selectors]
151
159
  to_return << "#{old_spaces}/* "
@@ -191,7 +199,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
191
199
 
192
200
  def debug_info_rule(debug_info, options)
193
201
  node = Sass::Tree::DirectiveNode.resolved("@media -sass-debug-info")
194
- debug_info.map {|k, v| [k.to_s, v.to_s]}.sort.each do |k, v|
202
+ Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
195
203
  rule = Sass::Tree::RuleNode.new([""])
196
204
  rule.resolved_rules = Sass::Selector::CommaSequence.new(
197
205
  [Sass::Selector::Sequence.new(
@@ -218,6 +218,20 @@ module Sass
218
218
  lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
219
219
  end
220
220
 
221
+ # Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
222
+ # with the following exceptions:
223
+ #
224
+ # * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
225
+ # * In Ruby 1.9 when running tests, this is ordered in the same way it would
226
+ # be under Ruby 1.8 (sorted key order rather than insertion order).
227
+ #
228
+ # @param hash [Hash]
229
+ # @return [Array]
230
+ def hash_to_a(hash)
231
+ return hash.to_a unless ruby1_8? || defined?(Test::Unit)
232
+ return hash.sort_by {|k, v| k}
233
+ end
234
+
221
235
  # Returns information about the caller of the previous method.
222
236
  #
223
237
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
@@ -561,6 +575,24 @@ MSG
561
575
  ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
562
576
  end
563
577
 
578
+ # Destructively removes all elements from an array that match a block, and
579
+ # returns the removed elements.
580
+ #
581
+ # @param array [Array] The array from which to remove elements.
582
+ # @yield [el] Called for each element.
583
+ # @yieldparam el [*] The element to test.
584
+ # @yieldreturn [Boolean] Whether or not to extract the element.
585
+ # @return [Array] The extracted elements.
586
+ def extract!(array)
587
+ out = []
588
+ array.reject! do |e|
589
+ next false unless yield e
590
+ out << e
591
+ true
592
+ end
593
+ out
594
+ end
595
+
564
596
  # Returns the ASCII code of the given character.
565
597
  #
566
598
  # @param c [String] All characters but the first are ignored.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
- require 'test_helper'
4
- require 'sass/test_helper'
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require File.dirname(__FILE__) + '/test_helper'
5
5
  require 'sass/engine'
6
6
  require 'stringio'
7
7
  require 'mock_importer'
@@ -1028,6 +1028,23 @@ foo
1028
1028
  SASS
1029
1029
  end
1030
1030
 
1031
+ def test_debug_info_in_keyframes
1032
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true))
1033
+ @-webkit-keyframes warm {
1034
+ from {
1035
+ color: black; }
1036
+
1037
+ to {
1038
+ color: red; } }
1039
+ CSS
1040
+ @-webkit-keyframes warm
1041
+ from
1042
+ color: black
1043
+ to
1044
+ color: red
1045
+ SASS
1046
+ end
1047
+
1031
1048
  def test_empty_first_line
1032
1049
  assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
1033
1050
  end
@@ -2562,6 +2579,15 @@ CSS
2562
2579
  SASS
2563
2580
  end
2564
2581
 
2582
+ def test_selector_compression
2583
+ assert_equal <<CSS, render(<<SASS, :style => :compressed)
2584
+ a>b,c+d,:-moz-any(e,f,g){h:i}
2585
+ CSS
2586
+ a > b, c + d, :-moz-any(e, f, g)
2587
+ h: i
2588
+ SASS
2589
+ end
2590
+
2565
2591
  # Encodings
2566
2592
 
2567
2593
  unless Sass::Util.ruby1_8?
@@ -1165,6 +1165,70 @@ SCSS
1165
1165
  CSS
1166
1166
  .baz.foo {a: b}
1167
1167
  foo > bar {@extend .foo}
1168
+ SCSS
1169
+
1170
+ assert_equal <<CSS, render(<<SCSS)
1171
+ .baz > .foo, .baz > .bar {
1172
+ a: b; }
1173
+ CSS
1174
+ .baz > {
1175
+ .foo {a: b}
1176
+ .bar {@extend .foo}
1177
+ }
1178
+ SCSS
1179
+
1180
+ assert_equal <<CSS, render(<<SCSS)
1181
+ .foo .bar, .foo > .baz {
1182
+ a: b; }
1183
+ CSS
1184
+ .foo {
1185
+ .bar {a: b}
1186
+ > .baz {@extend .bar}
1187
+ }
1188
+ SCSS
1189
+
1190
+ assert_equal <<CSS, render(<<SCSS)
1191
+ .foo .bar, .foo .bip > .baz {
1192
+ a: b; }
1193
+ CSS
1194
+ .foo {
1195
+ .bar {a: b}
1196
+ .bip > .baz {@extend .bar}
1197
+ }
1198
+ SCSS
1199
+
1200
+ assert_equal <<CSS, render(<<SCSS)
1201
+ .foo .bip .bar, .foo .bip .foo > .baz {
1202
+ a: b; }
1203
+ CSS
1204
+ .foo {
1205
+ .bip .bar {a: b}
1206
+ > .baz {@extend .bar}
1207
+ }
1208
+ SCSS
1209
+
1210
+ assert_equal <<CSS, render(<<SCSS)
1211
+ .foo > .bar, .foo > .bip + .baz {
1212
+ a: b; }
1213
+ CSS
1214
+ .foo > .bar {a: b}
1215
+ .bip + .baz {@extend .bar}
1216
+ SCSS
1217
+
1218
+ assert_equal <<CSS, render(<<SCSS)
1219
+ .foo + .bar, .bip > .foo + .baz {
1220
+ a: b; }
1221
+ CSS
1222
+ .foo + .bar {a: b}
1223
+ .bip > .baz {@extend .bar}
1224
+ SCSS
1225
+
1226
+ assert_equal <<CSS, render(<<SCSS)
1227
+ .foo > .bar, .bip.foo > .baz {
1228
+ a: b; }
1229
+ CSS
1230
+ .foo > .bar {a: b}
1231
+ .bip > .baz {@extend .bar}
1168
1232
  SCSS
1169
1233
  end
1170
1234
 
@@ -1186,7 +1250,7 @@ SCSS
1186
1250
 
1187
1251
  def test_nested_extender_with_hacky_selector
1188
1252
  assert_equal <<CSS, render(<<SCSS)
1189
- .baz .foo, .baz foo + > > + bar {
1253
+ .baz .foo, .baz foo + > > + bar, foo .baz + > > + bar {
1190
1254
  a: b; }
1191
1255
  CSS
1192
1256
  .baz .foo {a: b}
@@ -1194,7 +1258,7 @@ foo + > > + bar {@extend .foo}
1194
1258
  SCSS
1195
1259
 
1196
1260
  assert_equal <<CSS, render(<<SCSS)
1197
- .baz .foo, .baz > > bar {
1261
+ .baz .foo, > > .baz bar {
1198
1262
  a: b; }
1199
1263
  CSS
1200
1264
  .baz .foo {a: b}
@@ -1223,6 +1287,402 @@ CSS
1223
1287
  SCSS
1224
1288
  end
1225
1289
 
1290
+ # Combinator Unification
1291
+
1292
+ def test_combinator_unification_for_hacky_combinators
1293
+ assert_equal <<CSS, render(<<SCSS)
1294
+ .a > + x, .a .b > + y, .b .a > + y {
1295
+ a: b; }
1296
+ CSS
1297
+ .a > + x {a: b}
1298
+ .b y {@extend x}
1299
+ SCSS
1300
+
1301
+ assert_equal <<CSS, render(<<SCSS)
1302
+ .a x, .a .b > + y, .b .a > + y {
1303
+ a: b; }
1304
+ CSS
1305
+ .a x {a: b}
1306
+ .b > + y {@extend x}
1307
+ SCSS
1308
+
1309
+ assert_equal <<CSS, render(<<SCSS)
1310
+ .a > + x, .a .b > + y, .b .a > + y {
1311
+ a: b; }
1312
+ CSS
1313
+ .a > + x {a: b}
1314
+ .b > + y {@extend x}
1315
+ SCSS
1316
+
1317
+ assert_equal <<CSS, render(<<SCSS)
1318
+ .a ~ > + x, .a .b ~ > + y, .b .a ~ > + y {
1319
+ a: b; }
1320
+ CSS
1321
+ .a ~ > + x {a: b}
1322
+ .b > + y {@extend x}
1323
+ SCSS
1324
+
1325
+ assert_equal <<CSS, render(<<SCSS)
1326
+ .a + > x {
1327
+ a: b; }
1328
+ CSS
1329
+ .a + > x {a: b}
1330
+ .b > + y {@extend x}
1331
+ SCSS
1332
+
1333
+ assert_equal <<CSS, render(<<SCSS)
1334
+ .a + > x {
1335
+ a: b; }
1336
+ CSS
1337
+ .a + > x {a: b}
1338
+ .b > + y {@extend x}
1339
+ SCSS
1340
+
1341
+ assert_equal <<CSS, render(<<SCSS)
1342
+ .a ~ > + .b > x, .a .c ~ > + .d.b > y, .c .a ~ > + .d.b > y {
1343
+ a: b; }
1344
+ CSS
1345
+ .a ~ > + .b > x {a: b}
1346
+ .c > + .d > y {@extend x}
1347
+ SCSS
1348
+ end
1349
+
1350
+ def test_combinator_unification_double_tilde
1351
+ assert_equal <<CSS, render(<<SCSS)
1352
+ .a.b ~ x, .a.b ~ y {
1353
+ a: b; }
1354
+ CSS
1355
+ .a.b ~ x {a: b}
1356
+ .a ~ y {@extend x}
1357
+ SCSS
1358
+
1359
+ assert_equal <<CSS, render(<<SCSS)
1360
+ .a ~ x, .a.b ~ y {
1361
+ a: b; }
1362
+ CSS
1363
+ .a ~ x {a: b}
1364
+ .a.b ~ y {@extend x}
1365
+ SCSS
1366
+
1367
+ assert_equal <<CSS, render(<<SCSS)
1368
+ .a ~ x, .a ~ .b ~ y, .b ~ .a ~ y, .b.a ~ y {
1369
+ a: b; }
1370
+ CSS
1371
+ .a ~ x {a: b}
1372
+ .b ~ y {@extend x}
1373
+ SCSS
1374
+
1375
+ assert_equal <<CSS, render(<<SCSS)
1376
+ a.a ~ x, a.a ~ b.b ~ y, b.b ~ a.a ~ y {
1377
+ a: b; }
1378
+ CSS
1379
+ a.a ~ x {a: b}
1380
+ b.b ~ y {@extend x}
1381
+ SCSS
1382
+ end
1383
+
1384
+ def test_combinator_unification_tilde_plus
1385
+ assert_equal <<CSS, render(<<SCSS)
1386
+ .a.b + x, .a.b + y {
1387
+ a: b; }
1388
+ CSS
1389
+ .a.b + x {a: b}
1390
+ .a ~ y {@extend x}
1391
+ SCSS
1392
+
1393
+ assert_equal <<CSS, render(<<SCSS)
1394
+ .a + x, .a.b ~ .a + y, .a.b + y {
1395
+ a: b; }
1396
+ CSS
1397
+ .a + x {a: b}
1398
+ .a.b ~ y {@extend x}
1399
+ SCSS
1400
+
1401
+ assert_equal <<CSS, render(<<SCSS)
1402
+ .a + x, .b ~ .a + y, .b.a + y {
1403
+ a: b; }
1404
+ CSS
1405
+ .a + x {a: b}
1406
+ .b ~ y {@extend x}
1407
+ SCSS
1408
+
1409
+ assert_equal <<CSS, render(<<SCSS)
1410
+ a.a + x, b.b ~ a.a + y {
1411
+ a: b; }
1412
+ CSS
1413
+ a.a + x {a: b}
1414
+ b.b ~ y {@extend x}
1415
+ SCSS
1416
+
1417
+ assert_equal <<CSS, render(<<SCSS)
1418
+ .a.b ~ x, .a.b ~ .a + y, .a.b + y {
1419
+ a: b; }
1420
+ CSS
1421
+ .a.b ~ x {a: b}
1422
+ .a + y {@extend x}
1423
+ SCSS
1424
+
1425
+ assert_equal <<CSS, render(<<SCSS)
1426
+ .a ~ x, .a.b + y {
1427
+ a: b; }
1428
+ CSS
1429
+ .a ~ x {a: b}
1430
+ .a.b + y {@extend x}
1431
+ SCSS
1432
+
1433
+ assert_equal <<CSS, render(<<SCSS)
1434
+ .a ~ x, .a ~ .b + y, .a.b + y {
1435
+ a: b; }
1436
+ CSS
1437
+ .a ~ x {a: b}
1438
+ .b + y {@extend x}
1439
+ SCSS
1440
+
1441
+ assert_equal <<CSS, render(<<SCSS)
1442
+ a.a ~ x, a.a ~ b.b + y {
1443
+ a: b; }
1444
+ CSS
1445
+ a.a ~ x {a: b}
1446
+ b.b + y {@extend x}
1447
+ SCSS
1448
+ end
1449
+
1450
+ def test_combinator_unification_angle_sibling
1451
+ assert_equal <<CSS, render(<<SCSS)
1452
+ .a > x, .a > .b ~ y {
1453
+ a: b; }
1454
+ CSS
1455
+ .a > x {a: b}
1456
+ .b ~ y {@extend x}
1457
+ SCSS
1458
+
1459
+ assert_equal <<CSS, render(<<SCSS)
1460
+ .a > x, .a > .b + y {
1461
+ a: b; }
1462
+ CSS
1463
+ .a > x {a: b}
1464
+ .b + y {@extend x}
1465
+ SCSS
1466
+
1467
+ assert_equal <<CSS, render(<<SCSS)
1468
+ .a ~ x, .b > .a ~ y {
1469
+ a: b; }
1470
+ CSS
1471
+ .a ~ x {a: b}
1472
+ .b > y {@extend x}
1473
+ SCSS
1474
+
1475
+ assert_equal <<CSS, render(<<SCSS)
1476
+ .a + x, .b > .a + y {
1477
+ a: b; }
1478
+ CSS
1479
+ .a + x {a: b}
1480
+ .b > y {@extend x}
1481
+ SCSS
1482
+ end
1483
+
1484
+ def test_combinator_unification_double_angle
1485
+ assert_equal <<CSS, render(<<SCSS)
1486
+ .a.b > x, .b.a > y {
1487
+ a: b; }
1488
+ CSS
1489
+ .a.b > x {a: b}
1490
+ .b > y {@extend x}
1491
+ SCSS
1492
+
1493
+ assert_equal <<CSS, render(<<SCSS)
1494
+ .a > x, .a.b > y {
1495
+ a: b; }
1496
+ CSS
1497
+ .a > x {a: b}
1498
+ .a.b > y {@extend x}
1499
+ SCSS
1500
+
1501
+ assert_equal <<CSS, render(<<SCSS)
1502
+ .a > x, .b.a > y {
1503
+ a: b; }
1504
+ CSS
1505
+ .a > x {a: b}
1506
+ .b > y {@extend x}
1507
+ SCSS
1508
+
1509
+ assert_equal <<CSS, render(<<SCSS)
1510
+ a.a > x {
1511
+ a: b; }
1512
+ CSS
1513
+ a.a > x {a: b}
1514
+ b.b > y {@extend x}
1515
+ SCSS
1516
+ end
1517
+
1518
+ def test_combinator_unification_double_plus
1519
+ assert_equal <<CSS, render(<<SCSS)
1520
+ .a.b + x, .b.a + y {
1521
+ a: b; }
1522
+ CSS
1523
+ .a.b + x {a: b}
1524
+ .b + y {@extend x}
1525
+ SCSS
1526
+
1527
+ assert_equal <<CSS, render(<<SCSS)
1528
+ .a + x, .a.b + y {
1529
+ a: b; }
1530
+ CSS
1531
+ .a + x {a: b}
1532
+ .a.b + y {@extend x}
1533
+ SCSS
1534
+
1535
+ assert_equal <<CSS, render(<<SCSS)
1536
+ .a + x, .b.a + y {
1537
+ a: b; }
1538
+ CSS
1539
+ .a + x {a: b}
1540
+ .b + y {@extend x}
1541
+ SCSS
1542
+
1543
+ assert_equal <<CSS, render(<<SCSS)
1544
+ a.a + x {
1545
+ a: b; }
1546
+ CSS
1547
+ a.a + x {a: b}
1548
+ b.b + y {@extend x}
1549
+ SCSS
1550
+ end
1551
+
1552
+ def test_combinator_unification_angle_space
1553
+ assert_equal <<CSS, render(<<SCSS)
1554
+ .a.b > x, .a.b > y {
1555
+ a: b; }
1556
+ CSS
1557
+ .a.b > x {a: b}
1558
+ .a y {@extend x}
1559
+ SCSS
1560
+
1561
+ assert_equal <<CSS, render(<<SCSS)
1562
+ .a > x, .a.b .a > y {
1563
+ a: b; }
1564
+ CSS
1565
+ .a > x {a: b}
1566
+ .a.b y {@extend x}
1567
+ SCSS
1568
+
1569
+ assert_equal <<CSS, render(<<SCSS)
1570
+ .a > x, .b .a > y {
1571
+ a: b; }
1572
+ CSS
1573
+ .a > x {a: b}
1574
+ .b y {@extend x}
1575
+ SCSS
1576
+
1577
+ assert_equal <<CSS, render(<<SCSS)
1578
+ .a.b x, .a.b .a > y {
1579
+ a: b; }
1580
+ CSS
1581
+ .a.b x {a: b}
1582
+ .a > y {@extend x}
1583
+ SCSS
1584
+
1585
+ assert_equal <<CSS, render(<<SCSS)
1586
+ .a x, .a.b > y {
1587
+ a: b; }
1588
+ CSS
1589
+ .a x {a: b}
1590
+ .a.b > y {@extend x}
1591
+ SCSS
1592
+
1593
+ assert_equal <<CSS, render(<<SCSS)
1594
+ .a x, .a .b > y {
1595
+ a: b; }
1596
+ CSS
1597
+ .a x {a: b}
1598
+ .b > y {@extend x}
1599
+ SCSS
1600
+ end
1601
+
1602
+ def test_combinator_unification_plus_space
1603
+ assert_equal <<CSS, render(<<SCSS)
1604
+ .a.b + x, .a .a.b + y {
1605
+ a: b; }
1606
+ CSS
1607
+ .a.b + x {a: b}
1608
+ .a y {@extend x}
1609
+ SCSS
1610
+
1611
+ assert_equal <<CSS, render(<<SCSS)
1612
+ .a + x, .a.b .a + y {
1613
+ a: b; }
1614
+ CSS
1615
+ .a + x {a: b}
1616
+ .a.b y {@extend x}
1617
+ SCSS
1618
+
1619
+ assert_equal <<CSS, render(<<SCSS)
1620
+ .a + x, .b .a + y {
1621
+ a: b; }
1622
+ CSS
1623
+ .a + x {a: b}
1624
+ .b y {@extend x}
1625
+ SCSS
1626
+
1627
+ assert_equal <<CSS, render(<<SCSS)
1628
+ .a.b x, .a.b .a + y {
1629
+ a: b; }
1630
+ CSS
1631
+ .a.b x {a: b}
1632
+ .a + y {@extend x}
1633
+ SCSS
1634
+
1635
+ assert_equal <<CSS, render(<<SCSS)
1636
+ .a x, .a .a.b + y {
1637
+ a: b; }
1638
+ CSS
1639
+ .a x {a: b}
1640
+ .a.b + y {@extend x}
1641
+ SCSS
1642
+
1643
+ assert_equal <<CSS, render(<<SCSS)
1644
+ .a x, .a .b + y {
1645
+ a: b; }
1646
+ CSS
1647
+ .a x {a: b}
1648
+ .b + y {@extend x}
1649
+ SCSS
1650
+ end
1651
+
1652
+ def test_combinator_unification_nested
1653
+ assert_equal <<CSS, render(<<SCSS)
1654
+ .a > .b + x, .c.a > .d.b + y {
1655
+ a: b; }
1656
+ CSS
1657
+ .a > .b + x {a: b}
1658
+ .c > .d + y {@extend x}
1659
+ SCSS
1660
+
1661
+ assert_equal <<CSS, render(<<SCSS)
1662
+ .a > .b + x, .c.a > .b + y {
1663
+ a: b; }
1664
+ CSS
1665
+ .a > .b + x {a: b}
1666
+ .c > y {@extend x}
1667
+ SCSS
1668
+ end
1669
+
1670
+ def test_combinator_unification_with_newlines
1671
+ assert_equal <<CSS, render(<<SCSS)
1672
+ .a >
1673
+ .b
1674
+ + x, .c.a > .d.b + y {
1675
+ a: b; }
1676
+ CSS
1677
+ .a >
1678
+ .b
1679
+ + x {a: b}
1680
+ .c
1681
+ > .d +
1682
+ y {@extend x}
1683
+ SCSS
1684
+ end
1685
+
1226
1686
  # Loops
1227
1687
 
1228
1688
  def test_extend_self_loop
@@ -1431,6 +1891,20 @@ SCSS
1431
1891
 
1432
1892
  # Regression Tests
1433
1893
 
1894
+ def test_newline_near_combinator
1895
+ assert_equal <<CSS, render(<<SCSS)
1896
+ .a +
1897
+ .b x, .a +
1898
+ .b .c y, .c .a +
1899
+ .b y {
1900
+ a: b; }
1901
+ CSS
1902
+ .a +
1903
+ .b x {a: b}
1904
+ .c y {@extend x}
1905
+ SCSS
1906
+ end
1907
+
1434
1908
  def test_duplicated_selector_with_newlines
1435
1909
  assert_equal(<<CSS, render(<<SCSS))
1436
1910
  .example-1-1,
@@ -1,3 +1,5 @@
1
+ @import url(basic.css);
2
+ @import url(../results/complex.css);
1
3
  imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
2
4
 
3
5
  body { font: Arial; background: blue; }
@@ -22,8 +24,6 @@ body { font: Arial; background: blue; }
22
24
  #content.user.show #container.top #column.right { width: 600px; }
23
25
  #content.user.show #container.bottom { background: brown; }
24
26
 
25
- @import url(basic.css);
26
- @import url(../results/complex.css);
27
27
  #foo { background-color: #bbaaff; }
28
28
 
29
29
  nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
@@ -1,3 +1,5 @@
1
+ @import url(basic.css);
2
+ @import url(../results/complex.css);
1
3
  imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
2
4
 
3
5
  body { font: Arial; background: blue; }
@@ -24,8 +26,6 @@ body { font: Arial; background: blue; }
24
26
  #content.user.show #container.top #column.right { width: 600px; }
25
27
  #content.user.show #container.bottom { background: brown; }
26
28
 
27
- @import url(basic.css);
28
- @import url(../results/complex.css);
29
29
  #foo { background-color: #bbaaff; }
30
30
 
31
31
  nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
@@ -1,4 +1,5 @@
1
1
  @charset "UTF-8";
2
+ @import url(foo.css);
2
3
  .foo { a: b; }
3
4
 
4
5
  .bar { a: щ; }
@@ -1,4 +1,5 @@
1
1
  @charset "IBM866";
2
+ @import url(foo.css);
2
3
  .foo { a: b; }
3
4
 
4
5
  .bar { a: �; }
@@ -1,4 +1,5 @@
1
1
  @charset "IBM866";
2
+ @import url(foo.css);
2
3
  .foo { a: b; }
3
4
 
4
5
  .bar { a: �; }
@@ -1,3 +1,5 @@
1
+ @import url(basic.css);
2
+ @import url(../results/complex.css);
1
3
  imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
2
4
 
3
5
  body { font: Arial; background: blue; }
@@ -24,8 +26,6 @@ body { font: Arial; background: blue; }
24
26
  #content.user.show #container.top #column.right { width: 600px; }
25
27
  #content.user.show #container.bottom { background: brown; }
26
28
 
27
- @import url(basic.css);
28
- @import url(../results/complex.css);
29
29
  #foo { background-color: #bbaaff; }
30
30
 
31
31
  nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
@@ -432,6 +432,11 @@ SASS
432
432
  assert_raise_message(Sass::SyntaxError, "wrong number of arguments (1 for 0) for `arg-error'") {resolve("arg-error(1)")}
433
433
  end
434
434
 
435
+ def test_boolean_ops_short_circuit
436
+ assert_equal "false", resolve("$ie and $ie <= 7", {}, env('ie' => Sass::Script::Bool.new(false)))
437
+ assert_equal "true", resolve("$ie or $undef", {}, env('ie' => Sass::Script::Bool.new(true)))
438
+ end
439
+
435
440
  # Regression Tests
436
441
 
437
442
  def test_funcall_has_higher_precedence_than_color_name
@@ -1245,11 +1245,11 @@ SCSS
1245
1245
 
1246
1246
 
1247
1247
  def test_newlines_removed_from_selectors_when_compressed
1248
- assert_equal <<CSS, render(<<SCSS, :style=>:compressed)
1248
+ assert_equal <<CSS, render(<<SCSS, :style => :compressed)
1249
1249
  z a,z b{display:block}
1250
1250
  CSS
1251
- a,
1252
- b {
1251
+ a
1252
+ , b {
1253
1253
  z & {
1254
1254
  display: block;
1255
1255
  }
@@ -1,6 +1,8 @@
1
1
  .foo
2
2
  a: b
3
3
 
4
+ @import "foo.css"
5
+
4
6
  // Even though the imported file is in IBM866,
5
7
  // since the root file is in UTF-8/ASCII
6
8
  // the output will end up being UTF-8.
@@ -1,4 +1,6 @@
1
1
  .foo
2
2
  a: b
3
3
 
4
+ @import "foo.css"
5
+
4
6
  @import "imported_charset_ibm866"
@@ -3,6 +3,8 @@
3
3
  .foo
4
4
  a: b
5
5
 
6
+ @import "foo.css"
7
+
6
8
  // Even though the imported file is in UTF-8,
7
9
  // since the root file is in IBM866
8
10
  // the output will end up being IBM866.
@@ -159,6 +159,12 @@ class UtilTest < Test::Unit::TestCase
159
159
  enum_cons(%w[foo bar baz], 2).map {|s1, s2| "#{s1}#{s2}"})
160
160
  end
161
161
 
162
+ def test_extract
163
+ arr = [1, 2, 3, 4, 5]
164
+ assert_equal([1, 3, 5], extract!(arr) {|e| e % 2 == 1})
165
+ assert_equal([2, 4], arr)
166
+ end
167
+
162
168
  def test_ord
163
169
  assert_equal(102, ord("f"))
164
170
  assert_equal(98, ord("bar"))
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- hash: 592302947
4
+ hash: 592303005
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 2
9
9
  - 0
10
10
  - alpha
11
- - 63
12
- version: 3.2.0.alpha.63
11
+ - 64
12
+ version: 3.2.0.alpha.64
13
13
  platform: ruby
14
14
  authors:
15
15
  - Nathan Weizenbaum
@@ -19,7 +19,7 @@ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2012-01-25 00:00:00 -05:00
22
+ date: 2012-02-05 00:00:00 -05:00
23
23
  default_executable:
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
@@ -86,7 +86,7 @@ files:
86
86
  - lib/sass/logger.rb
87
87
  - lib/sass/logger/base.rb
88
88
  - lib/sass/logger/log_level.rb
89
- - lib/sass/media.rb
89
+ - lib/sass/util.rb
90
90
  - lib/sass/plugin.rb
91
91
  - lib/sass/plugin/compiler.rb
92
92
  - lib/sass/plugin/configuration.rb
@@ -163,7 +163,7 @@ files:
163
163
  - lib/sass/tree/visitors/to_css.rb
164
164
  - lib/sass/tree/warn_node.rb
165
165
  - lib/sass/tree/while_node.rb
166
- - lib/sass/util.rb
166
+ - lib/sass/media.rb
167
167
  - lib/sass/util/multibyte_string_scanner.rb
168
168
  - lib/sass/util/subset_map.rb
169
169
  - lib/sass/version.rb