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
@@ -5,7 +5,7 @@ module Sass
5
5
  class Simple
6
6
  # The line of the Sass template on which this selector was declared.
7
7
  #
8
- # @return [Fixnum]
8
+ # @return [Integer]
9
9
  attr_accessor :line
10
10
 
11
11
  # The name of the file in which this selector was declared,
@@ -14,28 +14,20 @@ 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
+ #
26
+ # @param opts [Hash] rendering options.
27
+ # @option opts [Symbol] :style The css rendering style.
36
28
  # @return [String]
37
- def to_s
38
- inspect
29
+ def to_s(opts = {})
30
+ Sass::Util.abstract(self)
39
31
  end
40
32
 
41
33
  # Returns a hash code for this selector object.
@@ -44,7 +36,7 @@ module Sass
44
36
  # so if that contains information irrelevant to the identity of the selector,
45
37
  # this should be overridden.
46
38
  #
47
- # @return [Fixnum]
39
+ # @return [Integer]
48
40
  def hash
49
41
  @_hash ||= equality_key.hash
50
42
  end
@@ -58,7 +50,7 @@ module Sass
58
50
  # @param other [Object] The object to test equality against
59
51
  # @return [Boolean] Whether or not this is equal to `other`
60
52
  def eql?(other)
61
- other.class == self.class && other.hash == hash && other.equality_key.eql?(equality_key)
53
+ other.class == self.class && other.hash == hash && other.equality_key == equality_key
62
54
  end
63
55
  alias_method :==, :eql?
64
56
 
@@ -80,14 +72,12 @@ module Sass
80
72
  # by the time extension and unification happen,
81
73
  # this exception will only ever be raised as a result of programmer error
82
74
  def unify(sels)
75
+ return sels.first.unify([self]) if sels.length == 1 && sels.first.is_a?(Universal)
83
76
  return sels if sels.any? {|sel2| eql?(sel2)}
84
77
  sels_with_ix = Sass::Util.enum_with_index(sels)
85
- _, i =
86
- if is_a?(Pseudo) || is_a?(SelectorPseudoClass)
87
- sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.type == :element)}
88
- else
89
- sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
90
- end
78
+ if !is_a?(Pseudo) || (sels.last.is_a?(Pseudo) && sels.last.type == :element)
79
+ _, i = sels_with_ix.find {|sel, _| sel.is_a?(Pseudo)}
80
+ end
91
81
  return sels + [self] unless i
92
82
  sels[0...i] + [self] + sels[i..-1]
93
83
  end
@@ -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,10 +107,10 @@ 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 == ['*']
124
- [ns1 || ns2, true]
110
+ return ns2, true if ns1 == '*'
111
+ return ns1, true if ns2 == '*'
112
+ return nil, false unless ns1 == ns2
113
+ [ns1, true]
125
114
  end
126
115
  end
127
116
  end
@@ -43,11 +43,16 @@ module Sass
43
43
  end
44
44
 
45
45
  def pseudo_elements
46
- @pseudo_elements ||= (members - [base]).
47
- select {|sel| sel.is_a?(Pseudo) && sel.type == :element}
46
+ @pseudo_elements ||= members.select {|sel| sel.is_a?(Pseudo) && sel.type == :element}
48
47
  end
49
48
 
50
- # Returns the non-base, non-pseudo-class selectors in this sequence.
49
+ def selector_pseudo_classes
50
+ @selector_pseudo_classes ||= members.
51
+ select {|sel| sel.is_a?(Pseudo) && sel.type == :class && sel.selector}.
52
+ group_by {|sel| sel.normalized_name}
53
+ end
54
+
55
+ # Returns the non-base, non-pseudo-element selectors in this sequence.
51
56
  #
52
57
  # @return [Set<Simple>]
53
58
  def rest
@@ -81,23 +86,28 @@ module Sass
81
86
  # @return [CommaSequence] This selector, with parent references resolved
82
87
  # @raise [Sass::SyntaxError] If a parent selector is invalid
83
88
  def resolve_parent_refs(super_cseq)
89
+ resolved_members = @members.map do |sel|
90
+ next sel unless sel.is_a?(Pseudo) && sel.selector
91
+ sel.with_selector(sel.selector.resolve_parent_refs(super_cseq, false))
92
+ end.flatten
93
+
84
94
  # Parent selector only appears as the first selector in the sequence
85
- unless (parent = @members.first).is_a?(Parent)
86
- return CommaSequence.new([Sequence.new([self])])
95
+ unless (parent = resolved_members.first).is_a?(Parent)
96
+ return CommaSequence.new([Sequence.new([SimpleSequence.new(resolved_members, subject?)])])
87
97
  end
88
98
 
89
- return super_cseq if @members.size == 1 && parent.suffix.empty?
99
+ return super_cseq if @members.size == 1 && parent.suffix.nil?
90
100
 
91
101
  CommaSequence.new(super_cseq.members.map do |super_seq|
92
102
  members = super_seq.members.dup
93
103
  newline = members.pop if members.last == "\n"
94
104
  unless members.last.is_a?(SimpleSequence)
95
105
  raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
96
- super_seq.to_a.join + '"')
106
+ super_seq.to_s + '"')
97
107
  end
98
108
 
99
109
  parent_sub = members.last.members
100
- unless parent.suffix.empty?
110
+ unless parent.suffix.nil?
101
111
  parent_sub = parent_sub.dup
102
112
  parent_sub[-1] = parent_sub.last.dup
103
113
  case parent_sub.last
@@ -108,39 +118,72 @@ module Sass
108
118
  parent_sub.last.name + parent.suffix,
109
119
  parent_sub.last.namespace)
110
120
  when Sass::Selector::Pseudo
111
- if parent_sub.last.arg
121
+ if parent_sub.last.arg || parent_sub.last.selector
112
122
  raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
113
- super_seq.to_a.join + '"')
123
+ super_seq.to_s + '"')
114
124
  end
115
- parent_sub[-1] = parent_sub.last.class.new(
125
+ parent_sub[-1] = Sass::Selector::Pseudo.new(
116
126
  parent_sub.last.type,
117
127
  parent_sub.last.name + parent.suffix,
118
- nil)
128
+ nil, nil)
119
129
  else
120
130
  raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
121
- super_seq.to_a.join + '"')
131
+ super_seq.to_s + '"')
122
132
  end
123
133
  end
124
134
 
125
135
  Sequence.new(members[0...-1] +
126
- [SimpleSequence.new(parent_sub + @members[1..-1], subject?)] +
136
+ [SimpleSequence.new(parent_sub + resolved_members[1..-1], subject?)] +
127
137
  [newline].compact)
128
- end)
138
+ end)
129
139
  end
130
140
 
131
- # Non-destrucively extends this selector with the extensions specified in a hash
141
+ # Non-destructively extends this selector with the extensions specified in a hash
132
142
  # (which should come from {Sass::Tree::Visitors::Cssize}).
133
143
  #
134
- # @overload def do_extend(extends, parent_directives)
135
144
  # @param extends [{Selector::Simple =>
136
145
  # Sass::Tree::Visitors::Cssize::Extend}]
137
146
  # The extensions to perform on this selector
138
147
  # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
139
148
  # The directives containing this selector.
149
+ # @param seen [Set<Array<Selector::Simple>>]
150
+ # The set of simple sequences that are currently being replaced.
151
+ # @param original [Boolean]
152
+ # Whether this is the original selector being extended, as opposed to
153
+ # the result of a previous extension that's being re-extended.
140
154
  # @return [Array<Sequence>] A list of selectors generated
141
155
  # by extending this selector with `extends`.
142
156
  # @see CommaSequence#do_extend
143
- def do_extend(extends, parent_directives, seen = Set.new)
157
+ def do_extend(extends, parent_directives, replace, seen)
158
+ seen_with_pseudo_selectors = seen.dup
159
+
160
+ modified_original = false
161
+ members = self.members.map do |sel|
162
+ next sel unless sel.is_a?(Pseudo) && sel.selector
163
+ next sel if seen.include?([sel])
164
+ extended = sel.selector.do_extend(extends, parent_directives, replace, seen, false)
165
+ next sel if extended == sel.selector
166
+ extended.members.reject! {|seq| seq.has_placeholder?}
167
+
168
+ # For `:not()`, we usually want to get rid of any complex
169
+ # selectors because that will cause the selector to fail to
170
+ # parse on all browsers at time of writing. We can keep them
171
+ # if either the original selector had a complex selector, or
172
+ # the result of extending has only complex selectors,
173
+ # because either way we aren't breaking anything that isn't
174
+ # already broken.
175
+ if sel.normalized_name == 'not' &&
176
+ (sel.selector.members.none? {|seq| seq.members.length > 1} &&
177
+ extended.members.any? {|seq| seq.members.length == 1})
178
+ extended.members.reject! {|seq| seq.members.length > 1}
179
+ end
180
+
181
+ modified_original = true
182
+ result = sel.with_selector(extended)
183
+ result.each {|new_sel| seen_with_pseudo_selectors << [new_sel]}
184
+ result
185
+ end.flatten
186
+
144
187
  groups = Sass::Util.group_by_to_a(extends[members.to_set]) {|ex| ex.extender}
145
188
  groups.map! do |seq, group|
146
189
  sels = group.map {|e| e.target}.flatten
@@ -149,7 +192,7 @@ module Sass
149
192
 
150
193
  self_without_sel = Sass::Util.array_minus(members, sels)
151
194
  group.each {|e| e.result = :failed_to_unify unless e.result == :succeeded}
152
- unified = seq.members.last.unify(self_without_sel, subject?)
195
+ unified = seq.members.last.unify(SimpleSequence.new(self_without_sel, subject?))
153
196
  next unless unified
154
197
  group.each {|e| e.result = :succeeded}
155
198
  group.each {|e| check_directives_match!(e, parent_directives)}
@@ -159,19 +202,30 @@ module Sass
159
202
  end
160
203
  groups.compact!
161
204
  groups.map! do |sels, seq|
162
- seen.include?(sels) ? [] : seq.do_extend(extends, parent_directives, seen + [sels])
205
+ next [] if seen.include?(sels)
206
+ seq.do_extend(
207
+ extends, parent_directives, false, seen_with_pseudo_selectors + [sels], false)
163
208
  end
164
209
  groups.flatten!
210
+
211
+ if modified_original || !replace || groups.empty?
212
+ # First Law of Extend: the result of extending a selector should
213
+ # (almost) always contain the base selector.
214
+ #
215
+ # See https://github.com/nex3/sass/issues/324.
216
+ original = Sequence.new([SimpleSequence.new(members, @subject, source_range)])
217
+ original.add_sources! sources
218
+ groups.unshift original
219
+ end
165
220
  groups.uniq!
166
221
  groups
167
222
  end
168
223
 
169
- # Unifies this selector with another {SimpleSequence}'s
170
- # {SimpleSequence#members members array}, returning another `SimpleSequence`
171
- # that matches both this selector and the input selector.
224
+ # Unifies this selector with another {SimpleSequence}, returning
225
+ # another `SimpleSequence` that is a subselector of both input
226
+ # selectors.
172
227
  #
173
- # @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
174
- # @param other_subject [Boolean] Whether the other {SimpleSequence} being merged is a subject.
228
+ # @param other [SimpleSequence]
175
229
  # @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
176
230
  # or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
177
231
  # @raise [Sass::SyntaxError] If this selector cannot be unified.
@@ -180,13 +234,13 @@ module Sass
180
234
  # Since these selectors should be resolved
181
235
  # by the time extension and unification happen,
182
236
  # this exception will only ever be raised as a result of programmer error
183
- def unify(sels, other_subject)
184
- sseq = members.inject(sels) do |member, sel|
237
+ def unify(other)
238
+ sseq = members.inject(other.members) do |member, sel|
185
239
  return unless member
186
240
  sel.unify(member)
187
241
  end
188
242
  return unless sseq
189
- SimpleSequence.new(sseq, other_subject || subject?)
243
+ SimpleSequence.new(sseq, other.subject? || subject?)
190
244
  end
191
245
 
192
246
  # Returns whether or not this selector matches all elements
@@ -195,17 +249,44 @@ module Sass
195
249
  # @example
196
250
  # (.foo).superselector?(.foo.bar) #=> true
197
251
  # (.foo).superselector?(.bar) #=> false
198
- # @param sseq [SimpleSequence]
252
+ # @param their_sseq [SimpleSequence]
253
+ # @param parents [Array<SimpleSequence, String>] The parent selectors of `their_sseq`, if any.
199
254
  # @return [Boolean]
200
- def superselector?(sseq)
201
- (base.nil? || base.eql?(sseq.base)) &&
202
- pseudo_elements.eql?(sseq.pseudo_elements) &&
203
- rest.subset?(sseq.rest)
255
+ def superselector?(their_sseq, parents = [])
256
+ return false unless base.nil? || base.eql?(their_sseq.base)
257
+ return false unless pseudo_elements.eql?(their_sseq.pseudo_elements)
258
+ our_spcs = selector_pseudo_classes
259
+ their_spcs = their_sseq.selector_pseudo_classes
260
+
261
+ # Some psuedo-selectors can be subselectors of non-pseudo selectors.
262
+ # Pull those out here so we can efficiently check against them below.
263
+ their_subselector_pseudos = %w(matches any nth-child nth-last-child).
264
+ map {|name| their_spcs[name] || []}.flatten
265
+
266
+ # If `self`'s non-pseudo simple selectors aren't a subset of `their_sseq`'s,
267
+ # it's definitely not a superselector. This also considers being matched
268
+ # by `:matches` or `:any`.
269
+ return false unless rest.all? do |our_sel|
270
+ next true if our_sel.is_a?(Pseudo) && our_sel.selector
271
+ next true if their_sseq.rest.include?(our_sel)
272
+ their_subselector_pseudos.any? do |their_pseudo|
273
+ their_pseudo.selector.members.all? do |their_seq|
274
+ next false unless their_seq.members.length == 1
275
+ their_sseq = their_seq.members.first
276
+ next false unless their_sseq.is_a?(SimpleSequence)
277
+ their_sseq.rest.include?(our_sel)
278
+ end
279
+ end
280
+ end
281
+
282
+ our_spcs.all? do |_name, pseudos|
283
+ pseudos.all? {|pseudo| pseudo.superselector?(their_sseq, parents)}
284
+ end
204
285
  end
205
286
 
206
- # @see Simple#to_a
207
- def to_a
208
- res = @members.map {|sel| sel.to_a}.flatten
287
+ # @see Simple#to_s
288
+ def to_s(opts = {})
289
+ res = @members.map {|m| m.to_s(opts)}.join
209
290
  res << '!' if subject?
210
291
  res
211
292
  end
@@ -215,7 +296,9 @@ module Sass
215
296
  #
216
297
  # @return [String]
217
298
  def inspect
218
- members.map {|m| m.inspect}.join
299
+ res = members.map {|m| m.inspect}.join
300
+ res << '!' if subject?
301
+ res
219
302
  end
220
303
 
221
304
  # Return a copy of this simple sequence with `sources` merged into the
@@ -249,12 +332,12 @@ MESSAGE
249
332
  end
250
333
 
251
334
  def _hash
252
- [base, Sass::Util.set_hash(rest)].hash
335
+ [base, rest.hash].hash
253
336
  end
254
337
 
255
338
  def _eql?(other)
256
339
  other.base.eql?(base) && other.pseudo_elements == pseudo_elements &&
257
- Sass::Util.set_eql?(other.rest, rest) && other.subject? == subject?
340
+ other.rest.eql?(rest) && other.subject? == subject?
258
341
  end
259
342
  end
260
343
  end