haml 3.0.0.rc.1 → 3.0.0.rc.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

data/REMEMBER CHANGED
@@ -1,6 +1,5 @@
1
1
  Be smart about default namespaces (http://www.w3.org/TR/css3-namespace/#declaration)
2
- Pseudo-selectors should go to the end w/@extend
3
2
  Extending nested selectors?
4
3
 
5
- .hoverlink {@extend a; @extend :hover}
6
- a.user:hover {text-decoration: underline}
4
+ Less
5
+ including mixins across imports
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.0.rc.1
1
+ 3.0.0.rc.2
@@ -58,8 +58,8 @@ module Haml
58
58
  # SyntaxErrors have weird line reporting
59
59
  # when there's trailing whitespace,
60
60
  # which there is for Haml documents.
61
- return exception.message.scan(/:(\d+)/).first.first if exception.is_a?(::SyntaxError)
62
- exception.backtrace[0].scan(/:(\d+)/).first.first
61
+ return (exception.message.scan(/:(\d+)/).first || ["??"]).first if exception.is_a?(::SyntaxError)
62
+ (exception.backtrace[0].scan(/:(\d+)/).first || ["??"]).first
63
63
  end
64
64
 
65
65
  # Tells optparse how to parse the arguments
@@ -371,8 +371,9 @@ MSG
371
371
  end
372
372
 
373
373
  dirs, files = @args.map {|name| name.split(':', 2)}.
374
- map {|from, to| [from, to || from.gsub(/\..*?$/, '.css')]}.
375
374
  partition {|i, _| File.directory? i}
375
+ files.map! {|from, to| [from, to || from.gsub(/\..*?$/, '.css')]}
376
+ dirs.map! {|from, to| [from, to || from]}
376
377
  ::Sass::Plugin.options[:template_location] = dirs
377
378
 
378
379
  ::Sass::Plugin.on_updating_stylesheet do |_, css|
@@ -648,7 +649,7 @@ END
648
649
 
649
650
  super
650
651
  input = @options[:input]
651
- raise "Error: '#{input}' is a directory (did you mean to use --recursive?)" if File.directory?(input)
652
+ raise "Error: '#{input.path}' is a directory (did you mean to use --recursive?)" if File.directory?(input)
652
653
  output = @options[:output]
653
654
  output = input if @options[:in_place]
654
655
  process_file(input, output)
@@ -750,7 +751,8 @@ END
750
751
  output.write(out)
751
752
  rescue ::Sass::SyntaxError => e
752
753
  raise e if @options[:trace]
753
- raise "Syntax error on line #{get_line e}: #{e.message}\n Use --trace for backtrace"
754
+ file = " of #{e.sass_filename}" if e.sass_filename
755
+ raise "Error on line #{e.sass_line}#{file}: #{e.message}\n Use --trace for backtrace"
754
756
  end
755
757
  end
756
758
  end
@@ -189,7 +189,7 @@ module Haml
189
189
  # # [2, 4, 5]]
190
190
  def paths(arrs)
191
191
  arrs.inject([[]]) do |paths, arr|
192
- arr.map {|e| paths.map {|path| path + [e]}}.flatten(1)
192
+ flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
193
193
  end
194
194
  end
195
195
 
@@ -370,6 +370,14 @@ module Haml
370
370
  Haml::Util::RUBY_VERSION[0] == 1 && Haml::Util::RUBY_VERSION[1] < 9
371
371
  end
372
372
 
373
+ # Whether or not this is running under Ruby 1.8.6 or lower.
374
+ # Note that lower versions are not officially supported.
375
+ #
376
+ # @return [Boolean]
377
+ def ruby1_8_6?
378
+ ruby1_8? && Haml::Util::RUBY_VERSION[2] < 7
379
+ end
380
+
373
381
  # Checks that the encoding of a string is valid in Ruby 1.9
374
382
  # and cleans up potential encoding gotchas like the UTF-8 BOM.
375
383
  # If it's not, yields an error string describing the invalid character
@@ -458,6 +466,38 @@ MSG
458
466
  ruby1_8? ? c[0] : c.ord
459
467
  end
460
468
 
469
+ # Flattens the first `n` nested arrays in a cross-version manner.
470
+ #
471
+ # @param arr [Array] The array to flatten
472
+ # @param n [Fixnum] The number of levels to flatten
473
+ # @return [Array] The flattened array
474
+ def flatten(arr, n)
475
+ return arr.flatten(n) unless ruby1_8_6?
476
+ return arr if n == 0
477
+ arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
478
+ end
479
+
480
+ # Returns the hash code for a set in a cross-version manner.
481
+ # Aggravatingly, this is order-dependent in Ruby 1.8.6.
482
+ #
483
+ # @param set [Set]
484
+ # @return [Fixnum] The order-independent hashcode of `set`
485
+ def set_hash(set)
486
+ return set.hash unless ruby1_8_6?
487
+ set.map {|e| e.hash}.uniq.sort.hash
488
+ end
489
+
490
+ # Tests the hash-equality of two sets in a cross-version manner.
491
+ # Aggravatingly, this is order-dependent in Ruby 1.8.6.
492
+ #
493
+ # @param set1 [Set]
494
+ # @param set2 [Set]
495
+ # @return [Boolean] Whether or not the sets are hashcode equal
496
+ def set_eql?(set1, set2)
497
+ return set1.eql?(set2) unless ruby1_8_6?
498
+ set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
499
+ end
500
+
461
501
  ## Static Method Stuff
462
502
 
463
503
  # The context in which the ERB for \{#def\_static\_method} will be run.
@@ -78,7 +78,7 @@ module Haml
78
78
  [index, subenum]
79
79
  end
80
80
  end
81
- res.flatten!(1)
81
+ res = Haml::Util.flatten(res, 1)
82
82
  res.compact!
83
83
  res.uniq!
84
84
  res.sort!
@@ -408,11 +408,15 @@ WARNING
408
408
  return Tree::RuleNode.new(parse_interp(line.text))
409
409
  end
410
410
  res.unshift(hack_char) if hack_char
411
+ if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
412
+ res << comment
413
+ end
411
414
 
412
415
  name = line.text[0...scanner.pos]
413
416
  if scanner.scan(/\s*([:=])(?:\s|$)/)
414
417
  parse_property(name, res, scanner[1], scanner.rest, :new, line)
415
418
  else
419
+ res.pop if comment
416
420
  Tree::RuleNode.new(res + parse_interp(scanner.rest))
417
421
  end
418
422
  end
@@ -77,6 +77,7 @@ module Sass
77
77
  end
78
78
 
79
79
  new_filename = nil
80
+ load_paths = load_paths.uniq
80
81
  load_paths.each do |load_path|
81
82
  new_filename ||= find_full_path("#{filename}.sass", load_path) unless was_scss
82
83
  new_filename ||= find_full_path("#{filename}.scss", load_path) unless was_sass
@@ -149,10 +149,10 @@ module Sass
149
149
  # @return [Boolean]
150
150
  def whitespace?(tok = @tok)
151
151
  if tok
152
- @scanner.string[0...tok.pos] =~ /\s$/
152
+ @scanner.string[0...tok.pos] =~ /\s\Z/
153
153
  else
154
154
  @scanner.string[@scanner.pos, 1] =~ /^\s/ ||
155
- @scanner.string[@scanner.pos - 1, 1] =~ /\s$/
155
+ @scanner.string[@scanner.pos - 1, 1] =~ /\s\Z/
156
156
  end
157
157
  end
158
158
 
@@ -25,6 +25,14 @@ module Sass::Script
25
25
  # @return [Array<String>]
26
26
  attr_reader :denominator_units
27
27
 
28
+ # The original representation of this number.
29
+ # For example, although the result of `1px/2px` is `0.5`,
30
+ # the value of `#original` is `"1px/2px"`.
31
+ #
32
+ # This is only non-nil when the original value should be used as the CSS value,
33
+ # as in `font: 1px/2px`.
34
+ #
35
+ # @return [Boolean, nil]
28
36
  attr_accessor :original
29
37
 
30
38
  # The precision with which numbers will be printed to CSS files.
@@ -538,6 +538,9 @@ module Sass
538
538
  return unless name = interp_ident
539
539
  name = [name] if name.is_a?(String)
540
540
  end
541
+ if comment = tok(COMMENT)
542
+ name << comment
543
+ end
541
544
  ss
542
545
 
543
546
  tok!(/:/)
@@ -15,7 +15,7 @@ module Sass
15
15
  # Used for error reporting
16
16
  # @return [Selector::CommaSequence] The parsed selector
17
17
  # @raise [Sass::SyntaxError] if there's a syntax error in the selector
18
- def parse_selector(line, filename)
18
+ def parse_selector(filename)
19
19
  init_scanner!
20
20
  selectors = [expr!(:_selector)]
21
21
  while tok(/,/)
@@ -25,7 +25,7 @@ module Sass
25
25
  end
26
26
  expected("selector") unless @scanner.eos?
27
27
  seq = Selector::CommaSequence.new(selectors)
28
- seq.line = line
28
+ seq.line = @line
29
29
  seq.filename = filename
30
30
  seq
31
31
  end
@@ -77,10 +77,19 @@ module Sass
77
77
  # These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
78
78
  # @see CommaSequence#do_extend
79
79
  def do_extend(extends, supers = [])
80
- Haml::Util.paths(members.map do |sseq_or_op|
80
+ paths = Haml::Util.paths(members.map do |sseq_or_op|
81
81
  next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
82
- [[sseq_or_op], *sseq_or_op.do_extend(extends, supers).map {|seq| seq.members}]
83
- end).map {|path| weave(path)}.flatten(1).map {|p| Sequence.new(p)}
82
+ extended = sseq_or_op.do_extend(extends, supers)
83
+ choices = extended.map {|seq| seq.members}
84
+ choices.unshift([sseq_or_op]) unless extended.any? {|seq| seq.superselector?(sseq_or_op)}
85
+ choices
86
+ end)
87
+ Haml::Util.flatten(paths.map {|path| weave(path)}, 1).map {|p| Sequence.new(p)}
88
+ end
89
+
90
+ def superselector?(sseq)
91
+ return false unless members.size == 1
92
+ members.last.superselector?(sseq)
84
93
  end
85
94
 
86
95
  # @see Simple#to_a
@@ -134,9 +143,9 @@ module Sass
134
143
  while !current.empty? && last_current.first.is_a?(String) || current.last.is_a?(String)
135
144
  last_current.unshift(current.pop)
136
145
  end
137
- befores = befores.map do |before|
138
- subweave(before, current).map {|seqs| seqs + last_current}
139
- end.flatten(1)
146
+ befores = Haml::Util.flatten(befores.map do |before|
147
+ subweave(before, current).map {|seqs| seqs + last_current}
148
+ end, 1)
140
149
  return befores if afters.empty?
141
150
  end
142
151
  end
@@ -75,10 +75,15 @@ module Sass
75
75
  # this exception will only ever be raised as a result of programmer error
76
76
  def unify(sels)
77
77
  return sels if sels.any? {|sel2| eql?(sel2)}
78
- if sels.last.is_a?(Pseudo) && sels.last.type == :element
79
- return sels[0...-1] + [self, sels.last]
80
- end
81
- sels + [self]
78
+ sels_with_ix = Haml::Util.enum_with_index(sels)
79
+ _, i =
80
+ if self.is_a?(Pseudo) || self.is_a?(Negation)
81
+ sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && sels.last.type == :element}
82
+ else
83
+ sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(Negation)}
84
+ end
85
+ return sels + [self] unless i
86
+ return sels[0...i] + [self] + sels[i..-1]
82
87
  end
83
88
 
84
89
  protected
@@ -62,21 +62,18 @@ module Sass
62
62
  # by extending this selector with `extends`.
63
63
  # @see CommaSequence#do_extend
64
64
  def do_extend(extends, supers = [])
65
- seqs = extends.get(members.to_set).map do |seq, sels|
65
+ extends.get(members.to_set).map do |seq, sels|
66
66
  # If A {@extend B} and C {...},
67
67
  # seq is A, sels is B, and self is C
68
68
 
69
69
  self_without_sel = self.members - sels
70
70
  next unless unified = seq.members.last.unify(self_without_sel)
71
71
  [sels, seq.members[0...-1] + [unified]]
72
- end.compact.map {|sels, seq| [sels, Sequence.new(seq)]}
73
-
74
- seqs.map {|_, seq| seq}.concat(
75
- seqs.map do |sels, seq|
76
- new_seqs = seq.do_extend(extends, supers.unshift(sels))[1..-1]
77
- supers.shift
78
- new_seqs
79
- end.flatten.uniq)
72
+ end.compact.map {|sels, seq| [sels, Sequence.new(seq)]}.map do |sels, seq|
73
+ seqs = seq.do_extend(extends, supers.unshift(sels))
74
+ supers.shift
75
+ seqs
76
+ end.flatten.uniq
80
77
  rescue SystemStackError
81
78
  handle_extend_loop(supers)
82
79
  end
@@ -102,6 +99,10 @@ module Sass
102
99
  SimpleSequence.new(sseq)
103
100
  end
104
101
 
102
+ def superselector?(sseq)
103
+ (base.nil? || base.eql?(sseq.base)) && rest.subset?(sseq.rest)
104
+ end
105
+
105
106
  # @see Simple#to_a
106
107
  def to_a
107
108
  @members.map {|sel| sel.to_a}.flatten
@@ -119,7 +120,7 @@ module Sass
119
120
  #
120
121
  # @return [Fixnum]
121
122
  def hash
122
- [base, rest].hash
123
+ [base, Haml::Util.set_hash(rest)].hash
123
124
  end
124
125
 
125
126
  # Checks equality between this and another object.
@@ -127,7 +128,8 @@ module Sass
127
128
  # @param other [Object] The object to test equality against
128
129
  # @return [Boolean] Whether or not this is equal to `other`
129
130
  def eql?(other)
130
- other.class == self.class && other.base.eql?(self.base) && other.rest.eql?(self.rest)
131
+ other.class == self.class && other.base.eql?(self.base) &&
132
+ Haml::Util.set_eql?(other.rest, self.rest)
131
133
  end
132
134
 
133
135
  private
@@ -25,7 +25,7 @@ module Sass::Tree
25
25
  # @see Node#to_src
26
26
  def to_src(tabs, opts, fmt)
27
27
  res = "#{' ' * tabs}#{value}"
28
- return res + "#{semi fmt}\n" if children.empty?
28
+ return res + "#{semi fmt}\n" unless has_children
29
29
  res + children_to_src(tabs, opts, fmt) + "\n"
30
30
  end
31
31
 
@@ -46,14 +46,19 @@ module Sass::Tree
46
46
 
47
47
  protected
48
48
 
49
+ # @see Node#to_src
50
+ def to_src(tabs, opts, fmt)
51
+ "#{' ' * tabs}@extend #{selector_to_src(@selector, tabs, opts, fmt).lstrip}#{semi fmt}\n"
52
+ end
53
+
49
54
  # Runs SassScript interpolation in the selector,
50
55
  # and then parses the result into a {Sass::Selector::CommaSequence}.
51
56
  #
52
57
  # @param environment [Sass::Environment] The lexical environment containing
53
58
  # variable and mixin values
54
59
  def perform!(environment)
55
- @resolved_selector = Sass::SCSS::CssParser.new(run_interp(@selector, environment)).
56
- parse_selector(self.line, self.filename)
60
+ @resolved_selector = Sass::SCSS::CssParser.new(run_interp(@selector, environment), self.line).
61
+ parse_selector(self.filename)
57
62
  super
58
63
  end
59
64
  end
@@ -43,6 +43,18 @@ module Sass
43
43
  environment.set_mixin(@name, Sass::Mixin.new(@name, @args, environment, children))
44
44
  []
45
45
  end
46
+
47
+ # Returns an error message if the given child node is invalid,
48
+ # and false otherwise.
49
+ #
50
+ # {ExtendNode}s are valid within {MixinDefNode}s.
51
+ #
52
+ # @param child [Tree::Node] A potential child node.
53
+ # @return [Boolean, String] Whether or not the child node is valid,
54
+ # as well as the error message to display if it is invalid
55
+ def invalid_child?(child)
56
+ super unless child.is_a?(ExtendNode)
57
+ end
46
58
  end
47
59
  end
48
60
  end
@@ -400,13 +400,52 @@ module Sass
400
400
  # @param tabs [Fixnum] The amount of tabulation to use for the Sass code
401
401
  # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
402
402
  # @param fmt [Symbol] `:sass` or `:scss`
403
- # @return [String] The Sass or CSS code corresponding to the children
403
+ # @return [String] The Sass or SCSS code corresponding to the children
404
404
  def children_to_src(tabs, opts, fmt)
405
+ return fmt == :sass ? "\n" : " {}\n" if children.empty?
406
+
405
407
  (fmt == :sass ? "\n" : " {\n") +
406
408
  children.map {|c| c.send("to_#{fmt}", tabs + 1, opts)}.join.rstrip +
407
409
  (fmt == :sass ? "\n" : " }\n")
408
410
  end
409
411
 
412
+ # Converts a selector to a Sass or SCSS string.
413
+ #
414
+ # @param sel [Array<String, Sass::Script::Node>] The selector to convert
415
+ # @param tabs [Fixnum] The indentation of the selector
416
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
417
+ # @param fmt [Symbol] `:sass` or `:scss`
418
+ # @return [String] The Sass or SCSS code corresponding to the selector
419
+ def selector_to_src(sel, tabs, opts, fmt)
420
+ fmt == :sass ? selector_to_sass(sel, opts) : selector_to_scss(sel, tabs, opts)
421
+ end
422
+
423
+ # Converts a selector to a Sass string.
424
+ #
425
+ # @param sel [Array<String, Sass::Script::Node>] The selector to convert
426
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
427
+ # @return [String] The Sass code corresponding to the selector
428
+ def selector_to_sass(sel, opts)
429
+ sel.map do |r|
430
+ if r.is_a?(String)
431
+ r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
432
+ else
433
+ "\#{#{r.to_sass(opts)}}"
434
+ end
435
+ end.join
436
+ end
437
+
438
+ # Converts a selector to a SCSS string.
439
+ #
440
+ # @param sel [Array<String, Sass::Script::Node>] The selector to convert
441
+ # @param tabs [Fixnum] The indentation of the selector
442
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
443
+ # @return [String] The SCSS code corresponding to the selector
444
+ def selector_to_scss(sel, tabs, opts)
445
+ sel.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(opts)}}"}.
446
+ join.gsub(/^[ \t]*/, ' ' * tabs)
447
+ end
448
+
410
449
  # Convert any underscores in a string into hyphens,
411
450
  # but only if the `:dasherize` option is set.
412
451
  #
@@ -179,7 +179,6 @@ module Sass::Tree
179
179
  class << self
180
180
  # @private
181
181
  def val_to_sass(value, opts)
182
- return value.to_sass(opts) unless value.context == :equals
183
182
  val_to_sass_comma(value, opts).to_sass(opts)
184
183
  end
185
184
 
@@ -208,7 +207,8 @@ module Sass::Tree
208
207
  def val_to_sass_div(node, opts)
209
208
  unless node.is_a?(Sass::Script::Operation) && node.operator == :div &&
210
209
  node.operand1.is_a?(Sass::Script::Number) &&
211
- node.operand2.is_a?(Sass::Script::Number)
210
+ node.operand2.is_a?(Sass::Script::Number) &&
211
+ (node.context == :equals || !node.operand1.original || !node.operand2.original)
212
212
  return node
213
213
  end
214
214
 
@@ -84,22 +84,14 @@ module Sass::Tree
84
84
 
85
85
  # @see Node#to_sass
86
86
  def to_sass(tabs, opts = {})
87
- name = rule.map do |r|
88
- if r.is_a?(String)
89
- r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
90
- else
91
- "\#{#{r.to_sass(opts)}}"
92
- end
93
- end.join
87
+ name = selector_to_sass(rule, opts)
94
88
  name = "\\" + name if name[0] == ?:
95
89
  name.gsub(/^/, ' ' * tabs) + children_to_src(tabs, opts, :sass)
96
90
  end
97
91
 
98
92
  # @see Node#to_scss
99
93
  def to_scss(tabs, opts = {})
100
- name = rule.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(opts)}}"}.
101
- join.gsub(/^[ \t]*/, ' ' * tabs)
102
-
94
+ name = selector_to_scss(rule, tabs, opts)
103
95
  res = name + children_to_src(tabs, opts, :scss)
104
96
 
105
97
  if children.last.is_a?(CommentNode) && children.last.silent
@@ -191,8 +183,8 @@ module Sass::Tree
191
183
  # @param environment [Sass::Environment] The lexical environment containing
192
184
  # variable and mixin values
193
185
  def perform!(environment)
194
- @parsed_rules = Sass::SCSS::StaticParser.new(run_interp(@rule, environment)).
195
- parse_selector(self.line, self.filename)
186
+ @parsed_rules = Sass::SCSS::StaticParser.new(run_interp(@rule, environment), self.line).
187
+ parse_selector(self.filename)
196
188
  super
197
189
  end
198
190
 
@@ -236,7 +228,7 @@ module Sass::Tree
236
228
  #
237
229
  # {ExtendNode}s are valid within {RuleNode}s.
238
230
  #
239
- # @param child [Tree::Node] A potential child nodecompact.
231
+ # @param child [Tree::Node] A potential child node.
240
232
  # @return [Boolean, String] Whether or not the child node is valid,
241
233
  # as well as the error message to display if it is invalid
242
234
  def invalid_child?(child)
@@ -144,6 +144,50 @@ class UtilTest < Test::Unit::TestCase
144
144
  assert_equal(98, ord("bar"))
145
145
  end
146
146
 
147
+ def test_flatten
148
+ assert_equal([1, 2, 3], flatten([1, 2, 3], 0))
149
+ assert_equal([1, 2, 3], flatten([1, 2, 3], 1))
150
+ assert_equal([1, 2, 3], flatten([1, 2, 3], 2))
151
+
152
+ assert_equal([[1, 2], 3], flatten([[1, 2], 3], 0))
153
+ assert_equal([1, 2, 3], flatten([[1, 2], 3], 1))
154
+ assert_equal([1, 2, 3], flatten([[1, 2], 3], 2))
155
+
156
+ assert_equal([[[1], 2], [3], 4], flatten([[[1], 2], [3], 4], 0))
157
+ assert_equal([[1], 2, 3, 4], flatten([[[1], 2], [3], 4], 1))
158
+ assert_equal([1, 2, 3, 4], flatten([[[1], 2], [3], 4], 2))
159
+ end
160
+
161
+ def test_set_hash
162
+ assert(set_hash(Set[1, 2, 3]) == set_hash(Set[3, 2, 1]))
163
+ assert(set_hash(Set[1, 2, 3]) == set_hash(Set[1, 2, 3]))
164
+
165
+ s1 = Set[]
166
+ s1 << 1
167
+ s1 << 2
168
+ s1 << 3
169
+ s2 = Set[]
170
+ s2 << 3
171
+ s2 << 2
172
+ s2 << 1
173
+ assert(set_hash(s1) == set_hash(s2))
174
+ end
175
+
176
+ def test_set_eql
177
+ assert(set_eql?(Set[1, 2, 3], Set[3, 2, 1]))
178
+ assert(set_eql?(Set[1, 2, 3], Set[1, 2, 3]))
179
+
180
+ s1 = Set[]
181
+ s1 << 1
182
+ s1 << 2
183
+ s1 << 3
184
+ s2 = Set[]
185
+ s2 << 3
186
+ s2 << 2
187
+ s2 << 1
188
+ assert(set_eql?(s1, s2))
189
+ end
190
+
147
191
  def test_caller_info
148
192
  assert_equal(["/tmp/foo.rb", 12, "fizzle"], caller_info("/tmp/foo.rb:12: in `fizzle'"))
149
193
  assert_equal(["/tmp/foo.rb", 12, nil], caller_info("/tmp/foo.rb:12"))
@@ -23,6 +23,19 @@ foo bar {
23
23
  SCSS
24
24
  end
25
25
 
26
+ def test_empty_selector
27
+ assert_renders "foo bar", "foo bar {}"
28
+ end
29
+
30
+ def test_empty_directive
31
+ assert_scss_to_sass "@media screen", "@media screen {}"
32
+ assert_scss_to_scss "@media screen {}"
33
+ end
34
+
35
+ def test_empty_control_directive
36
+ assert_renders "@if false", "@if false {}"
37
+ end
38
+
26
39
  def test_nesting
27
40
  assert_renders <<SASS, <<SCSS
28
41
  foo bar
@@ -727,6 +740,18 @@ SASS
727
740
  SCSS
728
741
  end
729
742
 
743
+ def test_extend
744
+ assert_renders <<SASS, <<SCSS
745
+ .foo
746
+ @extend .bar
747
+ @extend .baz:bang
748
+ SASS
749
+ .foo {
750
+ @extend .bar;
751
+ @extend .baz:bang; }
752
+ SCSS
753
+ end
754
+
730
755
  def test_argless_mixin_definition
731
756
  assert_renders <<SASS, <<SCSS
732
757
  =foo-bar
@@ -863,6 +888,60 @@ SCSS
863
888
  assert_sass_to_scss '$var: 12px $bar baz !default;', '$var ||= 12px $bar "baz"'
864
889
  end
865
890
 
891
+ def test_division_asserted_with_parens
892
+ assert_renders <<SASS, <<SCSS
893
+ foo
894
+ a: (1px / 2px)
895
+ SASS
896
+ foo {
897
+ a: (1px / 2px); }
898
+ SCSS
899
+ end
900
+
901
+ def test_division_not_asserted_when_unnecessary
902
+ assert_renders <<SASS, <<SCSS
903
+ $var: 1px / 2px
904
+
905
+ foo
906
+ a: $var
907
+ SASS
908
+ $var: 1px / 2px;
909
+
910
+ foo {
911
+ a: $var; }
912
+ SCSS
913
+
914
+ assert_renders <<SASS, <<SCSS
915
+ $var: 1px
916
+
917
+ foo
918
+ a: $var / 2px
919
+ SASS
920
+ $var: 1px;
921
+
922
+ foo {
923
+ a: $var / 2px; }
924
+ SCSS
925
+
926
+ assert_renders <<SASS, <<SCSS
927
+ foo
928
+ a: 1 + 1px / 2px
929
+ SASS
930
+ foo {
931
+ a: 1 + 1px / 2px; }
932
+ SCSS
933
+ end
934
+
935
+ def test_literal_slash
936
+ assert_renders <<SASS, <<SCSS
937
+ foo
938
+ a: 1px / 2px
939
+ SASS
940
+ foo {
941
+ a: 1px / 2px; }
942
+ SCSS
943
+ end
944
+
866
945
  # Hacks
867
946
 
868
947
  def test_declaration_hacks
@@ -872,6 +951,8 @@ foo
872
951
  *name: val
873
952
  #name: val
874
953
  .name: val
954
+ name/**/: val
955
+ name/*\\**/: val
875
956
  name: val
876
957
  SASS
877
958
  foo {
@@ -879,6 +960,8 @@ foo {
879
960
  *name: val;
880
961
  #name: val;
881
962
  .name: val;
963
+ name/**/: val;
964
+ name/*\\**/: val;
882
965
  name: val; }
883
966
  SCSS
884
967
  end
@@ -32,6 +32,8 @@ MSG
32
32
  "a\n :b:c d" => 'Invalid property: ":b:c d".',
33
33
  "a\n :b c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
34
34
  "a\n b: c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
35
+ ".foo ^bar\n a: b" => ['Invalid CSS after ".foo ": expected selector, was "^bar"', 1],
36
+ "a\n @extend .foo ^bar" => 'Invalid CSS after ".foo ": expected selector, was "^bar"',
35
37
  "a: b" => 'Properties aren\'t allowed at the root of a document.',
36
38
  ":a b" => 'Properties aren\'t allowed at the root of a document.',
37
39
  "!" => 'Invalid variable: "!".',
@@ -599,6 +601,8 @@ foo {
599
601
  *name: val;
600
602
  #name: val;
601
603
  .name: val;
604
+ name/**/: val;
605
+ name/*\\**/: val;
602
606
  name: val; }
603
607
  CSS
604
608
  foo
@@ -606,6 +610,8 @@ foo
606
610
  *name: val
607
611
  #name: val
608
612
  .name: val
613
+ name/**/: val
614
+ name/*\\**/: val
609
615
  name: val
610
616
  SASS
611
617
  end
@@ -102,7 +102,7 @@ CSS
102
102
  SCSS
103
103
 
104
104
  assert_equal <<CSS, render(<<SCSS)
105
- .foo.bar, .bar.baz, .foo.baz, .baz {
105
+ .foo.bar, .baz {
106
106
  a: b; }
107
107
  CSS
108
108
  .foo.bar {a: b}
@@ -121,7 +121,7 @@ CSS
121
121
  SCSS
122
122
 
123
123
  assert_equal <<CSS, render(<<SCSS)
124
- .foo.bar, .bar.baz, .foo.bang, .baz.bang {
124
+ .foo.bar, .bar.baz, .baz.bang, .foo.bang {
125
125
  a: b; }
126
126
  CSS
127
127
  .foo.bar {a: b}
@@ -190,7 +190,7 @@ CSS
190
190
  SCSS
191
191
 
192
192
  assert_equal <<CSS, render(<<SCSS)
193
- .foo.baz, .baz {
193
+ .baz {
194
194
  a: b; }
195
195
  CSS
196
196
  .foo.baz {a: b}
@@ -208,7 +208,7 @@ CSS
208
208
  SCSS
209
209
 
210
210
  assert_equal <<CSS, render(<<SCSS)
211
- .foo#baz, #baz {
211
+ #baz {
212
212
  a: b; }
213
213
  CSS
214
214
  .foo#baz {a: b}
@@ -242,7 +242,7 @@ CSS
242
242
  SCSS
243
243
 
244
244
  assert_equal <<CSS, render(<<SCSS)
245
- .foo.bar, .bar {
245
+ .bar {
246
246
  a: b; }
247
247
  CSS
248
248
  .foo.bar {a: b}
@@ -250,7 +250,7 @@ CSS
250
250
  SCSS
251
251
 
252
252
  assert_equal <<CSS, render(<<SCSS)
253
- .foo.bar, .bar {
253
+ .bar {
254
254
  a: b; }
255
255
  CSS
256
256
  .foo.bar {a: b}
@@ -268,7 +268,7 @@ SCSS
268
268
 
269
269
  def test_universal_unification_with_namespaceless_universal_target
270
270
  assert_equal <<CSS, render(<<SCSS)
271
- *.foo, * {
271
+ * {
272
272
  a: b; }
273
273
  CSS
274
274
  *.foo {a: b}
@@ -276,7 +276,7 @@ CSS
276
276
  SCSS
277
277
 
278
278
  assert_equal <<CSS, render(<<SCSS)
279
- *.foo, * {
279
+ * {
280
280
  a: b; }
281
281
  CSS
282
282
  *.foo {a: b}
@@ -292,7 +292,7 @@ CSS
292
292
  SCSS
293
293
 
294
294
  assert_equal <<CSS, render(<<SCSS)
295
- *|*.foo, *|* {
295
+ *|* {
296
296
  a: b; }
297
297
  CSS
298
298
  *|*.foo {a: b}
@@ -318,7 +318,7 @@ SCSS
318
318
 
319
319
  def test_universal_unification_with_namespaced_universal_target
320
320
  assert_equal <<CSS, render(<<SCSS)
321
- ns|*.foo, ns|* {
321
+ ns|* {
322
322
  a: b; }
323
323
  CSS
324
324
  ns|*.foo {a: b}
@@ -326,7 +326,7 @@ ns|*.foo {a: b}
326
326
  SCSS
327
327
 
328
328
  assert_equal <<CSS, render(<<SCSS)
329
- ns|*.foo, ns|* {
329
+ ns|* {
330
330
  a: b; }
331
331
  CSS
332
332
  ns|*.foo {a: b}
@@ -342,7 +342,7 @@ ns2|* {@extend .foo}
342
342
  SCSS
343
343
 
344
344
  assert_equal <<CSS, render(<<SCSS)
345
- ns|*.foo, ns|* {
345
+ ns|* {
346
346
  a: b; }
347
347
  CSS
348
348
  ns|*.foo {a: b}
@@ -352,7 +352,7 @@ SCSS
352
352
 
353
353
  def test_universal_unification_with_namespaceless_element_target
354
354
  assert_equal <<CSS, render(<<SCSS)
355
- a.foo, a {
355
+ a {
356
356
  a: b; }
357
357
  CSS
358
358
  a.foo {a: b}
@@ -360,7 +360,7 @@ a.foo {a: b}
360
360
  SCSS
361
361
 
362
362
  assert_equal <<CSS, render(<<SCSS)
363
- a.foo, a {
363
+ a {
364
364
  a: b; }
365
365
  CSS
366
366
  a.foo {a: b}
@@ -376,7 +376,7 @@ CSS
376
376
  SCSS
377
377
 
378
378
  assert_equal <<CSS, render(<<SCSS)
379
- *|a.foo, *|a {
379
+ *|a {
380
380
  a: b; }
381
381
  CSS
382
382
  *|a.foo {a: b}
@@ -402,7 +402,7 @@ SCSS
402
402
 
403
403
  def test_universal_unification_with_namespaced_element_target
404
404
  assert_equal <<CSS, render(<<SCSS)
405
- ns|a.foo, ns|a {
405
+ ns|a {
406
406
  a: b; }
407
407
  CSS
408
408
  ns|a.foo {a: b}
@@ -410,7 +410,7 @@ ns|a.foo {a: b}
410
410
  SCSS
411
411
 
412
412
  assert_equal <<CSS, render(<<SCSS)
413
- ns|a.foo, ns|a {
413
+ ns|a {
414
414
  a: b; }
415
415
  CSS
416
416
  ns|a.foo {a: b}
@@ -426,7 +426,7 @@ ns2|* {@extend .foo}
426
426
  SCSS
427
427
 
428
428
  assert_equal <<CSS, render(<<SCSS)
429
- ns|a.foo, ns|a {
429
+ ns|a {
430
430
  a: b; }
431
431
  CSS
432
432
  ns|a.foo {a: b}
@@ -554,7 +554,7 @@ SCSS
554
554
 
555
555
  def test_element_unification_with_namespaceless_element_target
556
556
  assert_equal <<CSS, render(<<SCSS)
557
- a.foo, a {
557
+ a {
558
558
  a: b; }
559
559
  CSS
560
560
  a.foo {a: b}
@@ -562,7 +562,7 @@ a {@extend .foo}
562
562
  SCSS
563
563
 
564
564
  assert_equal <<CSS, render(<<SCSS)
565
- a.foo, a {
565
+ a {
566
566
  a: b; }
567
567
  CSS
568
568
  a.foo {a: b}
@@ -578,7 +578,7 @@ a {@extend .foo}
578
578
  SCSS
579
579
 
580
580
  assert_equal <<CSS, render(<<SCSS)
581
- *|a.foo, *|a {
581
+ *|a {
582
582
  a: b; }
583
583
  CSS
584
584
  *|a.foo {a: b}
@@ -612,7 +612,7 @@ SCSS
612
612
 
613
613
  def test_element_unification_with_namespaced_element_target
614
614
  assert_equal <<CSS, render(<<SCSS)
615
- ns|a.foo, ns|a {
615
+ ns|a {
616
616
  a: b; }
617
617
  CSS
618
618
  ns|a.foo {a: b}
@@ -620,7 +620,7 @@ a {@extend .foo}
620
620
  SCSS
621
621
 
622
622
  assert_equal <<CSS, render(<<SCSS)
623
- ns|a.foo, ns|a {
623
+ ns|a {
624
624
  a: b; }
625
625
  CSS
626
626
  ns|a.foo {a: b}
@@ -636,7 +636,7 @@ ns2|a {@extend .foo}
636
636
  SCSS
637
637
 
638
638
  assert_equal <<CSS, render(<<SCSS)
639
- ns|a.foo, ns|a {
639
+ ns|a {
640
640
  a: b; }
641
641
  CSS
642
642
  ns|a.foo {a: b}
@@ -678,7 +678,7 @@ CSS
678
678
  SCSS
679
679
 
680
680
  assert_equal <<CSS, render(<<SCSS)
681
- [foo=bar].baz, [foo=bar] {
681
+ [foo=bar] {
682
682
  a: b; }
683
683
  CSS
684
684
  [foo=bar].baz {a: b}
@@ -720,7 +720,7 @@ CSS
720
720
  SCSS
721
721
 
722
722
  assert_equal <<CSS, render(<<SCSS)
723
- ::foo.baz, ::foo {
723
+ ::foo {
724
724
  a: b; }
725
725
  CSS
726
726
  ::foo.baz {a: b}
@@ -728,7 +728,7 @@ CSS
728
728
  SCSS
729
729
 
730
730
  assert_equal <<CSS, render(<<SCSS)
731
- ::foo(2n+1).baz, ::foo(2n+1) {
731
+ ::foo(2n+1) {
732
732
  a: b; }
733
733
  CSS
734
734
  ::foo(2n+1).baz {a: b}
@@ -744,7 +744,7 @@ CSS
744
744
  SCSS
745
745
 
746
746
  assert_equal <<CSS, render(<<SCSS)
747
- :foo.baz, :foo {
747
+ :foo {
748
748
  a: b; }
749
749
  CSS
750
750
  :foo.baz {a: b}
@@ -770,6 +770,70 @@ a.foo::bar {a: b}
770
770
  SCSS
771
771
  end
772
772
 
773
+ def test_pseudoclass_remains_at_end_of_selector
774
+ assert_equal <<CSS, render(<<SCSS)
775
+ .foo:bar, .baz:bar {
776
+ a: b; }
777
+ CSS
778
+ .foo:bar {a: b}
779
+ .baz {@extend .foo}
780
+ SCSS
781
+
782
+ assert_equal <<CSS, render(<<SCSS)
783
+ a.foo:bar, a.baz:bar {
784
+ a: b; }
785
+ CSS
786
+ a.foo:bar {a: b}
787
+ .baz {@extend .foo}
788
+ SCSS
789
+ end
790
+
791
+ def test_not_remains_at_end_of_selector
792
+ assert_equal <<CSS, render(<<SCSS)
793
+ .foo:not(.bar), .baz:not(.bar) {
794
+ a: b; }
795
+ CSS
796
+ .foo:not(.bar) {a: b}
797
+ .baz {@extend .foo}
798
+ SCSS
799
+ end
800
+
801
+ def test_pseudoelement_goes_lefter_than_pseudoclass
802
+ assert_equal <<CSS, render(<<SCSS)
803
+ .foo::bar, .baz:bang::bar {
804
+ a: b; }
805
+ CSS
806
+ .foo::bar {a: b}
807
+ .baz:bang {@extend .foo}
808
+ SCSS
809
+
810
+ assert_equal <<CSS, render(<<SCSS)
811
+ .foo:bar, .baz:bar::bang {
812
+ a: b; }
813
+ CSS
814
+ .foo:bar {a: b}
815
+ .baz::bang {@extend .foo}
816
+ SCSS
817
+ end
818
+
819
+ def test_pseudoelement_goes_lefter_than_not
820
+ assert_equal <<CSS, render(<<SCSS)
821
+ .foo::bar, .baz:not(.bang)::bar {
822
+ a: b; }
823
+ CSS
824
+ .foo::bar {a: b}
825
+ .baz:not(.bang) {@extend .foo}
826
+ SCSS
827
+
828
+ assert_equal <<CSS, render(<<SCSS)
829
+ .foo:not(.bang), .baz:not(.bang)::bar {
830
+ a: b; }
831
+ CSS
832
+ .foo:not(.bang) {a: b}
833
+ .baz::bar {@extend .foo}
834
+ SCSS
835
+ end
836
+
773
837
  def test_negation_unification
774
838
  assert_equal <<CSS, render(<<SCSS)
775
839
  :not(.foo).baz, :not(.foo):not(.bar) {
@@ -780,7 +844,7 @@ CSS
780
844
  SCSS
781
845
 
782
846
  assert_equal <<CSS, render(<<SCSS)
783
- :not(.foo).baz, :not(.foo) {
847
+ :not(.foo) {
784
848
  a: b; }
785
849
  CSS
786
850
  :not(.foo).baz {a: b}
@@ -788,7 +852,7 @@ CSS
788
852
  SCSS
789
853
 
790
854
  assert_equal <<CSS, render(<<SCSS)
791
- :not([a=b]).baz, :not([a=b]) {
855
+ :not([a=b]) {
792
856
  a: b; }
793
857
  CSS
794
858
  :not([a=b]).baz {a: b}
@@ -1036,6 +1100,16 @@ CSS
1036
1100
  SCSS
1037
1101
  end
1038
1102
 
1103
+ def test_multiple_extender_merges_with_superset_selector
1104
+ assert_equal <<CSS, render(<<SCSS)
1105
+ a.bar.baz, a.foo {
1106
+ a: b; }
1107
+ CSS
1108
+ .foo {@extend .bar; @extend .baz}
1109
+ a.bar.baz {a: b}
1110
+ SCSS
1111
+ end
1112
+
1039
1113
  private
1040
1114
 
1041
1115
  def render(sass, options = {})
@@ -349,6 +349,8 @@ foo {
349
349
  :name: val;
350
350
  .name: val;
351
351
  #name: val;
352
+ name/**/: val;
353
+ name/*\\**/: val;
352
354
  name: val; }
353
355
  SCSS
354
356
  end
@@ -942,9 +942,11 @@ SCSS
942
942
  end
943
943
 
944
944
  def test_post_resolution_selector_error
945
- assert_raise(Sass::SyntaxError, 'Invalid CSS after "foo ": expected selector, was ") bar"') do
946
- render 'foo #{") bar"} {a: b}'
947
- end
945
+ render "\n\nfoo \#{\") bar\"} {a: b}"
946
+ assert(false, "Expected syntax error")
947
+ rescue Sass::SyntaxError => e
948
+ assert_equal 'Invalid CSS after "foo ": expected selector, was ") bar"', e.message
949
+ assert_equal 3, e.sass_line
948
950
  end
949
951
 
950
952
  def test_parent_in_mid_selector_error
@@ -968,6 +970,21 @@ SCSS
968
970
  flim {
969
971
  && {a: b}
970
972
  }
973
+ SCSS
974
+ end
975
+
976
+ # Regression
977
+
978
+ def test_weird_added_space
979
+ assert_equal <<CSS, render(<<SCSS)
980
+ foo {
981
+ bar: -moz-bip; }
982
+ CSS
983
+ $value : bip;
984
+
985
+ foo {
986
+ bar: -moz-\#{$value};
987
+ }
971
988
  SCSS
972
989
  end
973
990
  end
metadata CHANGED
@@ -7,8 +7,8 @@ version: !ruby/object:Gem::Version
7
7
  - 0
8
8
  - 0
9
9
  - rc
10
- - 1
11
- version: 3.0.0.rc.1
10
+ - 2
11
+ version: 3.0.0.rc.2
12
12
  platform: ruby
13
13
  authors:
14
14
  - Nathan Weizenbaum
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-04-26 00:00:00 -07:00
20
+ date: 2010-04-27 00:00:00 -07:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency