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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +50 -70
- data/Rakefile +5 -26
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass.rb +12 -19
- data/lib/sass/cache_stores/base.rb +2 -2
- data/lib/sass/cache_stores/chain.rb +1 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -1
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +0 -1
- data/lib/sass/css.rb +13 -11
- data/lib/sass/engine.rb +173 -424
- data/lib/sass/environment.rb +58 -148
- data/lib/sass/error.rb +14 -11
- data/lib/sass/exec.rb +703 -5
- data/lib/sass/importers/base.rb +6 -49
- data/lib/sass/importers/filesystem.rb +19 -44
- data/lib/sass/logger.rb +4 -1
- data/lib/sass/logger/base.rb +4 -2
- data/lib/sass/logger/log_level.rb +7 -3
- data/lib/sass/media.rb +23 -20
- data/lib/sass/plugin.rb +7 -7
- data/lib/sass/plugin/compiler.rb +145 -304
- data/lib/sass/plugin/configuration.rb +23 -18
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script.rb +8 -35
- data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +606 -0
- data/lib/sass/script/css_lexer.rb +4 -8
- data/lib/sass/script/css_parser.rb +2 -5
- data/lib/sass/script/funcall.rb +245 -0
- data/lib/sass/script/functions.rb +408 -1491
- data/lib/sass/script/interpolation.rb +79 -0
- data/lib/sass/script/lexer.rb +68 -172
- data/lib/sass/script/list.rb +85 -0
- data/lib/sass/script/literal.rb +221 -0
- data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
- data/lib/sass/script/{value/null.rb → null.rb} +7 -14
- data/lib/sass/script/{value/number.rb → number.rb} +75 -152
- data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
- data/lib/sass/script/parser.rb +110 -245
- data/lib/sass/script/string.rb +51 -0
- data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
- data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
- data/lib/sass/script/variable.rb +58 -0
- data/lib/sass/scss/css_parser.rb +3 -9
- data/lib/sass/scss/parser.rb +421 -450
- data/lib/sass/scss/rx.rb +11 -19
- data/lib/sass/scss/static_parser.rb +7 -321
- data/lib/sass/selector.rb +194 -68
- data/lib/sass/selector/abstract_sequence.rb +14 -29
- data/lib/sass/selector/comma_sequence.rb +25 -108
- data/lib/sass/selector/sequence.rb +66 -159
- data/lib/sass/selector/simple.rb +25 -23
- data/lib/sass/selector/simple_sequence.rb +63 -173
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/supports.rb +15 -13
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +3 -3
- data/lib/sass/tree/css_import_node.rb +11 -11
- data/lib/sass/tree/debug_node.rb +2 -2
- data/lib/sass/tree/directive_node.rb +4 -21
- data/lib/sass/tree/each_node.rb +8 -8
- data/lib/sass/tree/extend_node.rb +7 -14
- data/lib/sass/tree/for_node.rb +4 -4
- data/lib/sass/tree/function_node.rb +4 -9
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +5 -4
- data/lib/sass/tree/media_node.rb +14 -4
- data/lib/sass/tree/mixin_def_node.rb +4 -4
- data/lib/sass/tree/mixin_node.rb +8 -21
- data/lib/sass/tree/node.rb +12 -54
- data/lib/sass/tree/prop_node.rb +20 -39
- data/lib/sass/tree/return_node.rb +2 -3
- data/lib/sass/tree/root_node.rb +3 -19
- data/lib/sass/tree/rule_node.rb +22 -35
- data/lib/sass/tree/supports_node.rb +13 -0
- data/lib/sass/tree/trace_node.rb +1 -2
- data/lib/sass/tree/variable_node.rb +3 -9
- data/lib/sass/tree/visitors/base.rb +8 -5
- data/lib/sass/tree/visitors/check_nesting.rb +19 -49
- data/lib/sass/tree/visitors/convert.rb +56 -74
- data/lib/sass/tree/visitors/cssize.rb +74 -202
- data/lib/sass/tree/visitors/deep_copy.rb +5 -10
- data/lib/sass/tree/visitors/extend.rb +7 -7
- data/lib/sass/tree/visitors/perform.rb +185 -278
- data/lib/sass/tree/visitors/set_options.rb +6 -20
- data/lib/sass/tree/visitors/to_css.rb +81 -234
- data/lib/sass/tree/warn_node.rb +2 -2
- data/lib/sass/tree/while_node.rb +2 -2
- data/lib/sass/util.rb +152 -522
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/subset_map.rb +3 -4
- data/lib/sass/util/test.rb +1 -0
- data/lib/sass/version.rb +22 -20
- data/test/Gemfile +3 -0
- data/test/Gemfile.lock +10 -0
- data/test/sass/cache_test.rb +20 -62
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/conversion_test.rb +2 -296
- data/test/sass/css2sass_test.rb +4 -23
- data/test/sass/engine_test.rb +354 -411
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +145 -324
- data/test/sass/functions_test.rb +86 -873
- data/test/sass/importer_test.rb +21 -241
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +26 -16
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +7 -36
- data/test/sass/script_test.rb +53 -485
- data/test/sass/scss/css_test.rb +28 -143
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +325 -2119
- data/test/sass/templates/scss_import.scss +1 -2
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +1 -86
- data/test/test_helper.rb +8 -37
- metadata +19 -66
- data/lib/sass/exec/base.rb +0 -187
- data/lib/sass/exec/sass_convert.rb +0 -264
- data/lib/sass/exec/sass_scss.rb +0 -424
- data/lib/sass/features.rb +0 -47
- data/lib/sass/script/tree.rb +0 -16
- data/lib/sass/script/tree/funcall.rb +0 -306
- data/lib/sass/script/tree/interpolation.rb +0 -118
- data/lib/sass/script/tree/list_literal.rb +0 -77
- data/lib/sass/script/tree/literal.rb +0 -45
- data/lib/sass/script/tree/map_literal.rb +0 -64
- data/lib/sass/script/tree/selector.rb +0 -26
- data/lib/sass/script/tree/variable.rb +0 -57
- data/lib/sass/script/value.rb +0 -11
- data/lib/sass/script/value/base.rb +0 -240
- data/lib/sass/script/value/bool.rb +0 -35
- data/lib/sass/script/value/color.rb +0 -680
- data/lib/sass/script/value/helpers.rb +0 -262
- data/lib/sass/script/value/list.rb +0 -113
- data/lib/sass/script/value/map.rb +0 -70
- data/lib/sass/script/value/string.rb +0 -97
- data/lib/sass/selector/pseudo.rb +0 -256
- data/lib/sass/source/map.rb +0 -210
- data/lib/sass/source/position.rb +0 -39
- data/lib/sass/source/range.rb +0 -41
- data/lib/sass/stack.rb +0 -120
- data/lib/sass/tree/at_root_node.rb +0 -83
- data/lib/sass/tree/error_node.rb +0 -18
- data/lib/sass/tree/keyframe_rule_node.rb +0 -15
- data/lib/sass/util/cross_platform_random.rb +0 -19
- data/lib/sass/util/normalized_map.rb +0 -130
- data/lib/sass/util/ordered_hash.rb +0 -192
- data/test/sass/compiler_test.rb +0 -232
- data/test/sass/encoding_test.rb +0 -219
- data/test/sass/source_map_test.rb +0 -977
- data/test/sass/superselector_test.rb +0 -191
- data/test/sass/util/normalized_map_test.rb +0 -51
- 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 `
|
7
|
-
# method that returns
|
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 ||=
|
66
|
-
|
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
|
-
#
|
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::
|
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
|
-
#
|
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
|
-
|
93
|
-
|
94
|
-
|
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
|
23
|
+
def resolve_parent_refs(super_cseq)
|
27
24
|
if super_cseq.nil?
|
28
25
|
if @members.any? do |sel|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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(
|
41
|
-
|
42
|
-
|
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
|
67
|
-
original = true)
|
54
|
+
def do_extend(extends, parent_directives)
|
68
55
|
CommaSequence.new(members.map do |seq|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
160
|
-
def
|
161
|
-
@members.
|
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
|
47
|
-
# @
|
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(
|
48
|
+
def resolve_parent_refs(super_seq)
|
53
49
|
members = @members.dup
|
54
50
|
nl = (members.first == "\n" && members.shift)
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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,
|
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,
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
#
|
98
|
+
# (.bar .foo).superselector?(.foo) #=> false
|
99
|
+
# @param sseq [SimpleSequence]
|
143
100
|
# @return [Boolean]
|
144
|
-
def superselector?(
|
145
|
-
|
101
|
+
def superselector?(sseq)
|
102
|
+
return false unless members.size == 1
|
103
|
+
members.last.superselector?(sseq)
|
146
104
|
end
|
147
105
|
|
148
|
-
# @see
|
149
|
-
def
|
150
|
-
@members.
|
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".
|
202
|
-
# have `.A .B {@extend .C}` and `.D .C {...}`,
|
203
|
-
# into `.D .C, .D (.A .B)`,
|
204
|
-
# `.D .A .B
|
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
|
-
|
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
|
-
|
217
|
-
|
218
|
-
current = current.dup
|
145
|
+
until afters.empty?
|
146
|
+
current = afters.shift.dup
|
219
147
|
last_current = [current.pop]
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
484
|
-
return _superselector?(seq1[2..-1], seq2[si
|
485
|
-
elsif seq2[si
|
486
|
-
return unless seq2[si
|
487
|
-
return _superselector?(seq1[1..-1], seq2[si
|
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
|
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
|
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
|
-
|
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?
|
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
|
-
|
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
|