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

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