oreorenasass 3.4.4 → 3.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +50 -70
  4. data/Rakefile +5 -26
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/sass +1 -1
  8. data/bin/scss +1 -1
  9. data/lib/sass.rb +12 -19
  10. data/lib/sass/cache_stores/base.rb +2 -2
  11. data/lib/sass/cache_stores/chain.rb +1 -2
  12. data/lib/sass/cache_stores/filesystem.rb +5 -1
  13. data/lib/sass/cache_stores/memory.rb +1 -1
  14. data/lib/sass/cache_stores/null.rb +2 -2
  15. data/lib/sass/callbacks.rb +0 -1
  16. data/lib/sass/css.rb +13 -11
  17. data/lib/sass/engine.rb +173 -424
  18. data/lib/sass/environment.rb +58 -148
  19. data/lib/sass/error.rb +14 -11
  20. data/lib/sass/exec.rb +703 -5
  21. data/lib/sass/importers/base.rb +6 -49
  22. data/lib/sass/importers/filesystem.rb +19 -44
  23. data/lib/sass/logger.rb +4 -1
  24. data/lib/sass/logger/base.rb +4 -2
  25. data/lib/sass/logger/log_level.rb +7 -3
  26. data/lib/sass/media.rb +23 -20
  27. data/lib/sass/plugin.rb +7 -7
  28. data/lib/sass/plugin/compiler.rb +145 -304
  29. data/lib/sass/plugin/configuration.rb +23 -18
  30. data/lib/sass/plugin/merb.rb +1 -1
  31. data/lib/sass/plugin/staleness_checker.rb +3 -3
  32. data/lib/sass/repl.rb +3 -3
  33. data/lib/sass/script.rb +8 -35
  34. data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
  35. data/lib/sass/script/bool.rb +18 -0
  36. data/lib/sass/script/color.rb +606 -0
  37. data/lib/sass/script/css_lexer.rb +4 -8
  38. data/lib/sass/script/css_parser.rb +2 -5
  39. data/lib/sass/script/funcall.rb +245 -0
  40. data/lib/sass/script/functions.rb +408 -1491
  41. data/lib/sass/script/interpolation.rb +79 -0
  42. data/lib/sass/script/lexer.rb +68 -172
  43. data/lib/sass/script/list.rb +85 -0
  44. data/lib/sass/script/literal.rb +221 -0
  45. data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
  46. data/lib/sass/script/{value/null.rb → null.rb} +7 -14
  47. data/lib/sass/script/{value/number.rb → number.rb} +75 -152
  48. data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
  49. data/lib/sass/script/parser.rb +110 -245
  50. data/lib/sass/script/string.rb +51 -0
  51. data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
  52. data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
  53. data/lib/sass/script/variable.rb +58 -0
  54. data/lib/sass/scss/css_parser.rb +3 -9
  55. data/lib/sass/scss/parser.rb +421 -450
  56. data/lib/sass/scss/rx.rb +11 -19
  57. data/lib/sass/scss/static_parser.rb +7 -321
  58. data/lib/sass/selector.rb +194 -68
  59. data/lib/sass/selector/abstract_sequence.rb +14 -29
  60. data/lib/sass/selector/comma_sequence.rb +25 -108
  61. data/lib/sass/selector/sequence.rb +66 -159
  62. data/lib/sass/selector/simple.rb +25 -23
  63. data/lib/sass/selector/simple_sequence.rb +63 -173
  64. data/lib/sass/shared.rb +1 -1
  65. data/lib/sass/supports.rb +15 -13
  66. data/lib/sass/tree/charset_node.rb +1 -1
  67. data/lib/sass/tree/comment_node.rb +3 -3
  68. data/lib/sass/tree/css_import_node.rb +11 -11
  69. data/lib/sass/tree/debug_node.rb +2 -2
  70. data/lib/sass/tree/directive_node.rb +4 -21
  71. data/lib/sass/tree/each_node.rb +8 -8
  72. data/lib/sass/tree/extend_node.rb +7 -14
  73. data/lib/sass/tree/for_node.rb +4 -4
  74. data/lib/sass/tree/function_node.rb +4 -9
  75. data/lib/sass/tree/if_node.rb +1 -1
  76. data/lib/sass/tree/import_node.rb +5 -4
  77. data/lib/sass/tree/media_node.rb +14 -4
  78. data/lib/sass/tree/mixin_def_node.rb +4 -4
  79. data/lib/sass/tree/mixin_node.rb +8 -21
  80. data/lib/sass/tree/node.rb +12 -54
  81. data/lib/sass/tree/prop_node.rb +20 -39
  82. data/lib/sass/tree/return_node.rb +2 -3
  83. data/lib/sass/tree/root_node.rb +3 -19
  84. data/lib/sass/tree/rule_node.rb +22 -35
  85. data/lib/sass/tree/supports_node.rb +13 -0
  86. data/lib/sass/tree/trace_node.rb +1 -2
  87. data/lib/sass/tree/variable_node.rb +3 -9
  88. data/lib/sass/tree/visitors/base.rb +8 -5
  89. data/lib/sass/tree/visitors/check_nesting.rb +19 -49
  90. data/lib/sass/tree/visitors/convert.rb +56 -74
  91. data/lib/sass/tree/visitors/cssize.rb +74 -202
  92. data/lib/sass/tree/visitors/deep_copy.rb +5 -10
  93. data/lib/sass/tree/visitors/extend.rb +7 -7
  94. data/lib/sass/tree/visitors/perform.rb +185 -278
  95. data/lib/sass/tree/visitors/set_options.rb +6 -20
  96. data/lib/sass/tree/visitors/to_css.rb +81 -234
  97. data/lib/sass/tree/warn_node.rb +2 -2
  98. data/lib/sass/tree/while_node.rb +2 -2
  99. data/lib/sass/util.rb +152 -522
  100. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  101. data/lib/sass/util/subset_map.rb +3 -4
  102. data/lib/sass/util/test.rb +1 -0
  103. data/lib/sass/version.rb +22 -20
  104. data/test/Gemfile +3 -0
  105. data/test/Gemfile.lock +10 -0
  106. data/test/sass/cache_test.rb +20 -62
  107. data/test/sass/callbacks_test.rb +1 -1
  108. data/test/sass/conversion_test.rb +2 -296
  109. data/test/sass/css2sass_test.rb +4 -23
  110. data/test/sass/engine_test.rb +354 -411
  111. data/test/sass/exec_test.rb +2 -2
  112. data/test/sass/extend_test.rb +145 -324
  113. data/test/sass/functions_test.rb +86 -873
  114. data/test/sass/importer_test.rb +21 -241
  115. data/test/sass/logger_test.rb +1 -1
  116. data/test/sass/more_results/more_import.css +1 -1
  117. data/test/sass/plugin_test.rb +26 -16
  118. data/test/sass/results/compact.css +1 -1
  119. data/test/sass/results/complex.css +4 -4
  120. data/test/sass/results/expanded.css +1 -1
  121. data/test/sass/results/import.css +1 -1
  122. data/test/sass/results/import_charset_ibm866.css +2 -2
  123. data/test/sass/results/mixins.css +17 -17
  124. data/test/sass/results/nested.css +1 -1
  125. data/test/sass/results/parent_ref.css +2 -2
  126. data/test/sass/results/script.css +3 -3
  127. data/test/sass/results/scss_import.css +1 -1
  128. data/test/sass/script_conversion_test.rb +7 -36
  129. data/test/sass/script_test.rb +53 -485
  130. data/test/sass/scss/css_test.rb +28 -143
  131. data/test/sass/scss/rx_test.rb +4 -4
  132. data/test/sass/scss/scss_test.rb +325 -2119
  133. data/test/sass/templates/scss_import.scss +1 -2
  134. data/test/sass/test_helper.rb +1 -1
  135. data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
  136. data/test/sass/util/subset_map_test.rb +2 -2
  137. data/test/sass/util_test.rb +1 -86
  138. data/test/test_helper.rb +8 -37
  139. metadata +19 -66
  140. data/lib/sass/exec/base.rb +0 -187
  141. data/lib/sass/exec/sass_convert.rb +0 -264
  142. data/lib/sass/exec/sass_scss.rb +0 -424
  143. data/lib/sass/features.rb +0 -47
  144. data/lib/sass/script/tree.rb +0 -16
  145. data/lib/sass/script/tree/funcall.rb +0 -306
  146. data/lib/sass/script/tree/interpolation.rb +0 -118
  147. data/lib/sass/script/tree/list_literal.rb +0 -77
  148. data/lib/sass/script/tree/literal.rb +0 -45
  149. data/lib/sass/script/tree/map_literal.rb +0 -64
  150. data/lib/sass/script/tree/selector.rb +0 -26
  151. data/lib/sass/script/tree/variable.rb +0 -57
  152. data/lib/sass/script/value.rb +0 -11
  153. data/lib/sass/script/value/base.rb +0 -240
  154. data/lib/sass/script/value/bool.rb +0 -35
  155. data/lib/sass/script/value/color.rb +0 -680
  156. data/lib/sass/script/value/helpers.rb +0 -262
  157. data/lib/sass/script/value/list.rb +0 -113
  158. data/lib/sass/script/value/map.rb +0 -70
  159. data/lib/sass/script/value/string.rb +0 -97
  160. data/lib/sass/selector/pseudo.rb +0 -256
  161. data/lib/sass/source/map.rb +0 -210
  162. data/lib/sass/source/position.rb +0 -39
  163. data/lib/sass/source/range.rb +0 -41
  164. data/lib/sass/stack.rb +0 -120
  165. data/lib/sass/tree/at_root_node.rb +0 -83
  166. data/lib/sass/tree/error_node.rb +0 -18
  167. data/lib/sass/tree/keyframe_rule_node.rb +0 -15
  168. data/lib/sass/util/cross_platform_random.rb +0 -19
  169. data/lib/sass/util/normalized_map.rb +0 -130
  170. data/lib/sass/util/ordered_hash.rb +0 -192
  171. data/test/sass/compiler_test.rb +0 -232
  172. data/test/sass/encoding_test.rb +0 -219
  173. data/test/sass/source_map_test.rb +0 -977
  174. data/test/sass/superselector_test.rb +0 -191
  175. data/test/sass/util/normalized_map_test.rb +0 -51
  176. data/test/sass/value_helpers_test.rb +0 -179
@@ -3,8 +3,8 @@ module Sass
3
3
  # The abstract parent class of the various selector sequence classes.
4
4
  #
5
5
  # All subclasses should implement a `members` method that returns an array
6
- # of object that respond to `#line=` and `#filename=`, as well as a `to_s`
7
- # method that returns the string representation of the selector.
6
+ # of object that respond to `#line=` and `#filename=`, as well as a `to_a`
7
+ # method that returns an array of strings and script nodes.
8
8
  class AbstractSequence
9
9
  # The line of the Sass template on which this selector was declared.
10
10
  #
@@ -55,33 +55,29 @@ module Sass
55
55
  # @param other [Object] The object to test equality against
56
56
  # @return [Boolean] Whether or not this is equal to `other`
57
57
  def eql?(other)
58
- other.class == self.class && other.hash == hash && _eql?(other)
58
+ other.class == self.class && other.hash == self.hash && _eql?(other)
59
59
  end
60
60
  alias_method :==, :eql?
61
61
 
62
62
  # Whether or not this selector sequence contains a placeholder selector.
63
63
  # Checks recursively.
64
64
  def has_placeholder?
65
- @has_placeholder ||= members.any? do |m|
66
- next m.has_placeholder? if m.is_a?(AbstractSequence)
67
- next m.selector && m.selector.has_placeholder? if m.is_a?(Pseudo)
68
- m.is_a?(Placeholder)
69
- end
65
+ @has_placeholder ||=
66
+ members.any? {|m| m.is_a?(AbstractSequence) ? m.has_placeholder? : m.is_a?(Placeholder)}
70
67
  end
71
68
 
72
- # Returns the selector string.
69
+ # Converts the selector into a string. This is the standard selector
70
+ # string, along with any SassScript interpolation that may exist.
73
71
  #
74
72
  # @return [String]
75
73
  def to_s
76
- Sass::Util.abstract(self)
74
+ to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
77
75
  end
78
76
 
79
- # Returns the specificity of the selector.
77
+ # Returns the specificity of the selector as an integer. The base is given
78
+ # by {Sass::Selector::SPECIFICITY_BASE}.
80
79
  #
81
- # The base is given by {Sass::Selector::SPECIFICITY_BASE}. This can be a
82
- # number or a range representing possible specificities.
83
- #
84
- # @return [Fixnum, Range]
80
+ # @return [Fixnum]
85
81
  def specificity
86
82
  _specificity(members)
87
83
  end
@@ -89,20 +85,9 @@ module Sass
89
85
  protected
90
86
 
91
87
  def _specificity(arr)
92
- min = 0
93
- max = 0
94
- arr.each do |m|
95
- next if m.is_a?(String)
96
- spec = m.specificity
97
- if spec.is_a?(Range)
98
- min += spec.begin
99
- max += spec.end
100
- else
101
- min += spec
102
- max += spec
103
- end
104
- end
105
- min == max ? min : (min..max)
88
+ spec = 0
89
+ arr.map {|m| spec += m.is_a?(String) ? 0 : m.specificity}
90
+ spec
106
91
  end
107
92
  end
108
93
  end
@@ -18,28 +18,24 @@ module Sass
18
18
  # handling commas appropriately.
19
19
  #
20
20
  # @param super_cseq [CommaSequence] The parent selector
21
- # @param implicit_parent [Boolean] Whether the the parent
22
- # selector should automatically be prepended to the resolved
23
- # selector if it contains no parent refs.
24
21
  # @return [CommaSequence] This selector, with parent references resolved
25
22
  # @raise [Sass::SyntaxError] If a parent selector is invalid
26
- def resolve_parent_refs(super_cseq, implicit_parent = true)
23
+ def resolve_parent_refs(super_cseq)
27
24
  if super_cseq.nil?
28
25
  if @members.any? do |sel|
29
- sel.members.any? do |sel_or_op|
30
- sel_or_op.is_a?(SimpleSequence) &&
31
- sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
32
- end
33
- end
34
- raise Sass::SyntaxError.new(
35
- "Base-level rules cannot contain the parent-selector-referencing character '&'.")
26
+ sel.members.any? do |sel_or_op|
27
+ sel_or_op.is_a?(SimpleSequence) && sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
28
+ end
29
+ end
30
+ raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '&'.")
36
31
  end
37
32
  return self
38
33
  end
39
34
 
40
- CommaSequence.new(Sass::Util.flatten_vertically(@members.map do |seq|
41
- seq.resolve_parent_refs(super_cseq, implicit_parent).members
42
- end))
35
+ CommaSequence.new(
36
+ super_cseq.members.map do |super_seq|
37
+ @members.map {|seq| seq.resolve_parent_refs(super_seq)}
38
+ end.flatten)
43
39
  end
44
40
 
45
41
  # Non-destrucively extends this selector with the extensions specified in a hash
@@ -53,99 +49,18 @@ module Sass
53
49
  # The extensions to perform on this selector
54
50
  # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
55
51
  # The directives containing this selector.
56
- # @param replace [Boolean]
57
- # Whether to replace the original selector entirely or include
58
- # it in the result.
59
- # @param seen [Set<Array<Selector::Simple>>]
60
- # The set of simple sequences that are currently being replaced.
61
- # @param original [Boolean]
62
- # Whether this is the original selector being extended, as opposed to
63
- # the result of a previous extension that's being re-extended.
64
52
  # @return [CommaSequence] A copy of this selector,
65
53
  # with extensions made according to `extends`
66
- def do_extend(extends, parent_directives = [], replace = false, seen = Set.new,
67
- original = true)
54
+ def do_extend(extends, parent_directives)
68
55
  CommaSequence.new(members.map do |seq|
69
- seq.do_extend(extends, parent_directives, replace, seen, original)
70
- end.flatten)
71
- end
72
-
73
- # Returns whether or not this selector matches all elements
74
- # that the given selector matches (as well as possibly more).
75
- #
76
- # @example
77
- # (.foo).superselector?(.foo.bar) #=> true
78
- # (.foo).superselector?(.bar) #=> false
79
- # @param cseq [CommaSequence]
80
- # @return [Boolean]
81
- def superselector?(cseq)
82
- cseq.members.all? {|seq1| members.any? {|seq2| seq2.superselector?(seq1)}}
83
- end
84
-
85
- # Populates a subset map that can then be used to extend
86
- # selectors. This registers an extension with this selector as
87
- # the extender and `extendee` as the extendee.
88
- #
89
- # @param extends [Sass::Util::SubsetMap{Selector::Simple =>
90
- # Sass::Tree::Visitors::Cssize::Extend}]
91
- # The subset map representing the extensions to perform.
92
- # @param extendee [CommaSequence] The selector being extended.
93
- # @param extend_node [Sass::Tree::ExtendNode]
94
- # The node that caused this extension.
95
- # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
96
- # The parent directives containing `extend_node`.
97
- # @raise [Sass::SyntaxError] if this extension is invalid.
98
- def populate_extends(extends, extendee, extend_node = nil, parent_directives = [])
99
- extendee.members.each do |seq|
100
- if seq.members.size > 1
101
- raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend nested selectors")
102
- end
103
-
104
- sseq = seq.members.first
105
- if !sseq.is_a?(Sass::Selector::SimpleSequence)
106
- raise Sass::SyntaxError.new("Can't extend #{seq}: invalid selector")
107
- elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
108
- raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend parent selectors")
109
- end
110
-
111
- sel = sseq.members
112
- members.each do |member|
113
- unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
114
- raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
115
- end
116
-
117
- extends[sel] = Sass::Tree::Visitors::Cssize::Extend.new(
118
- member, sel, extend_node, parent_directives, :not_found)
119
- end
120
- end
121
- end
122
-
123
- # Unifies this with another comma selector to produce a selector
124
- # that matches (a subset of) the intersection of the two inputs.
125
- #
126
- # @param other [CommaSequence]
127
- # @return [CommaSequence, nil] The unified selector, or nil if unification failed.
128
- # @raise [Sass::SyntaxError] If this selector cannot be unified.
129
- # This will only ever occur when a dynamic selector,
130
- # such as {Parent} or {Interpolation}, is used in unification.
131
- # Since these selectors should be resolved
132
- # by the time extension and unification happen,
133
- # this exception will only ever be raised as a result of programmer error
134
- def unify(other)
135
- results = members.map {|seq1| other.members.map {|seq2| seq1.unify(seq2)}}.flatten.compact
136
- results.empty? ? nil : CommaSequence.new(results.map {|cseq| cseq.members}.flatten)
137
- end
138
-
139
- # Returns a SassScript representation of this selector.
140
- #
141
- # @return [Sass::Script::Value::List]
142
- def to_sass_script
143
- Sass::Script::Value::List.new(members.map do |seq|
144
- Sass::Script::Value::List.new(seq.members.map do |component|
145
- next if component == "\n"
146
- Sass::Script::Value::String.new(component.to_s)
147
- end.compact, :space)
148
- end, :comma)
56
+ extended = seq.do_extend(extends, parent_directives)
57
+ # First Law of Extend: the result of extending a selector should
58
+ # always contain the base selector.
59
+ #
60
+ # See https://github.com/nex3/sass/issues/324.
61
+ extended.unshift seq unless seq.has_placeholder? || extended.include?(seq)
62
+ extended
63
+ end.flatten)
149
64
  end
150
65
 
151
66
  # Returns a string representation of the sequence.
@@ -156,9 +71,11 @@ module Sass
156
71
  members.map {|m| m.inspect}.join(", ")
157
72
  end
158
73
 
159
- # @see AbstractSequence#to_s
160
- def to_s
161
- @members.join(", ").gsub(", \n", ",\n")
74
+ # @see Simple#to_a
75
+ def to_a
76
+ arr = Sass::Util.intersperse(@members.map {|m| m.to_a}, ", ").flatten
77
+ arr.delete("\n")
78
+ arr
162
79
  end
163
80
 
164
81
  private
@@ -168,7 +85,7 @@ module Sass
168
85
  end
169
86
 
170
87
  def _eql?(other)
171
- other.class == self.class && other.members.eql?(members)
88
+ other.class == self.class && other.members.eql?(self.members)
172
89
  end
173
90
  end
174
91
  end
@@ -33,8 +33,7 @@ module Sass
33
33
  # @return [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>]
34
34
  attr_reader :members
35
35
 
36
- # @param seqs_and_ops [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>]
37
- # See \{#members}
36
+ # @param seqs_and_ops [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>] See \{#members}
38
37
  def initialize(seqs_and_ops)
39
38
  @members = seqs_and_ops
40
39
  end
@@ -43,94 +42,51 @@ module Sass
43
42
  # by replacing them with the given parent selector,
44
43
  # handling commas appropriately.
45
44
  #
46
- # @param super_cseq [CommaSequence] The parent selector
47
- # @param implicit_parent [Boolean] Whether the the parent
48
- # selector should automatically be prepended to the resolved
49
- # selector if it contains no parent refs.
50
- # @return [CommaSequence] This selector, with parent references resolved
45
+ # @param super_seq [Sequence] The parent selector sequence
46
+ # @return [Sequence] This selector, with parent references resolved
51
47
  # @raise [Sass::SyntaxError] If a parent selector is invalid
52
- def resolve_parent_refs(super_cseq, implicit_parent)
48
+ def resolve_parent_refs(super_seq)
53
49
  members = @members.dup
54
50
  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
58
- return CommaSequence.new([self]) if !implicit_parent && !contains_parent_ref
59
-
60
- unless contains_parent_ref
51
+ unless members.any? do |seq_or_op|
52
+ seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
53
+ end
61
54
  old_members, members = members, []
62
55
  members << nl if nl
63
56
  members << SimpleSequence.new([Parent.new], false)
64
57
  members += old_members
65
58
  end
66
59
 
67
- CommaSequence.new(Sass::Util.paths(members.map do |sseq_or_op|
68
- next [sseq_or_op] unless sseq_or_op.is_a?(SimpleSequence)
69
- sseq_or_op.resolve_parent_refs(super_cseq).members
70
- end).map do |path|
71
- Sequence.new(path.map do |seq_or_op|
72
- next seq_or_op unless seq_or_op.is_a?(Sequence)
73
- seq_or_op.members
60
+ Sequence.new(
61
+ members.map do |seq_or_op|
62
+ next seq_or_op unless seq_or_op.is_a?(SimpleSequence)
63
+ seq_or_op.resolve_parent_refs(super_seq)
74
64
  end.flatten)
75
- end)
76
65
  end
77
66
 
78
67
  # Non-destructively extends this selector with the extensions specified in a hash
79
68
  # (which should come from {Sass::Tree::Visitors::Cssize}).
80
69
  #
70
+ # @overload def do_extend(extends, parent_directives)
81
71
  # @param extends [Sass::Util::SubsetMap{Selector::Simple =>
82
72
  # Sass::Tree::Visitors::Cssize::Extend}]
83
73
  # The extensions to perform on this selector
84
74
  # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
85
75
  # 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.
94
76
  # @return [Array<Sequence>] A list of selectors generated
95
77
  # by extending this selector with `extends`.
96
78
  # These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
97
79
  # @see CommaSequence#do_extend
98
- def do_extend(extends, parent_directives, replace, seen, original)
80
+ def do_extend(extends, parent_directives, seen = Set.new)
99
81
  extended_not_expanded = members.map do |sseq_or_op|
100
82
  next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
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}
83
+ extended = sseq_or_op.do_extend(extends, parent_directives, seen)
84
+ choices = extended.map {|seq| seq.members}
85
+ choices.unshift([sseq_or_op]) unless extended.any? {|seq| seq.superselector?(sseq_or_op)}
86
+ choices
110
87
  end
111
88
  weaves = Sass::Util.paths(extended_not_expanded).map {|path| weave(path)}
112
- trim(weaves).map {|p| Sequence.new(p)}
113
- end
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)})
89
+ Sass::Util.flatten(trim(weaves), 1).map {|p| Sequence.new(p)}
134
90
  end
135
91
 
136
92
  # Returns whether or not this selector matches all elements
@@ -139,15 +95,18 @@ module Sass
139
95
  # @example
140
96
  # (.foo).superselector?(.foo.bar) #=> true
141
97
  # (.foo).superselector?(.bar) #=> false
142
- # @param cseq [Sequence]
98
+ # (.bar .foo).superselector?(.foo) #=> false
99
+ # @param sseq [SimpleSequence]
143
100
  # @return [Boolean]
144
- def superselector?(seq)
145
- _superselector?(members, seq.members)
101
+ def superselector?(sseq)
102
+ return false unless members.size == 1
103
+ members.last.superselector?(sseq)
146
104
  end
147
105
 
148
- # @see AbstractSequence#to_s
149
- def to_s
150
- @members.join(" ").gsub(/ ?\n ?/, "\n")
106
+ # @see Simple#to_a
107
+ def to_a
108
+ ary = @members.map {|seq_or_op| seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op}
109
+ Sass::Util.intersperse(ary, " ").flatten.compact
151
110
  end
152
111
 
153
112
  # Returns a string representation of the sequence.
@@ -167,63 +126,31 @@ module Sass
167
126
  members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}
168
127
  end
169
128
 
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
-
199
129
  private
200
130
 
201
- # Conceptually, this expands "parenthesized selectors". That is, if we
202
- # have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually expands
203
- # into `.D .C, .D (.A .B)`, and this function translates `.D (.A .B)` into
204
- # `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B` would also be
205
- # required, but including merged selectors results in exponential output
206
- # for very little gain.
131
+ # Conceptually, this expands "parenthesized selectors".
132
+ # That is, if we have `.A .B {@extend .C}` and `.D .C {...}`,
133
+ # this conceptually expands into `.D .C, .D (.A .B)`,
134
+ # and this function translates `.D (.A .B)` into `.D .A .B, .A.D .B, .D .A .B`.
207
135
  #
208
- # @param path [Array<Array<SimpleSequence or String>>]
209
- # A list of parenthesized selector groups.
136
+ # @param path [Array<Array<SimpleSequence or String>>] A list of parenthesized selector groups.
210
137
  # @return [Array<Array<SimpleSequence or String>>] A list of fully-expanded selectors.
211
138
  def weave(path)
212
139
  # This function works by moving through the selector path left-to-right,
213
- # building all possible prefixes simultaneously.
214
- prefixes = [[]]
140
+ # building all possible prefixes simultaneously. These prefixes are
141
+ # `befores`, while the remaining parenthesized suffixes is `afters`.
142
+ befores = [[]]
143
+ afters = path.dup
215
144
 
216
- path.each do |current|
217
- next if current.empty?
218
- current = current.dup
145
+ until afters.empty?
146
+ current = afters.shift.dup
219
147
  last_current = [current.pop]
220
- prefixes = Sass::Util.flatten(prefixes.map do |prefix|
221
- sub = subweave(prefix, current)
222
- next [] unless sub
223
- sub.map {|seqs| seqs + last_current}
224
- end, 1)
148
+ befores = Sass::Util.flatten(befores.map do |before|
149
+ next [] unless sub = subweave(before, current)
150
+ sub.map {|seqs| seqs + last_current}
151
+ end, 1)
225
152
  end
226
- prefixes
153
+ return befores
227
154
  end
228
155
 
229
156
  # This interweaves two lists of selectors,
@@ -249,10 +176,8 @@ module Sass
249
176
  return [seq1] if seq2.empty?
250
177
 
251
178
  seq1, seq2 = seq1.dup, seq2.dup
252
- init = merge_initial_ops(seq1, seq2)
253
- return unless init
254
- fin = merge_final_ops(seq1, seq2)
255
- return unless fin
179
+ return unless init = merge_initial_ops(seq1, seq2)
180
+ return unless fin = merge_final_ops(seq1, seq2)
256
181
  seq1 = group_selectors(seq1)
257
182
  seq2 = group_selectors(seq2)
258
183
  lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
@@ -297,7 +222,7 @@ module Sass
297
222
  # merged successfully
298
223
  lcs = Sass::Util.lcs(ops1, ops2)
299
224
  return unless lcs == ops1 || lcs == ops2
300
- (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
225
+ return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
301
226
  end
302
227
 
303
228
  # Extracts final selector combinators (`"+"`, `">"`, `"~"`) and the
@@ -313,8 +238,6 @@ module Sass
313
238
  # be nil. Otherwise, this will contained the merged selector. Array
314
239
  # elements are [Sass::Util#paths]-style options; conceptually, an "or"
315
240
  # of multiple selectors.
316
- # @comment
317
- # rubocop:disable MethodLength
318
241
  def merge_final_ops(seq1, seq2, res = [])
319
242
  ops1, ops2 = [], []
320
243
  ops1 << seq1.pop while seq1.last.is_a?(String)
@@ -348,7 +271,7 @@ module Sass
348
271
  elsif sel2.superselector?(sel1)
349
272
  res.unshift sel1, '~'
350
273
  else
351
- merged = sel1.unify(sel2)
274
+ merged = sel1.unify(sel2.members, sel2.subject?)
352
275
  res.unshift [
353
276
  [sel1, '~', sel2, '~'],
354
277
  [sel2, '~', sel1, '~'],
@@ -365,7 +288,7 @@ module Sass
365
288
  if tilde_sel.superselector?(plus_sel)
366
289
  res.unshift plus_sel, '+'
367
290
  else
368
- merged = plus_sel.unify(tilde_sel)
291
+ merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
369
292
  res.unshift [
370
293
  [tilde_sel, '~', plus_sel, '+'],
371
294
  ([merged, '+'] if merged)
@@ -378,8 +301,7 @@ module Sass
378
301
  res.unshift sel1, op1
379
302
  seq2.push sel2, op2
380
303
  elsif op1 == op2
381
- merged = sel1.unify(sel2)
382
- return unless merged
304
+ return unless merged = sel1.unify(sel2.members, sel2.subject?)
383
305
  res.unshift merged, op1
384
306
  else
385
307
  # Unknown selector combinators can't be unified
@@ -396,8 +318,6 @@ module Sass
396
318
  return merge_final_ops(seq1, seq2, res)
397
319
  end
398
320
  end
399
- # @comment
400
- # rubocop:enable MethodLength
401
321
 
402
322
  # Takes initial subsequences of `seq1` and `seq2` and returns all
403
323
  # orderings of those subsequences. The initial subsequences are determined
@@ -449,7 +369,7 @@ module Sass
449
369
  end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
450
370
  newseq << head
451
371
  end
452
- newseq
372
+ return newseq
453
373
  end
454
374
 
455
375
  # Given two selector sequences, returns whether `seq1` is a
@@ -468,25 +388,25 @@ module Sass
468
388
  seq1.first.is_a?(String) || seq2.first.is_a?(String)
469
389
  # More complex selectors are never superselectors of less complex ones
470
390
  return if seq1.size > seq2.size
471
- return seq1.first.superselector?(seq2.last, seq2[0...-1]) if seq1.size == 1
391
+ return seq1.first.superselector?(seq2.last) if seq1.size == 1
472
392
 
473
393
  _, si = Sass::Util.enum_with_index(seq2).find do |e, i|
474
394
  return if i == seq2.size - 1
475
395
  next if e.is_a?(String)
476
- seq1.first.superselector?(e, seq2[0...i])
396
+ seq1.first.superselector?(e)
477
397
  end
478
398
  return unless si
479
399
 
480
400
  if seq1[1].is_a?(String)
481
- return unless seq2[si + 1].is_a?(String)
401
+ return unless seq2[si+1].is_a?(String)
482
402
  # .foo ~ .bar is a superselector of .foo + .bar
483
- return unless seq1[1] == "~" ? seq2[si + 1] != ">" : seq1[1] == seq2[si + 1]
484
- return _superselector?(seq1[2..-1], seq2[si + 2..-1])
485
- elsif seq2[si + 1].is_a?(String)
486
- return unless seq2[si + 1] == ">"
487
- return _superselector?(seq1[1..-1], seq2[si + 2..-1])
403
+ return unless seq1[1] == "~" ? seq2[si+1] != ">" : seq1[1] == seq2[si+1]
404
+ return _superselector?(seq1[2..-1], seq2[si+2..-1])
405
+ elsif seq2[si+1].is_a?(String)
406
+ return unless seq2[si+1] == ">"
407
+ return _superselector?(seq1[1..-1], seq2[si+2..-1])
488
408
  else
489
- return _superselector?(seq1[1..-1], seq2[si + 1..-1])
409
+ return _superselector?(seq1[1..-1], seq2[si+1..-1])
490
410
  end
491
411
  end
492
412
 
@@ -501,8 +421,7 @@ module Sass
501
421
  # @param seq2 [Array<SimpleSequence or String>]
502
422
  # @return [Boolean]
503
423
  def parent_superselector?(seq1, seq2)
504
- base = Sass::Selector::SimpleSequence.new([Sass::Selector::Placeholder.new('<temp>')],
505
- false)
424
+ base = Sass::Selector::SimpleSequence.new([Sass::Selector::Placeholder.new('<temp>')], false)
506
425
  _superselector?(seq1 + [base], seq2 + [base])
507
426
  end
508
427
 
@@ -516,11 +435,11 @@ module Sass
516
435
  # the other. The more specific selector is removed.
517
436
  #
518
437
  # @param seqses [Array<Array<Array<SimpleSequence or String>>>]
519
- # @return [Array<Array<SimpleSequence or String>>]
438
+ # @return [Array<Array<Array<SimpleSequence or String>>>]
520
439
  def trim(seqses)
521
440
  # Avoid truly horrific quadratic behavior. TODO: I think there
522
441
  # may be a way to get perfect trimming without going quadratic.
523
- return Sass::Util.flatten(seqses, 1) if seqses.size > 100
442
+ return seqses if seqses.size > 100
524
443
 
525
444
  # Keep the results in a separate array so we can be sure we aren't
526
445
  # comparing against an already-trimmed selector. This ensures that two
@@ -531,15 +450,7 @@ module Sass
531
450
  # separate sequences should limit the quadratic behavior.
532
451
  seqses.each_with_index do |seqs1, i|
533
452
  result[i] = seqs1.reject do |seq1|
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
-
453
+ max_spec = _sources(seq1).map {|seq| seq.specificity}.max || 0
543
454
  result.any? do |seqs2|
544
455
  next if seqs1.equal?(seqs2)
545
456
  # Second Law of Extend: the specificity of a generated selector
@@ -547,15 +458,11 @@ module Sass
547
458
  # selector.
548
459
  #
549
460
  # See https://github.com/nex3/sass/issues/324.
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
461
+ seqs2.any? {|seq2| _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)}
555
462
  end
556
463
  end
557
464
  end
558
- Sass::Util.flatten(result, 1)
465
+ result
559
466
  end
560
467
 
561
468
  def _hash
@@ -563,7 +470,7 @@ module Sass
563
470
  end
564
471
 
565
472
  def _eql?(other)
566
- other.members.reject {|m| m == "\n"}.eql?(members.reject {|m| m == "\n"})
473
+ other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
567
474
  end
568
475
 
569
476
  private