sass 3.3.0 → 3.4.0

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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +58 -50
  4. data/Rakefile +1 -4
  5. data/VERSION +1 -1
  6. data/VERSION_DATE +1 -1
  7. data/VERSION_NAME +1 -1
  8. data/bin/sass +1 -1
  9. data/bin/scss +1 -1
  10. data/lib/sass/cache_stores/filesystem.rb +6 -2
  11. data/lib/sass/css.rb +1 -3
  12. data/lib/sass/engine.rb +37 -46
  13. data/lib/sass/environment.rb +13 -17
  14. data/lib/sass/error.rb +6 -9
  15. data/lib/sass/exec/base.rb +187 -0
  16. data/lib/sass/exec/sass_convert.rb +264 -0
  17. data/lib/sass/exec/sass_scss.rb +424 -0
  18. data/lib/sass/exec.rb +5 -771
  19. data/lib/sass/features.rb +7 -0
  20. data/lib/sass/importers/base.rb +7 -2
  21. data/lib/sass/importers/filesystem.rb +9 -25
  22. data/lib/sass/importers.rb +0 -1
  23. data/lib/sass/media.rb +1 -4
  24. data/lib/sass/plugin/compiler.rb +200 -83
  25. data/lib/sass/plugin/staleness_checker.rb +1 -1
  26. data/lib/sass/plugin.rb +3 -3
  27. data/lib/sass/script/css_lexer.rb +1 -1
  28. data/lib/sass/script/functions.rb +622 -268
  29. data/lib/sass/script/lexer.rb +99 -34
  30. data/lib/sass/script/parser.rb +24 -23
  31. data/lib/sass/script/tree/funcall.rb +1 -1
  32. data/lib/sass/script/tree/interpolation.rb +20 -2
  33. data/lib/sass/script/tree/selector.rb +26 -0
  34. data/lib/sass/script/tree/string_interpolation.rb +1 -1
  35. data/lib/sass/script/tree.rb +1 -0
  36. data/lib/sass/script/value/base.rb +7 -5
  37. data/lib/sass/script/value/bool.rb +0 -5
  38. data/lib/sass/script/value/color.rb +39 -21
  39. data/lib/sass/script/value/helpers.rb +107 -0
  40. data/lib/sass/script/value/list.rb +0 -15
  41. data/lib/sass/script/value/null.rb +0 -5
  42. data/lib/sass/script/value/number.rb +62 -14
  43. data/lib/sass/script/value/string.rb +59 -11
  44. data/lib/sass/script/value.rb +0 -1
  45. data/lib/sass/scss/css_parser.rb +8 -2
  46. data/lib/sass/scss/parser.rb +190 -328
  47. data/lib/sass/scss/rx.rb +15 -6
  48. data/lib/sass/scss/static_parser.rb +298 -1
  49. data/lib/sass/selector/abstract_sequence.rb +28 -13
  50. data/lib/sass/selector/comma_sequence.rb +92 -13
  51. data/lib/sass/selector/pseudo.rb +256 -0
  52. data/lib/sass/selector/sequence.rb +94 -24
  53. data/lib/sass/selector/simple.rb +14 -25
  54. data/lib/sass/selector/simple_sequence.rb +97 -33
  55. data/lib/sass/selector.rb +57 -194
  56. data/lib/sass/shared.rb +1 -1
  57. data/lib/sass/source/map.rb +26 -12
  58. data/lib/sass/stack.rb +0 -6
  59. data/lib/sass/supports.rb +2 -3
  60. data/lib/sass/tree/at_root_node.rb +1 -0
  61. data/lib/sass/tree/charset_node.rb +1 -1
  62. data/lib/sass/tree/directive_node.rb +8 -2
  63. data/lib/sass/tree/error_node.rb +18 -0
  64. data/lib/sass/tree/extend_node.rb +1 -1
  65. data/lib/sass/tree/function_node.rb +4 -0
  66. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  67. data/lib/sass/tree/prop_node.rb +1 -1
  68. data/lib/sass/tree/rule_node.rb +12 -7
  69. data/lib/sass/tree/visitors/check_nesting.rb +38 -10
  70. data/lib/sass/tree/visitors/convert.rb +16 -18
  71. data/lib/sass/tree/visitors/cssize.rb +29 -29
  72. data/lib/sass/tree/visitors/deep_copy.rb +5 -0
  73. data/lib/sass/tree/visitors/perform.rb +45 -33
  74. data/lib/sass/tree/visitors/set_options.rb +14 -0
  75. data/lib/sass/tree/visitors/to_css.rb +15 -14
  76. data/lib/sass/util/subset_map.rb +1 -1
  77. data/lib/sass/util.rb +222 -99
  78. data/lib/sass/version.rb +5 -5
  79. data/lib/sass.rb +0 -5
  80. data/test/sass/cache_test.rb +62 -20
  81. data/test/sass/callbacks_test.rb +1 -1
  82. data/test/sass/compiler_test.rb +19 -10
  83. data/test/sass/conversion_test.rb +58 -1
  84. data/test/sass/css2sass_test.rb +23 -4
  85. data/test/sass/encoding_test.rb +219 -0
  86. data/test/sass/engine_test.rb +136 -199
  87. data/test/sass/exec_test.rb +2 -2
  88. data/test/sass/extend_test.rb +236 -19
  89. data/test/sass/functions_test.rb +295 -253
  90. data/test/sass/importer_test.rb +31 -21
  91. data/test/sass/logger_test.rb +1 -1
  92. data/test/sass/more_results/more_import.css +1 -1
  93. data/test/sass/plugin_test.rb +14 -13
  94. data/test/sass/results/compact.css +1 -1
  95. data/test/sass/results/complex.css +4 -4
  96. data/test/sass/results/expanded.css +1 -1
  97. data/test/sass/results/import.css +1 -1
  98. data/test/sass/results/import_charset_ibm866.css +2 -2
  99. data/test/sass/results/mixins.css +17 -17
  100. data/test/sass/results/nested.css +1 -1
  101. data/test/sass/results/parent_ref.css +2 -2
  102. data/test/sass/results/script.css +3 -3
  103. data/test/sass/results/scss_import.css +1 -1
  104. data/test/sass/script_conversion_test.rb +10 -7
  105. data/test/sass/script_test.rb +288 -74
  106. data/test/sass/scss/css_test.rb +141 -24
  107. data/test/sass/scss/rx_test.rb +4 -4
  108. data/test/sass/scss/scss_test.rb +457 -18
  109. data/test/sass/source_map_test.rb +115 -25
  110. data/test/sass/superselector_test.rb +191 -0
  111. data/test/sass/templates/scss_import.scss +2 -1
  112. data/test/sass/test_helper.rb +1 -1
  113. data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
  114. data/test/sass/util/normalized_map_test.rb +1 -1
  115. data/test/sass/util/subset_map_test.rb +2 -2
  116. data/test/sass/util_test.rb +31 -1
  117. data/test/sass/value_helpers_test.rb +5 -7
  118. data/test/test_helper.rb +2 -2
  119. data/vendor/listen/CHANGELOG.md +1 -228
  120. data/vendor/listen/Gemfile +5 -15
  121. data/vendor/listen/README.md +111 -77
  122. data/vendor/listen/Rakefile +0 -42
  123. data/vendor/listen/lib/listen/adapter.rb +195 -82
  124. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  125. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  126. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  127. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  128. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  129. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  130. data/vendor/listen/lib/listen/listener.rb +135 -37
  131. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  132. data/vendor/listen/lib/listen/version.rb +1 -1
  133. data/vendor/listen/lib/listen.rb +33 -19
  134. data/vendor/listen/listen.gemspec +6 -0
  135. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  136. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  137. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  138. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  139. data/vendor/listen/spec/listen_spec.rb +15 -21
  140. data/vendor/listen/spec/spec_helper.rb +4 -0
  141. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  142. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  143. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  144. metadata +25 -22
  145. data/ext/mkrf_conf.rb +0 -27
  146. data/lib/sass/importers/deprecated_path.rb +0 -51
  147. data/lib/sass/script/value/deprecated_false.rb +0 -55
  148. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  149. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  150. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  151. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -0,0 +1,256 @@
1
+ module Sass
2
+ module Selector
3
+ # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`)
4
+ # selector. It can have arguments (e.g. `:nth-child(2n+1)`) which can
5
+ # contain selectors (e.g. `:nth-child(2n+1 of .foo)`).
6
+ class Pseudo < Simple
7
+ # Some pseudo-class-syntax selectors are actually considered
8
+ # pseudo-elements and must be treated differently. This is a list of such
9
+ # selectors.
10
+ #
11
+ # @return [Set<String>]
12
+ ACTUALLY_ELEMENTS = %w[after before first-line first-letter].to_set
13
+
14
+ # Like \{#type}, but returns the type of selector this looks like, rather
15
+ # than the type it is semantically. This only differs from type for
16
+ # selectors in \{ACTUALLY\_ELEMENTS}.
17
+ #
18
+ # @return [Symbol]
19
+ attr_reader :syntactic_type
20
+
21
+ # The name of the selector.
22
+ #
23
+ # @return [String]
24
+ attr_reader :name
25
+
26
+ # The argument to the selector,
27
+ # or `nil` if no argument was given.
28
+ #
29
+ # @return [String, nil]
30
+ attr_reader :arg
31
+
32
+ # The selector argument, or `nil` if no selector exists.
33
+ #
34
+ # If this and \{#arg\} are both set, \{#arg\} is considered a non-selector
35
+ # prefix.
36
+ #
37
+ # @return [CommaSequence]
38
+ attr_reader :selector
39
+
40
+ # @param syntactic_type [Symbol] See \{#syntactic_type}
41
+ # @param name [String] See \{#name}
42
+ # @param arg [nil, String] See \{#arg}
43
+ # @param selector [nil, CommaSequence] See \{#selector}
44
+ def initialize(syntactic_type, name, arg, selector)
45
+ @syntactic_type = syntactic_type
46
+ @name = name
47
+ @arg = arg
48
+ @selector = selector
49
+ end
50
+
51
+ # Returns a copy of this with \{#selector} set to \{#new\_selector}.
52
+ #
53
+ # @param new_selector [CommaSequence]
54
+ # @return [CommaSequence]
55
+ def with_selector(new_selector)
56
+ Pseudo.new(syntactic_type, name, arg, CommaSequence.new(new_selector.members.map do |seq|
57
+ next seq unless seq.members.length == 1
58
+ sseq = seq.members.first
59
+ next seq unless sseq.is_a?(SimpleSequence) && sseq.members.length == 1
60
+ sel = sseq.members.first
61
+ next seq unless sel.is_a?(Pseudo) && sel.selector
62
+
63
+ case normalized_name
64
+ when 'not'
65
+ # In theory, if there's a nested :not its contents should be
66
+ # unified with the return value. For example, if :not(.foo)
67
+ # extends .bar, :not(.bar) should become .foo:not(.bar). However,
68
+ # this is a narrow edge case and supporting it properly would make
69
+ # this code and the code calling it a lot more complicated, so
70
+ # it's not supported for now.
71
+ next [] unless sel.normalized_name == 'matches'
72
+ sel.selector.members
73
+ when 'matches', 'any', 'current', 'nth-child', 'nth-last-child'
74
+ # As above, we could theoretically support :not within :matches, but
75
+ # doing so would require this method and its callers to handle much
76
+ # more complex cases that likely aren't worth the pain.
77
+ next [] unless sel.name == name && sel.arg == arg
78
+ sel.selector.members
79
+ when 'has', 'host', 'host-context'
80
+ # We can't expand nested selectors here, because each layer adds an
81
+ # additional layer of semantics. For example, `:has(:has(img))`
82
+ # doesn't match `<div><img></div>` but `:has(img)` does.
83
+ sel
84
+ else
85
+ []
86
+ end
87
+ end.flatten))
88
+ end
89
+
90
+ # The type of the selector. `:class` if this is a pseudoclass selector,
91
+ # `:element` if it's a pseudoelement.
92
+ #
93
+ # @return [Symbol]
94
+ def type
95
+ ACTUALLY_ELEMENTS.include?(normalized_name) ? :element : syntactic_type
96
+ end
97
+
98
+ # Like \{#name\}, but without any vendor prefix.
99
+ #
100
+ # @return [String]
101
+ def normalized_name
102
+ @normalized_name ||= name.gsub(/^-[a-zA-Z0-9]+-/, '')
103
+ end
104
+
105
+ # @see Selector#to_s
106
+ def to_s
107
+ res = (syntactic_type == :class ? ":" : "::") + @name
108
+ if @arg || @selector
109
+ res << "("
110
+ res << @arg.strip if @arg
111
+ res << " " if @arg && @selector
112
+ res << @selector.to_s if @selector
113
+ res << ")"
114
+ end
115
+ res
116
+ end
117
+
118
+ # Returns `nil` if this is a pseudoelement selector
119
+ # and `sels` contains a pseudoelement selector different than this one.
120
+ #
121
+ # @see SimpleSequence#unify
122
+ def unify(sels)
123
+ return if type == :element && sels.any? do |sel|
124
+ sel.is_a?(Pseudo) && sel.type == :element &&
125
+ (sel.name != name || sel.arg != arg || sel.selector != selector)
126
+ end
127
+ super
128
+ end
129
+
130
+ # Returns whether or not this selector matches all elements
131
+ # that the given selector matches (as well as possibly more).
132
+ #
133
+ # @example
134
+ # (.foo).superselector?(.foo.bar) #=> true
135
+ # (.foo).superselector?(.bar) #=> false
136
+ # @param their_sseq [SimpleSequence]
137
+ # @param parents [Array<SimpleSequence, String>] The parent selectors of `their_sseq`, if any.
138
+ # @return [Boolean]
139
+ def superselector?(their_sseq, parents = [])
140
+ case normalized_name
141
+ when 'matches', 'any'
142
+ # :matches can be a superselector of another selector in one of two
143
+ # ways. Either its constituent selectors can be a superset of those of
144
+ # another :matches in the other selector, or any of its constituent
145
+ # selectors can individually be a superselector of the other selector.
146
+ (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
147
+ next false unless their_sel.is_a?(Pseudo)
148
+ next false unless their_sel.name == name
149
+ selector.superselector?(their_sel.selector)
150
+ end || selector.members.any? do |our_seq|
151
+ their_seq = Sequence.new(parents + [their_sseq])
152
+ our_seq.superselector?(their_seq)
153
+ end
154
+ when 'has', 'host', 'host-context'
155
+ # Like :matches, :has (et al) can be a superselector of another
156
+ # selector if its constituent selectors are a superset of those of
157
+ # another :has in the other selector. However, the :matches other case
158
+ # doesn't work, because :has refers to nested elements.
159
+ (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
160
+ next false unless their_sel.is_a?(Pseudo)
161
+ next false unless their_sel.name == name
162
+ selector.superselector?(their_sel.selector)
163
+ end
164
+ when 'not'
165
+ selector.members.all? do |our_seq|
166
+ their_sseq.members.any? do |their_sel|
167
+ if their_sel.is_a?(Element) || their_sel.is_a?(Id)
168
+ # `:not(a)` is a superselector of `h1` and `:not(#foo)` is a
169
+ # superselector of `#bar`.
170
+ our_sseq = our_seq.members.last
171
+ next false unless our_sseq.is_a?(SimpleSequence)
172
+ our_sseq.members.any? do |our_sel|
173
+ our_sel.class == their_sel.class && our_sel != their_sel
174
+ end
175
+ else
176
+ next false unless their_sel.is_a?(Pseudo)
177
+ next false unless their_sel.name == name
178
+ # :not(X) is a superselector of :not(Y) exactly when Y is a
179
+ # superselector of X.
180
+ their_sel.selector.superselector?(CommaSequence.new([our_seq]))
181
+ end
182
+ end
183
+ end
184
+ when 'current'
185
+ (their_sseq.selector_pseudo_classes['current'] || []).any? do |their_current|
186
+ next false if their_current.name != name
187
+ # Explicitly don't check for nested superselector relationships
188
+ # here. :current(.foo) isn't always a superselector of
189
+ # :current(.foo.bar), since it matches the *innermost* ancestor of
190
+ # the current element that matches the selector. For example:
191
+ #
192
+ # <div class="foo bar">
193
+ # <p class="foo">
194
+ # <span>current element</span>
195
+ # </p>
196
+ # </div>
197
+ #
198
+ # Here :current(.foo) would match the p element and *not* the div
199
+ # element, whereas :current(.foo.bar) would match the div and not
200
+ # the p.
201
+ selector == their_current.selector
202
+ end
203
+ when 'nth-child', 'nth-last-child'
204
+ their_sseq.members.any? do |their_sel|
205
+ # This misses a few edge cases. For example, `:nth-child(n of X)`
206
+ # is a superselector of `X`, and `:nth-child(2n of X)` is a
207
+ # superselector of `:nth-child(4n of X)`. These seem rare enough
208
+ # not to be worth worrying about, though.
209
+ next false unless their_sel.is_a?(Pseudo)
210
+ next false unless their_sel.name == name
211
+ next false unless their_sel.arg == arg
212
+ selector.superselector?(their_sel.selector)
213
+ end
214
+ else
215
+ throw "[BUG] Unknown selector pseudo class #{name}"
216
+ end
217
+ end
218
+
219
+ # @see AbstractSequence#specificity
220
+ def specificity
221
+ return 1 if type == :element
222
+ return SPECIFICITY_BASE unless selector
223
+ @specificity ||=
224
+ if normalized_name == 'not'
225
+ min = 0
226
+ max = 0
227
+ selector.members.each do |seq|
228
+ spec = seq.specificity
229
+ if spec.is_a?(Range)
230
+ min = Sass::Util.max(spec.begin, min)
231
+ max = Sass::Util.max(spec.end, max)
232
+ else
233
+ min = Sass::Util.max(spec, min)
234
+ max = Sass::Util.max(spec, max)
235
+ end
236
+ end
237
+ min == max ? max : (min..max)
238
+ else
239
+ min = 0
240
+ max = 0
241
+ selector.members.each do |seq|
242
+ spec = seq.specificity
243
+ if spec.is_a?(Range)
244
+ min = Sass::Util.min(spec.begin, min)
245
+ max = Sass::Util.max(spec.end, max)
246
+ else
247
+ min = Sass::Util.min(spec, min)
248
+ max = Sass::Util.max(spec, max)
249
+ end
250
+ end
251
+ min == max ? max : (min..max)
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
@@ -78,48 +78,76 @@ module Sass
78
78
  # Non-destructively extends this selector with the extensions specified in a hash
79
79
  # (which should come from {Sass::Tree::Visitors::Cssize}).
80
80
  #
81
- # @overload def do_extend(extends, parent_directives)
82
81
  # @param extends [Sass::Util::SubsetMap{Selector::Simple =>
83
82
  # Sass::Tree::Visitors::Cssize::Extend}]
84
83
  # The extensions to perform on this selector
85
84
  # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
86
85
  # The directives containing this selector.
86
+ # @param replace [Boolean]
87
+ # Whether to replace the original selector entirely or include
88
+ # it in the result.
89
+ # @param seen [Set<Array<Selector::Simple>>]
90
+ # The set of simple sequences that are currently being replaced.
91
+ # @param original [Boolean]
92
+ # Whether this is the original selector being extended, as opposed to
93
+ # the result of a previous extension that's being re-extended.
87
94
  # @return [Array<Sequence>] A list of selectors generated
88
95
  # by extending this selector with `extends`.
89
96
  # These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
90
97
  # @see CommaSequence#do_extend
91
- def do_extend(extends, parent_directives, seen = Set.new)
98
+ def do_extend(extends, parent_directives, replace, seen, original)
92
99
  extended_not_expanded = members.map do |sseq_or_op|
93
100
  next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
94
- extended = sseq_or_op.do_extend(extends, parent_directives, seen)
95
- choices = extended.map {|seq| seq.members}
96
- choices.unshift([sseq_or_op]) unless extended.any? {|seq| seq.superselector?(sseq_or_op)}
97
- choices
101
+ extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)
102
+
103
+ # The First Law of Extend says that the generated selector should have
104
+ # specificity greater than or equal to that of the original selector.
105
+ # In order to ensure that, we record the original selector's
106
+ # (`extended.first`) original specificity.
107
+ extended.first.add_sources!([self]) if original && !has_placeholder?
108
+
109
+ extended.map {|seq| seq.members}
98
110
  end
99
111
  weaves = Sass::Util.paths(extended_not_expanded).map {|path| weave(path)}
100
112
  trim(weaves).map {|p| Sequence.new(p)}
101
113
  end
102
114
 
115
+ # Unifies this with another selector sequence to produce a selector
116
+ # that matches (a subset of) the intersection of the two inputs.
117
+ #
118
+ # @param other [Sequence]
119
+ # @return [CommaSequence, nil] The unified selector, or nil if unification failed.
120
+ # @raise [Sass::SyntaxError] If this selector cannot be unified.
121
+ # This will only ever occur when a dynamic selector,
122
+ # such as {Parent} or {Interpolation}, is used in unification.
123
+ # Since these selectors should be resolved
124
+ # by the time extension and unification happen,
125
+ # this exception will only ever be raised as a result of programmer error
126
+ def unify(other)
127
+ base = members.last
128
+ other_base = other.members.last
129
+ return unless base.is_a?(SimpleSequence) && other_base.is_a?(SimpleSequence)
130
+ return unless (unified = other_base.unify(base))
131
+
132
+ woven = weave([members[0...-1], other.members[0...-1] + [unified]])
133
+ CommaSequence.new(woven.map {|w| Sequence.new(w)})
134
+ end
135
+
103
136
  # Returns whether or not this selector matches all elements
104
137
  # that the given selector matches (as well as possibly more).
105
138
  #
106
139
  # @example
107
140
  # (.foo).superselector?(.foo.bar) #=> true
108
141
  # (.foo).superselector?(.bar) #=> false
109
- # (.bar .foo).superselector?(.foo) #=> false
110
- # @param sseq [SimpleSequence]
142
+ # @param cseq [Sequence]
111
143
  # @return [Boolean]
112
- def superselector?(sseq)
113
- return false unless members.size == 1
114
- members.last.superselector?(sseq)
144
+ def superselector?(seq)
145
+ _superselector?(members, seq.members)
115
146
  end
116
147
 
117
- # @see Simple#to_a
118
- def to_a
119
- ary = @members.map do |seq_or_op|
120
- seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op
121
- end
122
- Sass::Util.intersperse(ary, " ").flatten.compact
148
+ # @see AbstractSequence#to_s
149
+ def to_s
150
+ @members.join(" ").gsub(/ ?\n ?/, "\n")
123
151
  end
124
152
 
125
153
  # Returns a string representation of the sequence.
@@ -139,6 +167,35 @@ module Sass
139
167
  members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}
140
168
  end
141
169
 
170
+ # Converts the subject operator "!", if it exists, into a ":has()"
171
+ # selector.
172
+ #
173
+ # @retur [Sequence]
174
+ def subjectless
175
+ pre_subject = []
176
+ has = []
177
+ subject = nil
178
+ members.each do |sseq_or_op|
179
+ if subject
180
+ has << sseq_or_op
181
+ elsif sseq_or_op.is_a?(String) || !sseq_or_op.subject?
182
+ pre_subject << sseq_or_op
183
+ else
184
+ subject = sseq_or_op.dup
185
+ subject.members = sseq_or_op.members.dup
186
+ subject.subject = false
187
+ has = []
188
+ end
189
+ end
190
+
191
+ return self unless subject
192
+
193
+ unless has.empty?
194
+ subject.members << Pseudo.new(:class, 'has', nil, CommaSequence.new([Sequence.new(has)]))
195
+ end
196
+ Sequence.new(pre_subject + [subject])
197
+ end
198
+
142
199
  private
143
200
 
144
201
  # Conceptually, this expands "parenthesized selectors". That is, if we
@@ -157,6 +214,7 @@ module Sass
157
214
  prefixes = [[]]
158
215
 
159
216
  path.each do |current|
217
+ next if current.empty?
160
218
  current = current.dup
161
219
  last_current = [current.pop]
162
220
  prefixes = Sass::Util.flatten(prefixes.map do |prefix|
@@ -290,7 +348,7 @@ module Sass
290
348
  elsif sel2.superselector?(sel1)
291
349
  res.unshift sel1, '~'
292
350
  else
293
- merged = sel1.unify(sel2.members, sel2.subject?)
351
+ merged = sel1.unify(sel2)
294
352
  res.unshift [
295
353
  [sel1, '~', sel2, '~'],
296
354
  [sel2, '~', sel1, '~'],
@@ -307,7 +365,7 @@ module Sass
307
365
  if tilde_sel.superselector?(plus_sel)
308
366
  res.unshift plus_sel, '+'
309
367
  else
310
- merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
368
+ merged = plus_sel.unify(tilde_sel)
311
369
  res.unshift [
312
370
  [tilde_sel, '~', plus_sel, '+'],
313
371
  ([merged, '+'] if merged)
@@ -320,7 +378,7 @@ module Sass
320
378
  res.unshift sel1, op1
321
379
  seq2.push sel2, op2
322
380
  elsif op1 == op2
323
- merged = sel1.unify(sel2.members, sel2.subject?)
381
+ merged = sel1.unify(sel2)
324
382
  return unless merged
325
383
  res.unshift merged, op1
326
384
  else
@@ -410,12 +468,12 @@ module Sass
410
468
  seq1.first.is_a?(String) || seq2.first.is_a?(String)
411
469
  # More complex selectors are never superselectors of less complex ones
412
470
  return if seq1.size > seq2.size
413
- return seq1.first.superselector?(seq2.last) if seq1.size == 1
471
+ return seq1.first.superselector?(seq2.last, seq2[0...-1]) if seq1.size == 1
414
472
 
415
473
  _, si = Sass::Util.enum_with_index(seq2).find do |e, i|
416
474
  return if i == seq2.size - 1
417
475
  next if e.is_a?(String)
418
- seq1.first.superselector?(e)
476
+ seq1.first.superselector?(e, seq2[0...i])
419
477
  end
420
478
  return unless si
421
479
 
@@ -473,7 +531,15 @@ module Sass
473
531
  # separate sequences should limit the quadratic behavior.
474
532
  seqses.each_with_index do |seqs1, i|
475
533
  result[i] = seqs1.reject do |seq1|
476
- max_spec = _sources(seq1).map {|seq| seq.specificity}.max || 0
534
+ # The maximum specificity of the sources that caused [seq1] to be
535
+ # generated. In order for [seq1] to be removed, there must be
536
+ # another selector that's a superselector of it *and* that has
537
+ # specificity greater or equal to this.
538
+ max_spec = _sources(seq1).map do |seq|
539
+ spec = seq.specificity
540
+ spec.is_a?(Range) ? spec.max : spec
541
+ end.max || 0
542
+
477
543
  result.any? do |seqs2|
478
544
  next if seqs1.equal?(seqs2)
479
545
  # Second Law of Extend: the specificity of a generated selector
@@ -481,7 +547,11 @@ module Sass
481
547
  # selector.
482
548
  #
483
549
  # See https://github.com/nex3/sass/issues/324.
484
- seqs2.any? {|seq2| _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)}
550
+ seqs2.any? do |seq2|
551
+ spec2 = _specificity(seq2)
552
+ spec2 = spec2.begin if spec2.is_a?(Range)
553
+ spec2 >= max_spec && _superselector?(seq2, seq1)
554
+ end
485
555
  end
486
556
  end
487
557
  end
@@ -14,28 +14,18 @@ module Sass
14
14
  # @return [String, nil]
15
15
  attr_accessor :filename
16
16
 
17
- # Returns a representation of the node as an array of strings and
18
- # potentially {Sass::Script::Tree::Node}s (if there's interpolation in the
19
- # selector). When the interpolation is resolved and the strings are joined
20
- # together, this will be the string representation of this node.
21
- #
22
- # @return [Array<String, Sass::Script::Tree::Node>]
23
- def to_a
24
- Sass::Util.abstract(self)
25
- end
26
-
27
- # Returns a string representation of the node.
28
- # This is basically the selector string.
17
+ # @see #to_s
29
18
  #
30
19
  # @return [String]
31
20
  def inspect
32
- to_a.map {|e| e.is_a?(Sass::Script::Tree::Node) ? "\#{#{e.to_sass}}" : e}.join
21
+ to_s
33
22
  end
34
23
 
35
- # @see \{#inspect}
24
+ # Returns the selector string.
25
+ #
36
26
  # @return [String]
37
27
  def to_s
38
- inspect
28
+ Sass::Util.abstract(self)
39
29
  end
40
30
 
41
31
  # Returns a hash code for this selector object.
@@ -58,7 +48,7 @@ module Sass
58
48
  # @param other [Object] The object to test equality against
59
49
  # @return [Boolean] Whether or not this is equal to `other`
60
50
  def eql?(other)
61
- other.class == self.class && other.hash == hash && other.equality_key.eql?(equality_key)
51
+ other.class == self.class && other.hash == hash && other.equality_key == equality_key
62
52
  end
63
53
  alias_method :==, :eql?
64
54
 
@@ -83,10 +73,10 @@ module Sass
83
73
  return sels if sels.any? {|sel2| eql?(sel2)}
84
74
  sels_with_ix = Sass::Util.enum_with_index(sels)
85
75
  _, i =
86
- if is_a?(Pseudo) || is_a?(SelectorPseudoClass)
76
+ if is_a?(Pseudo)
87
77
  sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.type == :element)}
88
78
  else
89
- sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
79
+ sels_with_ix.find {|sel, _| sel.is_a?(Pseudo)}
90
80
  end
91
81
  return sels + [self] unless i
92
82
  sels[0...i] + [self] + sels[i..-1]
@@ -96,12 +86,11 @@ module Sass
96
86
 
97
87
  # Returns the key used for testing whether selectors are equal.
98
88
  #
99
- # This is based on \{#to\_a}, with adjacent strings merged so that
100
- # selectors constructed in different ways are considered equivalent.
89
+ # This is a cached version of \{#to\_s}.
101
90
  #
102
- # @return [Array<String, Sass::Script::Tree::Node>]
91
+ # @return [String]
103
92
  def equality_key
104
- @equality_key ||= Sass::Util.merge_adjacent_strings(to_a)
93
+ @equality_key ||= to_s
105
94
  end
106
95
 
107
96
  # Unifies two namespaces,
@@ -118,9 +107,9 @@ module Sass
118
107
  # could be found at all.
119
108
  # If the second value is `false`, the first should be ignored.
120
109
  def unify_namespaces(ns1, ns2)
121
- return nil, false unless ns1 == ns2 || ns1.nil? || ns1 == ['*'] || ns2.nil? || ns2 == ['*']
122
- return ns2, true if ns1 == ['*']
123
- return ns1, true if ns2 == ['*']
110
+ return nil, false unless ns1 == ns2 || ns1.nil? || ns1 == '*' || ns2.nil? || ns2 == '*'
111
+ return ns2, true if ns1 == '*'
112
+ return ns1, true if ns2 == '*'
124
113
  [ns1 || ns2, true]
125
114
  end
126
115
  end