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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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