sass 3.3.0 → 3.4.25

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 (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -0,0 +1,266 @@
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 [Array<Simple>]
55
+ def with_selector(new_selector)
56
+ result = Pseudo.new(syntactic_type, name, arg,
57
+ CommaSequence.new(new_selector.members.map do |seq|
58
+ next seq unless seq.members.length == 1
59
+ sseq = seq.members.first
60
+ next seq unless sseq.is_a?(SimpleSequence) && sseq.members.length == 1
61
+ sel = sseq.members.first
62
+ next seq unless sel.is_a?(Pseudo) && sel.selector
63
+
64
+ case normalized_name
65
+ when 'not'
66
+ # In theory, if there's a nested :not its contents should be
67
+ # unified with the return value. For example, if :not(.foo)
68
+ # extends .bar, :not(.bar) should become .foo:not(.bar). However,
69
+ # this is a narrow edge case and supporting it properly would make
70
+ # this code and the code calling it a lot more complicated, so
71
+ # it's not supported for now.
72
+ next [] unless sel.normalized_name == 'matches'
73
+ sel.selector.members
74
+ when 'matches', 'any', 'current', 'nth-child', 'nth-last-child'
75
+ # As above, we could theoretically support :not within :matches, but
76
+ # doing so would require this method and its callers to handle much
77
+ # more complex cases that likely aren't worth the pain.
78
+ next [] unless sel.name == name && sel.arg == arg
79
+ sel.selector.members
80
+ when 'has', 'host', 'host-context'
81
+ # We can't expand nested selectors here, because each layer adds an
82
+ # additional layer of semantics. For example, `:has(:has(img))`
83
+ # doesn't match `<div><img></div>` but `:has(img)` does.
84
+ sel
85
+ else
86
+ []
87
+ end
88
+ end.flatten))
89
+
90
+ # Older browsers support :not but only with a single complex selector.
91
+ # In order to support those browsers, we break up the contents of a :not
92
+ # unless it originally contained a selector list.
93
+ return [result] unless normalized_name == 'not'
94
+ return [result] if selector.members.length > 1
95
+ result.selector.members.map do |seq|
96
+ Pseudo.new(syntactic_type, name, arg, CommaSequence.new([seq]))
97
+ end
98
+ end
99
+
100
+ # The type of the selector. `:class` if this is a pseudoclass selector,
101
+ # `:element` if it's a pseudoelement.
102
+ #
103
+ # @return [Symbol]
104
+ def type
105
+ ACTUALLY_ELEMENTS.include?(normalized_name) ? :element : syntactic_type
106
+ end
107
+
108
+ # Like \{#name\}, but without any vendor prefix.
109
+ #
110
+ # @return [String]
111
+ def normalized_name
112
+ @normalized_name ||= name.gsub(/^-[a-zA-Z0-9]+-/, '')
113
+ end
114
+
115
+ # @see Selector#to_s
116
+ def to_s(opts = {})
117
+ res = (syntactic_type == :class ? ":" : "::") + @name
118
+ if @arg || @selector
119
+ res << "("
120
+ res << @arg.strip if @arg
121
+ res << " " if @arg && @selector
122
+ res << @selector.to_s(opts) if @selector
123
+ res << ")"
124
+ end
125
+ res
126
+ end
127
+
128
+ # Returns `nil` if this is a pseudoelement selector
129
+ # and `sels` contains a pseudoelement selector different than this one.
130
+ #
131
+ # @see SimpleSequence#unify
132
+ def unify(sels)
133
+ return if type == :element && sels.any? do |sel|
134
+ sel.is_a?(Pseudo) && sel.type == :element &&
135
+ (sel.name != name || sel.arg != arg || sel.selector != selector)
136
+ end
137
+ super
138
+ end
139
+
140
+ # Returns whether or not this selector matches all elements
141
+ # that the given selector matches (as well as possibly more).
142
+ #
143
+ # @example
144
+ # (.foo).superselector?(.foo.bar) #=> true
145
+ # (.foo).superselector?(.bar) #=> false
146
+ # @param their_sseq [SimpleSequence]
147
+ # @param parents [Array<SimpleSequence, String>] The parent selectors of `their_sseq`, if any.
148
+ # @return [Boolean]
149
+ def superselector?(their_sseq, parents = [])
150
+ case normalized_name
151
+ when 'matches', 'any'
152
+ # :matches can be a superselector of another selector in one of two
153
+ # ways. Either its constituent selectors can be a superset of those of
154
+ # another :matches in the other selector, or any of its constituent
155
+ # selectors can individually be a superselector of the other selector.
156
+ (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
157
+ next false unless their_sel.is_a?(Pseudo)
158
+ next false unless their_sel.name == name
159
+ selector.superselector?(their_sel.selector)
160
+ end || selector.members.any? do |our_seq|
161
+ their_seq = Sequence.new(parents + [their_sseq])
162
+ our_seq.superselector?(their_seq)
163
+ end
164
+ when 'has', 'host', 'host-context'
165
+ # Like :matches, :has (et al) can be a superselector of another
166
+ # selector if its constituent selectors are a superset of those of
167
+ # another :has in the other selector. However, the :matches other case
168
+ # doesn't work, because :has refers to nested elements.
169
+ (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
170
+ next false unless their_sel.is_a?(Pseudo)
171
+ next false unless their_sel.name == name
172
+ selector.superselector?(their_sel.selector)
173
+ end
174
+ when 'not'
175
+ selector.members.all? do |our_seq|
176
+ their_sseq.members.any? do |their_sel|
177
+ if their_sel.is_a?(Element) || their_sel.is_a?(Id)
178
+ # `:not(a)` is a superselector of `h1` and `:not(#foo)` is a
179
+ # superselector of `#bar`.
180
+ our_sseq = our_seq.members.last
181
+ next false unless our_sseq.is_a?(SimpleSequence)
182
+ our_sseq.members.any? do |our_sel|
183
+ our_sel.class == their_sel.class && our_sel != their_sel
184
+ end
185
+ else
186
+ next false unless their_sel.is_a?(Pseudo)
187
+ next false unless their_sel.name == name
188
+ # :not(X) is a superselector of :not(Y) exactly when Y is a
189
+ # superselector of X.
190
+ their_sel.selector.superselector?(CommaSequence.new([our_seq]))
191
+ end
192
+ end
193
+ end
194
+ when 'current'
195
+ (their_sseq.selector_pseudo_classes['current'] || []).any? do |their_current|
196
+ next false if their_current.name != name
197
+ # Explicitly don't check for nested superselector relationships
198
+ # here. :current(.foo) isn't always a superselector of
199
+ # :current(.foo.bar), since it matches the *innermost* ancestor of
200
+ # the current element that matches the selector. For example:
201
+ #
202
+ # <div class="foo bar">
203
+ # <p class="foo">
204
+ # <span>current element</span>
205
+ # </p>
206
+ # </div>
207
+ #
208
+ # Here :current(.foo) would match the p element and *not* the div
209
+ # element, whereas :current(.foo.bar) would match the div and not
210
+ # the p.
211
+ selector == their_current.selector
212
+ end
213
+ when 'nth-child', 'nth-last-child'
214
+ their_sseq.members.any? do |their_sel|
215
+ # This misses a few edge cases. For example, `:nth-child(n of X)`
216
+ # is a superselector of `X`, and `:nth-child(2n of X)` is a
217
+ # superselector of `:nth-child(4n of X)`. These seem rare enough
218
+ # not to be worth worrying about, though.
219
+ next false unless their_sel.is_a?(Pseudo)
220
+ next false unless their_sel.name == name
221
+ next false unless their_sel.arg == arg
222
+ selector.superselector?(their_sel.selector)
223
+ end
224
+ else
225
+ throw "[BUG] Unknown selector pseudo class #{name}"
226
+ end
227
+ end
228
+
229
+ # @see AbstractSequence#specificity
230
+ def specificity
231
+ return 1 if type == :element
232
+ return SPECIFICITY_BASE unless selector
233
+ @specificity ||=
234
+ if normalized_name == 'not'
235
+ min = 0
236
+ max = 0
237
+ selector.members.each do |seq|
238
+ spec = seq.specificity
239
+ if spec.is_a?(Range)
240
+ min = Sass::Util.max(spec.begin, min)
241
+ max = Sass::Util.max(spec.end, max)
242
+ else
243
+ min = Sass::Util.max(spec, min)
244
+ max = Sass::Util.max(spec, max)
245
+ end
246
+ end
247
+ min == max ? max : (min..max)
248
+ else
249
+ min = 0
250
+ max = 0
251
+ selector.members.each do |seq|
252
+ spec = seq.specificity
253
+ if spec.is_a?(Range)
254
+ min = Sass::Util.min(spec.begin, min)
255
+ max = Sass::Util.max(spec.end, max)
256
+ else
257
+ min = Sass::Util.min(spec, min)
258
+ max = Sass::Util.max(spec, max)
259
+ end
260
+ end
261
+ min == max ? max : (min..max)
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
@@ -6,11 +6,11 @@ module Sass
6
6
  # Sets the line of the Sass template on which this selector was declared.
7
7
  # This also sets the line for all child selectors.
8
8
  #
9
- # @param line [Fixnum]
10
- # @return [Fixnum]
9
+ # @param line [Integer]
10
+ # @return [Integer]
11
11
  def line=(line)
12
12
  members.each {|m| m.line = line if m.is_a?(SimpleSequence)}
13
- line
13
+ @line = line
14
14
  end
15
15
 
16
16
  # Sets the name of the file in which this selector was declared,
@@ -52,9 +52,7 @@ module Sass
52
52
  def resolve_parent_refs(super_cseq, implicit_parent)
53
53
  members = @members.dup
54
54
  nl = (members.first == "\n" && members.shift)
55
- contains_parent_ref = members.any? do |seq_or_op|
56
- seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
57
- end
55
+ contains_parent_ref = contains_parent_ref?
58
56
  return CommaSequence.new([self]) if !implicit_parent && !contains_parent_ref
59
57
 
60
58
  unless contains_parent_ref
@@ -75,51 +73,92 @@ module Sass
75
73
  end)
76
74
  end
77
75
 
76
+ # Returns whether there's a {Parent} selector anywhere in this sequence.
77
+ #
78
+ # @return [Boolean]
79
+ def contains_parent_ref?
80
+ members.any? do |sseq_or_op|
81
+ next false unless sseq_or_op.is_a?(SimpleSequence)
82
+ next true if sseq_or_op.members.first.is_a?(Parent)
83
+ sseq_or_op.members.any? do |sel|
84
+ sel.is_a?(Pseudo) && sel.selector && sel.selector.contains_parent_ref?
85
+ end
86
+ end
87
+ end
88
+
78
89
  # Non-destructively extends this selector with the extensions specified in a hash
79
90
  # (which should come from {Sass::Tree::Visitors::Cssize}).
80
91
  #
81
- # @overload def do_extend(extends, parent_directives)
82
92
  # @param extends [Sass::Util::SubsetMap{Selector::Simple =>
83
93
  # Sass::Tree::Visitors::Cssize::Extend}]
84
94
  # The extensions to perform on this selector
85
95
  # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
86
96
  # The directives containing this selector.
97
+ # @param replace [Boolean]
98
+ # Whether to replace the original selector entirely or include
99
+ # it in the result.
100
+ # @param seen [Set<Array<Selector::Simple>>]
101
+ # The set of simple sequences that are currently being replaced.
102
+ # @param original [Boolean]
103
+ # Whether this is the original selector being extended, as opposed to
104
+ # the result of a previous extension that's being re-extended.
87
105
  # @return [Array<Sequence>] A list of selectors generated
88
106
  # by extending this selector with `extends`.
89
107
  # These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
90
108
  # @see CommaSequence#do_extend
91
- def do_extend(extends, parent_directives, seen = Set.new)
109
+ def do_extend(extends, parent_directives, replace, seen, original)
92
110
  extended_not_expanded = members.map do |sseq_or_op|
93
111
  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
112
+ extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)
113
+
114
+ # The First Law of Extend says that the generated selector should have
115
+ # specificity greater than or equal to that of the original selector.
116
+ # In order to ensure that, we record the original selector's
117
+ # (`extended.first`) original specificity.
118
+ extended.first.add_sources!([self]) if original && !has_placeholder?
119
+
120
+ extended.map {|seq| seq.members}
98
121
  end
99
122
  weaves = Sass::Util.paths(extended_not_expanded).map {|path| weave(path)}
100
123
  trim(weaves).map {|p| Sequence.new(p)}
101
124
  end
102
125
 
126
+ # Unifies this with another selector sequence to produce a selector
127
+ # that matches (a subset of) the intersection of the two inputs.
128
+ #
129
+ # @param other [Sequence]
130
+ # @return [CommaSequence, nil] The unified selector, or nil if unification failed.
131
+ # @raise [Sass::SyntaxError] If this selector cannot be unified.
132
+ # This will only ever occur when a dynamic selector,
133
+ # such as {Parent} or {Interpolation}, is used in unification.
134
+ # Since these selectors should be resolved
135
+ # by the time extension and unification happen,
136
+ # this exception will only ever be raised as a result of programmer error
137
+ def unify(other)
138
+ base = members.last
139
+ other_base = other.members.last
140
+ return unless base.is_a?(SimpleSequence) && other_base.is_a?(SimpleSequence)
141
+ return unless (unified = other_base.unify(base))
142
+
143
+ woven = weave([members[0...-1], other.members[0...-1] + [unified]])
144
+ CommaSequence.new(woven.map {|w| Sequence.new(w)})
145
+ end
146
+
103
147
  # Returns whether or not this selector matches all elements
104
148
  # that the given selector matches (as well as possibly more).
105
149
  #
106
150
  # @example
107
151
  # (.foo).superselector?(.foo.bar) #=> true
108
152
  # (.foo).superselector?(.bar) #=> false
109
- # (.bar .foo).superselector?(.foo) #=> false
110
- # @param sseq [SimpleSequence]
153
+ # @param cseq [Sequence]
111
154
  # @return [Boolean]
112
- def superselector?(sseq)
113
- return false unless members.size == 1
114
- members.last.superselector?(sseq)
155
+ def superselector?(seq)
156
+ _superselector?(members, seq.members)
115
157
  end
116
158
 
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
159
+ # @see AbstractSequence#to_s
160
+ def to_s(opts = {})
161
+ @members.map {|m| m.is_a?(String) ? m : m.to_s(opts)}.join(" ").gsub(/ ?\n ?/, "\n")
123
162
  end
124
163
 
125
164
  # Returns a string representation of the sequence.
@@ -139,6 +178,35 @@ module Sass
139
178
  members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}
140
179
  end
141
180
 
181
+ # Converts the subject operator "!", if it exists, into a ":has()"
182
+ # selector.
183
+ #
184
+ # @retur [Sequence]
185
+ def subjectless
186
+ pre_subject = []
187
+ has = []
188
+ subject = nil
189
+ members.each do |sseq_or_op|
190
+ if subject
191
+ has << sseq_or_op
192
+ elsif sseq_or_op.is_a?(String) || !sseq_or_op.subject?
193
+ pre_subject << sseq_or_op
194
+ else
195
+ subject = sseq_or_op.dup
196
+ subject.members = sseq_or_op.members.dup
197
+ subject.subject = false
198
+ has = []
199
+ end
200
+ end
201
+
202
+ return self unless subject
203
+
204
+ unless has.empty?
205
+ subject.members << Pseudo.new(:class, 'has', nil, CommaSequence.new([Sequence.new(has)]))
206
+ end
207
+ Sequence.new(pre_subject + [subject])
208
+ end
209
+
142
210
  private
143
211
 
144
212
  # Conceptually, this expands "parenthesized selectors". That is, if we
@@ -157,13 +225,14 @@ module Sass
157
225
  prefixes = [[]]
158
226
 
159
227
  path.each do |current|
228
+ next if current.empty?
160
229
  current = current.dup
161
230
  last_current = [current.pop]
162
- prefixes = Sass::Util.flatten(prefixes.map do |prefix|
231
+ prefixes = prefixes.map do |prefix|
163
232
  sub = subweave(prefix, current)
164
233
  next [] unless sub
165
234
  sub.map {|seqs| seqs + last_current}
166
- end, 1)
235
+ end.flatten(1)
167
236
  end
168
237
  prefixes
169
238
  end
@@ -191,10 +260,22 @@ module Sass
191
260
  return [seq1] if seq2.empty?
192
261
 
193
262
  seq1, seq2 = seq1.dup, seq2.dup
194
- init = merge_initial_ops(seq1, seq2)
195
- return unless init
196
- fin = merge_final_ops(seq1, seq2)
197
- return unless fin
263
+ return unless (init = merge_initial_ops(seq1, seq2))
264
+ return unless (fin = merge_final_ops(seq1, seq2))
265
+
266
+ # Make sure there's only one root selector in the output.
267
+ root1 = has_root?(seq1.first) && seq1.shift
268
+ root2 = has_root?(seq2.first) && seq2.shift
269
+ if root1 && root2
270
+ return unless (root = root1.unify(root2))
271
+ seq1.unshift root
272
+ seq2.unshift root
273
+ elsif root1
274
+ seq2.unshift root1
275
+ elsif root2
276
+ seq1.unshift root2
277
+ end
278
+
198
279
  seq1 = group_selectors(seq1)
199
280
  seq2 = group_selectors(seq2)
200
281
  lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
@@ -205,6 +286,7 @@ module Sass
205
286
  end
206
287
 
207
288
  diff = [[init]]
289
+
208
290
  until lcs.empty?
209
291
  diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]
210
292
  seq1.shift
@@ -290,7 +372,7 @@ module Sass
290
372
  elsif sel2.superselector?(sel1)
291
373
  res.unshift sel1, '~'
292
374
  else
293
- merged = sel1.unify(sel2.members, sel2.subject?)
375
+ merged = sel1.unify(sel2)
294
376
  res.unshift [
295
377
  [sel1, '~', sel2, '~'],
296
378
  [sel2, '~', sel1, '~'],
@@ -307,20 +389,20 @@ module Sass
307
389
  if tilde_sel.superselector?(plus_sel)
308
390
  res.unshift plus_sel, '+'
309
391
  else
310
- merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
392
+ merged = plus_sel.unify(tilde_sel)
311
393
  res.unshift [
312
394
  [tilde_sel, '~', plus_sel, '+'],
313
395
  ([merged, '+'] if merged)
314
396
  ].compact
315
397
  end
316
- elsif op1 == '>' && %w[~ +].include?(op2)
398
+ elsif op1 == '>' && %w(~ +).include?(op2)
317
399
  res.unshift sel2, op2
318
400
  seq1.push sel1, op1
319
- elsif op2 == '>' && %w[~ +].include?(op1)
401
+ elsif op2 == '>' && %w(~ +).include?(op1)
320
402
  res.unshift sel1, op1
321
403
  seq2.push sel2, op2
322
404
  elsif op1 == op2
323
- merged = sel1.unify(sel2.members, sel2.subject?)
405
+ merged = sel1.unify(sel2)
324
406
  return unless merged
325
407
  res.unshift merged, op1
326
408
  else
@@ -410,19 +492,26 @@ module Sass
410
492
  seq1.first.is_a?(String) || seq2.first.is_a?(String)
411
493
  # More complex selectors are never superselectors of less complex ones
412
494
  return if seq1.size > seq2.size
413
- return seq1.first.superselector?(seq2.last) if seq1.size == 1
495
+ return seq1.first.superselector?(seq2.last, seq2[0...-1]) if seq1.size == 1
414
496
 
415
497
  _, si = Sass::Util.enum_with_index(seq2).find do |e, i|
416
498
  return if i == seq2.size - 1
417
499
  next if e.is_a?(String)
418
- seq1.first.superselector?(e)
500
+ seq1.first.superselector?(e, seq2[0...i])
419
501
  end
420
502
  return unless si
421
503
 
422
504
  if seq1[1].is_a?(String)
423
505
  return unless seq2[si + 1].is_a?(String)
506
+
424
507
  # .foo ~ .bar is a superselector of .foo + .bar
425
508
  return unless seq1[1] == "~" ? seq2[si + 1] != ">" : seq1[1] == seq2[si + 1]
509
+
510
+ # .foo > .baz is not a superselector of .foo > .bar > .baz or .foo >
511
+ # .bar .baz, despite the fact that .baz is a superselector of .bar >
512
+ # .baz and .bar .baz. Same goes for + and ~.
513
+ return if seq1.length == 3 && seq2.length > 3
514
+
426
515
  return _superselector?(seq1[2..-1], seq2[si + 2..-1])
427
516
  elsif seq2[si + 1].is_a?(String)
428
517
  return unless seq2[si + 1] == ">"
@@ -462,7 +551,7 @@ module Sass
462
551
  def trim(seqses)
463
552
  # Avoid truly horrific quadratic behavior. TODO: I think there
464
553
  # may be a way to get perfect trimming without going quadratic.
465
- return Sass::Util.flatten(seqses, 1) if seqses.size > 100
554
+ return seqses.flatten(1) if seqses.size > 100
466
555
 
467
556
  # Keep the results in a separate array so we can be sure we aren't
468
557
  # comparing against an already-trimmed selector. This ensures that two
@@ -473,7 +562,15 @@ module Sass
473
562
  # separate sequences should limit the quadratic behavior.
474
563
  seqses.each_with_index do |seqs1, i|
475
564
  result[i] = seqs1.reject do |seq1|
476
- max_spec = _sources(seq1).map {|seq| seq.specificity}.max || 0
565
+ # The maximum specificity of the sources that caused [seq1] to be
566
+ # generated. In order for [seq1] to be removed, there must be
567
+ # another selector that's a superselector of it *and* that has
568
+ # specificity greater or equal to this.
569
+ max_spec = _sources(seq1).map do |seq|
570
+ spec = seq.specificity
571
+ spec.is_a?(Range) ? spec.max : spec
572
+ end.max || 0
573
+
477
574
  result.any? do |seqs2|
478
575
  next if seqs1.equal?(seqs2)
479
576
  # Second Law of Extend: the specificity of a generated selector
@@ -481,11 +578,15 @@ module Sass
481
578
  # selector.
482
579
  #
483
580
  # See https://github.com/nex3/sass/issues/324.
484
- seqs2.any? {|seq2| _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)}
581
+ seqs2.any? do |seq2|
582
+ spec2 = _specificity(seq2)
583
+ spec2 = spec2.begin if spec2.is_a?(Range)
584
+ spec2 >= max_spec && _superselector?(seq2, seq1)
585
+ end
485
586
  end
486
587
  end
487
588
  end
488
- Sass::Util.flatten(result, 1)
589
+ result.flatten(1)
489
590
  end
490
591
 
491
592
  def _hash
@@ -525,6 +626,11 @@ module Sass
525
626
  "(#{choices.join ', '})"
526
627
  end.join ' '
527
628
  end
629
+
630
+ def has_root?(sseq)
631
+ sseq.is_a?(SimpleSequence) &&
632
+ sseq.members.any? {|sel| sel.is_a?(Pseudo) && sel.normalized_name == "root"}
633
+ end
528
634
  end
529
635
  end
530
636
  end