oreorenasass 3.4.4 → 3.4.5

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