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