sass 3.2.0.alpha.96 → 3.2.0.alpha.99

Sign up to get free protection for your applications and to get access to all the features.
data/REVISION CHANGED
@@ -1 +1 @@
1
- 71e121eea2e0cd0b5752f4a7ffdce70b16fcbf58
1
+ 052db34a7f6d34fa0ed364f6c38923dedcd95cd6
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0.alpha.96
1
+ 3.2.0.alpha.99
data/lib/sass/engine.rb CHANGED
@@ -8,6 +8,7 @@ require 'sass/tree/comment_node'
8
8
  require 'sass/tree/prop_node'
9
9
  require 'sass/tree/directive_node'
10
10
  require 'sass/tree/media_node'
11
+ require 'sass/tree/supports_node'
11
12
  require 'sass/tree/css_import_node'
12
13
  require 'sass/tree/variable_node'
13
14
  require 'sass/tree/mixin_def_node'
@@ -41,6 +42,7 @@ require 'sass/error'
41
42
  require 'sass/importers'
42
43
  require 'sass/shared'
43
44
  require 'sass/media'
45
+ require 'sass/supports'
44
46
 
45
47
  module Sass
46
48
 
@@ -425,36 +425,50 @@ module Sass
425
425
 
426
426
  # http://www.w3.org/TR/css3-conditional/
427
427
  def supports_directive(name)
428
- value = str {expr!(:supports_condition)}
429
- directive_body(["@#{name} #{value}".strip])
428
+ condition = expr!(:supports_condition)
429
+ node = node(Sass::Tree::SupportsNode.new(name, condition))
430
+
431
+ tok!(/\{/)
432
+ node.has_children = true
433
+ block_contents(node, :directive)
434
+ tok!(/\}/)
435
+
436
+ node
430
437
  end
431
438
 
432
439
  def supports_condition
433
- supports_negation || supports_operator || supports_declaration_condition
440
+ supports_negation || supports_operator || supports_interpolation
434
441
  end
435
442
 
436
443
  def supports_negation
437
444
  return unless tok(/not/i)
438
445
  ss
439
- expr!(:supports_condition_in_parens)
446
+ Sass::Supports::Negation.new(expr!(:supports_condition_in_parens))
440
447
  end
441
448
 
442
449
  def supports_operator
443
- return unless supports_condition_in_parens
444
- tok!(/and|or/i)
450
+ return unless cond = supports_condition_in_parens
451
+ return cond unless op = tok(/and|or/i)
445
452
  begin
446
453
  ss
447
- expr!(:supports_condition_in_parens)
448
- end while tok(/and|or/i)
449
- true
454
+ cond = Sass::Supports::Operator.new(
455
+ cond, expr!(:supports_condition_in_parens), op)
456
+ end while op = tok(/and|or/i)
457
+ cond
450
458
  end
451
459
 
452
460
  def supports_condition_in_parens
461
+ interp = supports_interpolation and return interp
453
462
  return unless tok(/\(/); ss
454
- if supports_condition
463
+ if cond = supports_condition
455
464
  tok!(/\)/); ss
465
+ cond
456
466
  else
457
- supports_declaration_body
467
+ name = sass_script(:parse)
468
+ tok!(/:/); ss
469
+ value = sass_script(:parse)
470
+ tok!(/\)/); ss
471
+ Sass::Supports::Declaration.new(name, value)
458
472
  end
459
473
  end
460
474
 
@@ -463,11 +477,10 @@ module Sass
463
477
  supports_declaration_body
464
478
  end
465
479
 
466
- def supports_declaration_body
467
- tok!(IDENT); ss
468
- tok!(/:/); ss
469
- expr!(:expr); ss
470
- tok!(/\)/); ss
480
+ def supports_interpolation
481
+ return unless interp = interpolation
482
+ ss
483
+ Sass::Supports::Interpolation.new(interp)
471
484
  end
472
485
 
473
486
  def variable
data/lib/sass/selector.rb CHANGED
@@ -18,6 +18,13 @@ module Sass
18
18
  # Finally, {Simple} is the superclass of the simplest selectors,
19
19
  # such as `.foo` or `#bar`.
20
20
  module Selector
21
+ # The base used for calculating selector specificity. The spec says this
22
+ # should be "sufficiently high"; it's extremely unlikely that any single
23
+ # selector sequence will contain 1,000 simple selectors.
24
+ #
25
+ # @type [Fixnum]
26
+ SPECIFICITY_BASE = 1_000
27
+
21
28
  # A parent-referencing selector (`&` in Sass).
22
29
  # The function of this is to be replaced by the parent selector
23
30
  # in the nested hierarchy.
@@ -52,6 +59,11 @@ module Sass
52
59
  def to_a
53
60
  [".", *@name]
54
61
  end
62
+
63
+ # @see AbstractSequence#specificity
64
+ def specificity
65
+ SPECIFICITY_BASE
66
+ end
55
67
  end
56
68
 
57
69
  # An id selector (e.g. `#foo`).
@@ -79,6 +91,11 @@ module Sass
79
91
  return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
80
92
  super
81
93
  end
94
+
95
+ # @see AbstractSequence#specificity
96
+ def specificity
97
+ SPECIFICITY_BASE**2
98
+ end
82
99
  end
83
100
 
84
101
  # A placeholder selector (e.g. `%foo`).
@@ -100,6 +117,11 @@ module Sass
100
117
  def to_a
101
118
  ["%", *@name]
102
119
  end
120
+
121
+ # @see AbstractSequence#specificity
122
+ def specificity
123
+ 0
124
+ end
103
125
  end
104
126
 
105
127
  # A universal selector (`*` in CSS).
@@ -162,6 +184,11 @@ module Sass
162
184
  return unless accept
163
185
  [name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
164
186
  end
187
+
188
+ # @see AbstractSequence#specificity
189
+ def specificity
190
+ 0
191
+ end
165
192
  end
166
193
 
167
194
  # An element selector (e.g. `h1`).
@@ -224,6 +251,11 @@ module Sass
224
251
  return unless accept
225
252
  [Element.new(name, ns)] + sels[1..-1]
226
253
  end
254
+
255
+ # @see AbstractSequence#specificity
256
+ def specificity
257
+ 1
258
+ end
227
259
  end
228
260
 
229
261
  # Selector interpolation (`#{}` in Sass).
@@ -296,6 +328,11 @@ module Sass
296
328
  (res << @operator).concat @value if @value
297
329
  res << "]"
298
330
  end
331
+
332
+ # @see AbstractSequence#specificity
333
+ def specificity
334
+ SPECIFICITY_BASE
335
+ end
299
336
  end
300
337
 
301
338
  # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
@@ -363,6 +400,11 @@ module Sass
363
400
  return sels + [self] if final?
364
401
  super
365
402
  end
403
+
404
+ # @see AbstractSequence#specificity
405
+ def specificity
406
+ type == :class ? SPECIFICITY_BASE : 1
407
+ end
366
408
  end
367
409
 
368
410
  # A pseudoclass selector whose argument is itself a selector
@@ -389,6 +431,11 @@ module Sass
389
431
  def to_a
390
432
  [":", @name, "("] + @selector.to_a + [")"]
391
433
  end
434
+
435
+ # @see AbstractSequence#specificity
436
+ def specificity
437
+ SPECIFICITY_BASE
438
+ end
392
439
  end
393
440
  end
394
441
  end
@@ -73,6 +73,22 @@ module Sass
73
73
  def to_s
74
74
  to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
75
75
  end
76
+
77
+ # Returns the specificity of the selector as an integer. The base is given
78
+ # by {Sass::Selector::SPECIFICITY_BASE}.
79
+ #
80
+ # @return [Fixnum]
81
+ def specificity
82
+ _specificity(members)
83
+ end
84
+
85
+ protected
86
+
87
+ def _specificity(arr)
88
+ spec = 0
89
+ arr.map {|m| spec += m.is_a?(String) ? 0 : m.specificity}
90
+ spec
91
+ end
76
92
  end
77
93
  end
78
94
  end
@@ -49,7 +49,15 @@ module Sass
49
49
  # @return [CommaSequence] A copy of this selector,
50
50
  # with extensions made according to `extends`
51
51
  def do_extend(extends)
52
- CommaSequence.new(members.map {|seq| seq.do_extend(extends)}.flatten)
52
+ CommaSequence.new(members.map do |seq|
53
+ extended = seq.do_extend(extends)
54
+ # First Law of Extend: the result of extending a selector should
55
+ # always contain the base selector.
56
+ #
57
+ # See https://github.com/nex3/sass/issues/324.
58
+ extended.unshift seq unless seq.has_placeholder? || extended.include?(seq)
59
+ extended
60
+ end.flatten)
53
61
  end
54
62
 
55
63
  # Returns a string representation of the sequence.
@@ -115,6 +115,15 @@ module Sass
115
115
  members.map {|m| m.inspect}.join(" ")
116
116
  end
117
117
 
118
+ # Add to the {SimpleSequence#sources} sets of the child simple sequences.
119
+ # This destructively modifies this sequence's members array, but not the
120
+ # child simple sequences.
121
+ #
122
+ # @param sources [Set<Sequence>]
123
+ def add_sources!(sources)
124
+ members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}
125
+ end
126
+
118
127
  private
119
128
 
120
129
  # Conceptually, this expands "parenthesized selectors".
@@ -430,9 +439,16 @@ module Sass
430
439
  # separate sequences should limit the quadratic behavior.
431
440
  seqses.map do |seqs1|
432
441
  seqs1.reject do |seq1|
442
+ min_spec = 0
443
+ _sources(seq1).map {|seq| min_spec += seq.specificity}
433
444
  seqses.any? do |seqs2|
434
- next if seqs1.object_id == seqs2.object_id
435
- seqs2.any? {|seq2| _superselector?(seq2, seq1)}
445
+ next if seqs1.equal?(seqs2)
446
+ # Second Law of Extend: the specificity of a generated selector
447
+ # should never be less than the specificity of the extending
448
+ # selector.
449
+ #
450
+ # See https://github.com/nex3/sass/issues/324.
451
+ seqs2.any? {|seq2| _specificity(seq2) >= min_spec && _superselector?(seq2, seq1)}
436
452
  end
437
453
  end
438
454
  end
@@ -448,6 +464,12 @@ module Sass
448
464
 
449
465
  private
450
466
 
467
+ def _sources(seq)
468
+ s = Set.new
469
+ seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}
470
+ s
471
+ end
472
+
451
473
  def extended_not_expanded_to_s(extended_not_expanded)
452
474
  extended_not_expanded.map do |choices|
453
475
  choices = choices.map do |sel|
@@ -8,7 +8,23 @@ module Sass
8
8
  # The array of individual selectors.
9
9
  #
10
10
  # @return [Array<Simple>]
11
- attr_reader :members
11
+ attr_accessor :members
12
+
13
+ # The extending selectors that caused this selector sequence to be
14
+ # generated. For example:
15
+ #
16
+ # a.foo { ... }
17
+ # b.bar {@extend a}
18
+ # c.baz {@extend b}
19
+ #
20
+ # The generated selector `b.foo.bar` has `{b.bar}` as its `sources` set,
21
+ # and the generated selector `c.foo.bar.baz` has `{b.bar, c.baz}` as its
22
+ # `sources` set.
23
+ #
24
+ # This is populated during the {#do_extend} process.
25
+ #
26
+ # @return {Set<Sequence>}
27
+ attr_accessor :sources
12
28
 
13
29
  # Returns the element or universal selector in this sequence,
14
30
  # if it exists.
@@ -26,8 +42,10 @@ module Sass
26
42
  end
27
43
 
28
44
  # @param selectors [Array<Simple>] See \{#members}
29
- def initialize(selectors)
45
+ # @param sources [Set<Sequence>]
46
+ def initialize(selectors, sources = Set.new)
30
47
  @members = selectors
48
+ @sources = sources
31
49
  end
32
50
 
33
51
  # Resolves the {Parent} selectors within this selector
@@ -54,7 +72,7 @@ module Sass
54
72
  # Non-destrucively extends this selector with the extensions specified in a hash
55
73
  # (which should come from {Sass::Tree::Visitors::Cssize}).
56
74
  #
57
- # @overload def do_extend(extends)
75
+ # @overload def do_extend(extends, sources)
58
76
  # @param extends [{Selector::Simple => Selector::Sequence}]
59
77
  # The extensions to perform on this selector
60
78
  # @return [Array<Sequence>] A list of selectors generated
@@ -68,9 +86,10 @@ module Sass
68
86
 
69
87
  self_without_sel = self.members - sels
70
88
  next unless unified = seq.members.last.unify(self_without_sel)
71
- [sels, seq.members[0...-1] + [unified]]
89
+ new_seq = Sequence.new(seq.members[0...-1] + [unified])
90
+ new_seq.add_sources!(sources + [seq])
91
+ [sels, new_seq]
72
92
  end.compact.map do |sels, seq|
73
- seq = Sequence.new(seq)
74
93
  seen.include?(sels) ? [] : seq.do_extend(extends, seen + [sels])
75
94
  end.flatten.uniq
76
95
  end
@@ -121,6 +140,18 @@ module Sass
121
140
  members.map {|m| m.inspect}.join
122
141
  end
123
142
 
143
+ # Return a copy of this simple sequence with `sources` merged into the
144
+ # {#sources} set.
145
+ #
146
+ # @param sources [Set<Sequence>]
147
+ # @return [SimpleSequence]
148
+ def with_more_sources(sources)
149
+ sseq = dup
150
+ sseq.members = members.dup
151
+ sseq.sources.merge sources
152
+ sseq
153
+ end
154
+
124
155
  private
125
156
 
126
157
  def _hash
@@ -0,0 +1,229 @@
1
+ # A namespace for the `@supports` condition parse tree.
2
+ module Sass::Supports
3
+ # The abstract superclass of all Supports conditions.
4
+ class Condition
5
+ # Runs the SassScript in the supports condition.
6
+ #
7
+ # @param env [Sass::Environment] The environment in which to run the script.
8
+ def perform(environment); Sass::Util.abstract(self); end
9
+
10
+ # Returns the CSS for this condition.
11
+ #
12
+ # @return [String]
13
+ def to_css; Sass::Util.abstract(self); end
14
+
15
+ # Returns the Sass/CSS code for this condition.
16
+ #
17
+ # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
18
+ # @return [String]
19
+ def to_src(options); Sass::Util.abstract(self); end
20
+
21
+ # Returns a deep copy of this condition and all its children.
22
+ #
23
+ # @return [Condition]
24
+ def deep_copy; Sass::Util.abstract(self); end
25
+
26
+ # Sets the options hash for the script nodes in the supports condition.
27
+ #
28
+ # @param options [{Symbol => Object}] The options has to set.
29
+ def options=(options); Sass::Util.abstract(self); end
30
+ end
31
+
32
+ # An operator condition (e.g. `CONDITION1 and CONDITION2`).
33
+ class Operator < Condition
34
+ # The left-hand condition.
35
+ #
36
+ # @return [Sass::Supports::Condition]
37
+ attr_accessor :left
38
+
39
+ # The right-hand condition.
40
+ #
41
+ # @return [Sass::Supports::Condition]
42
+ attr_accessor :right
43
+
44
+ # The operator ("and" or "or").
45
+ #
46
+ # @return [String]
47
+ attr_accessor :op
48
+
49
+ def initialize(left, right, op)
50
+ @left = left
51
+ @right = right
52
+ @op = op
53
+ end
54
+
55
+ def perform(env)
56
+ @left.perform(env)
57
+ @right.perform(env)
58
+ end
59
+
60
+ def to_css
61
+ "#{left_parens @left.to_css} #{op} #{right_parens @right.to_css}"
62
+ end
63
+
64
+ def to_src(options)
65
+ "#{left_parens @left.to_src(options)} #{op} #{right_parens @right.to_src(options)}"
66
+ end
67
+
68
+ def deep_copy
69
+ copy = dup
70
+ copy.left = @left.deep_copy
71
+ copy.right = @right.deep_copy
72
+ copy
73
+ end
74
+
75
+ def options=(options)
76
+ @left.options = options
77
+ @right.options = options
78
+ end
79
+
80
+ private
81
+
82
+ def left_parens(str)
83
+ return "(#{str})" if @left.is_a?(Negation)
84
+ return str
85
+ end
86
+
87
+ def right_parens(str)
88
+ return "(#{str})" if @right.is_a?(Negation) || @right.is_a?(Operator)
89
+ return str
90
+ end
91
+ end
92
+
93
+ # A negation condition (`not CONDITION`).
94
+ class Negation < Condition
95
+ # The condition being negated.
96
+ #
97
+ # @return [Sass::Supports::Condition]
98
+ attr_accessor :condition
99
+
100
+ def initialize(condition)
101
+ @condition = condition
102
+ end
103
+
104
+ def perform(env)
105
+ @condition.perform(env)
106
+ end
107
+
108
+ def to_css
109
+ "not #{parens @condition.to_css}"
110
+ end
111
+
112
+ def to_src(options)
113
+ "not #{parens @condition.to_src(options)}"
114
+ end
115
+
116
+ def deep_copy
117
+ copy = dup
118
+ copy.condition = condition.deep_copy
119
+ copy
120
+ end
121
+
122
+ def options=(options)
123
+ condition.options = options
124
+ end
125
+
126
+ private
127
+
128
+ def parens(str)
129
+ return "(#{str})" if @condition.is_a?(Negation) || @condition.is_a?(Operator)
130
+ return str
131
+ end
132
+ end
133
+
134
+ # A declaration condition (e.g. `(feature: value)`).
135
+ class Declaration < Condition
136
+ # The feature name.
137
+ #
138
+ # @param [Sass::Script::Node]
139
+ attr_accessor :name
140
+
141
+ # The name of the feature after any SassScript has been resolved.
142
+ # Only set once \{Tree::Visitors::Perform} has been run.
143
+ #
144
+ # @return [String]
145
+ attr_accessor :resolved_name
146
+
147
+ # The feature value.
148
+ #
149
+ # @param [Sass::Script::Node]
150
+ attr_accessor :value
151
+
152
+ # The value of the feature after any SassScript has been resolved.
153
+ # Only set once \{Tree::Visitors::Perform} has been run.
154
+ #
155
+ # @return [String]
156
+ attr_accessor :resolved_value
157
+
158
+ def initialize(name, value)
159
+ @name = name
160
+ @value = value
161
+ end
162
+
163
+ def perform(env)
164
+ @resolved_name = name.perform(env)
165
+ @resolved_value = value.perform(env)
166
+ end
167
+
168
+ def to_css
169
+ "(#{@resolved_name}: #{@resolved_value})"
170
+ end
171
+
172
+ def to_src(options)
173
+ "(#{@name.to_sass(options)}: #{@value.to_sass(options)})"
174
+ end
175
+
176
+ def deep_copy
177
+ copy = dup
178
+ copy.name = @name.deep_copy
179
+ copy.value = @value.deep_copy
180
+ copy
181
+ end
182
+
183
+ def options=(options)
184
+ @name.options = options
185
+ @value.options = options
186
+ end
187
+ end
188
+
189
+ # An interpolation condition (e.g. `#{$var}`).
190
+ class Interpolation < Condition
191
+ # The SassScript expression in the interpolation.
192
+ #
193
+ # @param [Sass::Script::Node]
194
+ attr_accessor :value
195
+
196
+ # The value of the expression after it's been resolved.
197
+ # Only set once \{Tree::Visitors::Perform} has been run.
198
+ #
199
+ # @return [String]
200
+ attr_accessor :resolved_value
201
+
202
+ def initialize(value)
203
+ @value = value
204
+ end
205
+
206
+ def perform(env)
207
+ val = value.perform(env)
208
+ @resolved_value = val.is_a?(Sass::Script::String) ? val.value : val.to_s
209
+ end
210
+
211
+ def to_css
212
+ @resolved_value
213
+ end
214
+
215
+ def to_src(options)
216
+ "\#{#{@value.to_sass(options)}}"
217
+ end
218
+
219
+ def deep_copy
220
+ copy = dup
221
+ copy.value = @value.deep_copy
222
+ copy
223
+ end
224
+
225
+ def options=(options)
226
+ @value.options = options
227
+ end
228
+ end
229
+ end