sass 3.3.0 → 3.4.0
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 +58 -50
- data/Rakefile +1 -4
- 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/lib/sass/cache_stores/filesystem.rb +6 -2
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +37 -46
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +424 -0
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/features.rb +7 -0
- data/lib/sass/importers/base.rb +7 -2
- data/lib/sass/importers/filesystem.rb +9 -25
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +200 -83
- data/lib/sass/plugin/staleness_checker.rb +1 -1
- data/lib/sass/plugin.rb +3 -3
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +622 -268
- data/lib/sass/script/lexer.rb +99 -34
- data/lib/sass/script/parser.rb +24 -23
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +20 -2
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +1 -1
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/value/base.rb +7 -5
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +39 -21
- data/lib/sass/script/value/helpers.rb +107 -0
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +62 -14
- data/lib/sass/script/value/string.rb +59 -11
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +190 -328
- data/lib/sass/scss/rx.rb +15 -6
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +92 -13
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +94 -24
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +97 -33
- data/lib/sass/selector.rb +57 -194
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +26 -12
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +2 -3
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/charset_node.rb +1 -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 +4 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/prop_node.rb +1 -1
- data/lib/sass/tree/rule_node.rb +12 -7
- data/lib/sass/tree/visitors/check_nesting.rb +38 -10
- data/lib/sass/tree/visitors/convert.rb +16 -18
- data/lib/sass/tree/visitors/cssize.rb +29 -29
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +45 -33
- data/lib/sass/tree/visitors/set_options.rb +14 -0
- data/lib/sass/tree/visitors/to_css.rb +15 -14
- data/lib/sass/util/subset_map.rb +1 -1
- data/lib/sass/util.rb +222 -99
- data/lib/sass/version.rb +5 -5
- data/lib/sass.rb +0 -5
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +19 -10
- data/test/sass/conversion_test.rb +58 -1
- data/test/sass/css2sass_test.rb +23 -4
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +136 -199
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +236 -19
- data/test/sass/functions_test.rb +295 -253
- data/test/sass/importer_test.rb +31 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +14 -13
- 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 +10 -7
- data/test/sass/script_test.rb +288 -74
- data/test/sass/scss/css_test.rb +141 -24
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +457 -18
- data/test/sass/source_map_test.rb +115 -25
- data/test/sass/superselector_test.rb +191 -0
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- 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 +31 -1
- data/test/sass/value_helpers_test.rb +5 -7
- data/test/test_helper.rb +2 -2
- 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 +25 -22
- data/ext/mkrf_conf.rb +0 -27
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
- 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
data/lib/sass/scss/rx.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module Sass
|
2
3
|
module SCSS
|
3
4
|
# A module containing regular expressions used
|
@@ -63,9 +64,9 @@ module Sass
|
|
63
64
|
STRING1 = /\"((?:[^\n\r\f\\"]|\\#{NL}|#{ESCAPE})*)\"/
|
64
65
|
STRING2 = /\'((?:[^\n\r\f\\']|\\#{NL}|#{ESCAPE})*)\'/
|
65
66
|
|
66
|
-
IDENT =
|
67
|
+
IDENT = /-*#{NMSTART}#{NMCHAR}*/
|
67
68
|
NAME = /#{NMCHAR}+/
|
68
|
-
NUM =
|
69
|
+
NUM = //
|
69
70
|
STRING = /#{STRING1}|#{STRING2}/
|
70
71
|
URLCHAR = /[#%&*-~]|#{NONASCII}|#{ESCAPE}/
|
71
72
|
URL = /(#{URLCHAR}*)/
|
@@ -80,7 +81,7 @@ module Sass
|
|
80
81
|
|
81
82
|
S = /[ \t\r\n\f]+/
|
82
83
|
|
83
|
-
COMMENT = %r{/\*[^*]
|
84
|
+
COMMENT = %r{/\*([^*]|\*+[^/*])*\**\*/}
|
84
85
|
SINGLE_LINE_COMMENT = %r{//.*(\n[ \t]*//.*)*}
|
85
86
|
|
86
87
|
CDO = quote("<!--")
|
@@ -95,7 +96,9 @@ module Sass
|
|
95
96
|
|
96
97
|
IMPORTANT = /!#{W}important/i
|
97
98
|
|
98
|
-
|
99
|
+
UNITLESS_NUMBER = /(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?\d+)?/
|
100
|
+
NUMBER = /#{UNITLESS_NUMBER}(?:#{IDENT}|%)?/
|
101
|
+
PERCENTAGE = /#{UNITLESS_NUMBER}%/
|
99
102
|
|
100
103
|
URI = /url\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
|
101
104
|
FUNCTION = /#{IDENT}\(/
|
@@ -118,10 +121,16 @@ module Sass
|
|
118
121
|
INTERP_START = /#\{/
|
119
122
|
ANY = /:(-[-\w]+-)?any\(/i
|
120
123
|
OPTIONAL = /!#{W}optional/i
|
124
|
+
IDENT_START = /-|#{NMSTART}/
|
125
|
+
|
126
|
+
# A unit is like an IDENT, but disallows a hyphen followed by a digit.
|
127
|
+
# This allows "1px-2px" to be interpreted as subtraction rather than "1"
|
128
|
+
# with the unit "px-2px". It also allows "%".
|
129
|
+
UNIT = /-?#{NMSTART}(?:[a-zA-Z0-9_]|#{NONASCII}|#{ESCAPE}|-(?!\d))*|%/
|
121
130
|
|
122
131
|
IDENT_HYPHEN_INTERP = /-(#\{)/
|
123
|
-
STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)
|
124
|
-
STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)
|
132
|
+
STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|#{ESCAPE})*)\"/
|
133
|
+
STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|#{ESCAPE})*)\'/
|
125
134
|
STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
|
126
135
|
|
127
136
|
STATIC_COMPONENT = /#{IDENT}|#{STRING_NOINTERP}|#{HEXCOLOR}|[+-]?#{NUMBER}|\!important/i
|
@@ -41,6 +41,24 @@ module Sass
|
|
41
41
|
return type, directives
|
42
42
|
end
|
43
43
|
|
44
|
+
def parse_keyframes_selector
|
45
|
+
init_scanner!
|
46
|
+
sel = expr!(:keyframes_selector)
|
47
|
+
expected("keyframes selector") unless @scanner.eos?
|
48
|
+
sel
|
49
|
+
end
|
50
|
+
|
51
|
+
# @see Parser#initialize
|
52
|
+
# @param allow_parent_ref [Boolean] Whether to allow the
|
53
|
+
# parent-reference selector, `&`, when parsing the document.
|
54
|
+
# @comment
|
55
|
+
# rubocop:disable ParameterLists
|
56
|
+
def initialize(str, filename, importer, line = 1, offset = 1, allow_parent_ref = true)
|
57
|
+
# rubocop:enable ParameterLists
|
58
|
+
super(str, filename, importer, line, offset)
|
59
|
+
@allow_parent_ref = allow_parent_ref
|
60
|
+
end
|
61
|
+
|
44
62
|
private
|
45
63
|
|
46
64
|
def moz_document_function
|
@@ -52,7 +70,7 @@ module Sass
|
|
52
70
|
|
53
71
|
def variable; nil; end
|
54
72
|
def script_value; nil; end
|
55
|
-
def interpolation; nil; end
|
73
|
+
def interpolation(warn_for_color = false); nil; end
|
56
74
|
def var_expr; nil; end
|
57
75
|
def interp_string; (s = tok(STRING)) && [s]; end
|
58
76
|
def interp_uri; (s = tok(URI)) && [s]; end
|
@@ -64,6 +82,285 @@ module Sass
|
|
64
82
|
super
|
65
83
|
end
|
66
84
|
|
85
|
+
def selector_comma_sequence
|
86
|
+
sel = selector
|
87
|
+
return unless sel
|
88
|
+
selectors = [sel]
|
89
|
+
ws = ''
|
90
|
+
while tok(/,/)
|
91
|
+
ws << str {ss}
|
92
|
+
if (sel = selector)
|
93
|
+
selectors << sel
|
94
|
+
if ws.include?("\n")
|
95
|
+
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
|
96
|
+
end
|
97
|
+
ws = ''
|
98
|
+
end
|
99
|
+
end
|
100
|
+
Selector::CommaSequence.new(selectors)
|
101
|
+
end
|
102
|
+
|
103
|
+
def selector_string
|
104
|
+
sel = selector
|
105
|
+
return unless sel
|
106
|
+
sel.to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def selector
|
110
|
+
start_pos = source_position
|
111
|
+
# The combinator here allows the "> E" hack
|
112
|
+
val = combinator || simple_selector_sequence
|
113
|
+
return unless val
|
114
|
+
nl = str {ss}.include?("\n")
|
115
|
+
res = []
|
116
|
+
res << val
|
117
|
+
res << "\n" if nl
|
118
|
+
|
119
|
+
while (val = combinator || simple_selector_sequence)
|
120
|
+
res << val
|
121
|
+
res << "\n" if str {ss}.include?("\n")
|
122
|
+
end
|
123
|
+
seq = Selector::Sequence.new(res.compact)
|
124
|
+
|
125
|
+
if seq.members.any? {|sseq| sseq.is_a?(Selector::SimpleSequence) && sseq.subject?}
|
126
|
+
location = " of #{@filename}" if @filename
|
127
|
+
Sass::Util.sass_warn <<MESSAGE
|
128
|
+
DEPRECATION WARNING on line #{start_pos.line}, column #{start_pos.offset}#{location}:
|
129
|
+
The subject selector operator "!" is deprecated and will be removed in a future release.
|
130
|
+
This operator has been replaced by ":has()" in the CSS spec.
|
131
|
+
For example: #{seq.subjectless}
|
132
|
+
MESSAGE
|
133
|
+
end
|
134
|
+
|
135
|
+
seq
|
136
|
+
end
|
137
|
+
|
138
|
+
def combinator
|
139
|
+
tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
|
140
|
+
end
|
141
|
+
|
142
|
+
def reference_combinator
|
143
|
+
return unless tok(/\//)
|
144
|
+
res = '/'
|
145
|
+
ns, name = expr!(:qualified_name)
|
146
|
+
res << ns << '|' if ns
|
147
|
+
res << name << tok!(/\//)
|
148
|
+
res
|
149
|
+
end
|
150
|
+
|
151
|
+
def simple_selector_sequence
|
152
|
+
start_pos = source_position
|
153
|
+
e = element_name || id_selector || class_selector || placeholder_selector || attrib ||
|
154
|
+
pseudo || parent_selector
|
155
|
+
return unless e
|
156
|
+
res = [e]
|
157
|
+
|
158
|
+
# The tok(/\*/) allows the "E*" hack
|
159
|
+
while (v = id_selector || class_selector || placeholder_selector ||
|
160
|
+
attrib || pseudo || (tok(/\*/) && Selector::Universal.new(nil)))
|
161
|
+
res << v
|
162
|
+
end
|
163
|
+
|
164
|
+
pos = @scanner.pos
|
165
|
+
line = @line
|
166
|
+
if (sel = str? {simple_selector_sequence})
|
167
|
+
@scanner.pos = pos
|
168
|
+
@line = line
|
169
|
+
begin
|
170
|
+
# If we see "*E", don't force a throw because this could be the
|
171
|
+
# "*prop: val" hack.
|
172
|
+
expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
|
173
|
+
throw_error {expected('"{"')}
|
174
|
+
rescue Sass::SyntaxError => e
|
175
|
+
e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
|
176
|
+
raise e
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
Selector::SimpleSequence.new(res, tok(/!/), range(start_pos))
|
181
|
+
end
|
182
|
+
|
183
|
+
def parent_selector
|
184
|
+
return unless @allow_parent_ref && tok(/&/)
|
185
|
+
Selector::Parent.new(tok(NAME))
|
186
|
+
end
|
187
|
+
|
188
|
+
def class_selector
|
189
|
+
return unless tok(/\./)
|
190
|
+
@expected = "class name"
|
191
|
+
Selector::Class.new(tok!(IDENT))
|
192
|
+
end
|
193
|
+
|
194
|
+
def id_selector
|
195
|
+
return unless tok(/#(?!\{)/)
|
196
|
+
@expected = "id name"
|
197
|
+
Selector::Id.new(tok!(NAME))
|
198
|
+
end
|
199
|
+
|
200
|
+
def placeholder_selector
|
201
|
+
return unless tok(/%/)
|
202
|
+
@expected = "placeholder name"
|
203
|
+
Selector::Placeholder.new(tok!(IDENT))
|
204
|
+
end
|
205
|
+
|
206
|
+
def element_name
|
207
|
+
ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
|
208
|
+
return unless ns || name
|
209
|
+
|
210
|
+
if name == '*'
|
211
|
+
Selector::Universal.new(ns)
|
212
|
+
else
|
213
|
+
Selector::Element.new(name, ns)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def qualified_name(allow_star_name = false)
|
218
|
+
name = tok(IDENT) || tok(/\*/) || (tok?(/\|/) && "")
|
219
|
+
return unless name
|
220
|
+
return nil, name unless tok(/\|/)
|
221
|
+
|
222
|
+
return name, tok!(IDENT) unless allow_star_name
|
223
|
+
@expected = "identifier or *"
|
224
|
+
return name, tok(IDENT) || tok!(/\*/)
|
225
|
+
end
|
226
|
+
|
227
|
+
def attrib
|
228
|
+
return unless tok(/\[/)
|
229
|
+
ss
|
230
|
+
ns, name = attrib_name!
|
231
|
+
ss
|
232
|
+
|
233
|
+
op = tok(/=/) ||
|
234
|
+
tok(INCLUDES) ||
|
235
|
+
tok(DASHMATCH) ||
|
236
|
+
tok(PREFIXMATCH) ||
|
237
|
+
tok(SUFFIXMATCH) ||
|
238
|
+
tok(SUBSTRINGMATCH)
|
239
|
+
if op
|
240
|
+
@expected = "identifier or string"
|
241
|
+
ss
|
242
|
+
val = tok(IDENT) || tok!(STRING)
|
243
|
+
ss
|
244
|
+
end
|
245
|
+
flags = tok(IDENT) || tok(STRING)
|
246
|
+
tok!(/\]/)
|
247
|
+
|
248
|
+
Selector::Attribute.new(name, ns, op, val, flags)
|
249
|
+
end
|
250
|
+
|
251
|
+
def attrib_name!
|
252
|
+
if (name_or_ns = tok(IDENT))
|
253
|
+
# E, E|E
|
254
|
+
if tok(/\|(?!=)/)
|
255
|
+
ns = name_or_ns
|
256
|
+
name = tok(IDENT)
|
257
|
+
else
|
258
|
+
name = name_or_ns
|
259
|
+
end
|
260
|
+
else
|
261
|
+
# *|E or |E
|
262
|
+
ns = tok(/\*/) || ""
|
263
|
+
tok!(/\|/)
|
264
|
+
name = tok!(IDENT)
|
265
|
+
end
|
266
|
+
return ns, name
|
267
|
+
end
|
268
|
+
|
269
|
+
SELECTOR_PSEUDO_CLASSES = %w[not matches current any has host host-context].to_set
|
270
|
+
|
271
|
+
PREFIXED_SELECTOR_PSEUDO_CLASSES = %w[nth-child nth-last-child].to_set
|
272
|
+
|
273
|
+
def pseudo
|
274
|
+
s = tok(/::?/)
|
275
|
+
return unless s
|
276
|
+
@expected = "pseudoclass or pseudoelement"
|
277
|
+
name = tok!(IDENT)
|
278
|
+
if tok(/\(/)
|
279
|
+
ss
|
280
|
+
deprefixed = deprefix(name)
|
281
|
+
if s == ':' && SELECTOR_PSEUDO_CLASSES.include?(deprefixed)
|
282
|
+
sel = selector_comma_sequence
|
283
|
+
elsif s == ':' && PREFIXED_SELECTOR_PSEUDO_CLASSES.include?(deprefixed)
|
284
|
+
arg, sel = prefixed_selector_pseudo
|
285
|
+
else
|
286
|
+
arg = expr!(:pseudo_args)
|
287
|
+
end
|
288
|
+
|
289
|
+
tok!(/\)/)
|
290
|
+
end
|
291
|
+
Selector::Pseudo.new(s == ':' ? :class : :element, name, arg, sel)
|
292
|
+
end
|
293
|
+
|
294
|
+
def pseudo_args
|
295
|
+
arg = expr!(:pseudo_expr)
|
296
|
+
while tok(/,/)
|
297
|
+
arg << ',' << str {ss}
|
298
|
+
arg.concat expr!(:pseudo_expr)
|
299
|
+
end
|
300
|
+
arg
|
301
|
+
end
|
302
|
+
|
303
|
+
def pseudo_expr
|
304
|
+
res = pseudo_expr_token
|
305
|
+
return unless res
|
306
|
+
res << str {ss}
|
307
|
+
while (e = pseudo_expr_token)
|
308
|
+
res << e << str {ss}
|
309
|
+
end
|
310
|
+
res
|
311
|
+
end
|
312
|
+
|
313
|
+
def pseudo_expr_token
|
314
|
+
tok(PLUS) || tok(/[-*]/) || tok(NUMBER) || tok(STRING) || tok(IDENT)
|
315
|
+
end
|
316
|
+
|
317
|
+
def prefixed_selector_pseudo
|
318
|
+
prefix = str do
|
319
|
+
expr = str {expr!(:a_n_plus_b)}
|
320
|
+
ss
|
321
|
+
return expr, nil unless tok(/of/)
|
322
|
+
ss
|
323
|
+
end
|
324
|
+
return prefix, expr!(:selector_comma_sequence)
|
325
|
+
end
|
326
|
+
|
327
|
+
def a_n_plus_b
|
328
|
+
if (parity = tok(/even|odd/i))
|
329
|
+
return parity
|
330
|
+
end
|
331
|
+
|
332
|
+
if tok(/[+-]?[0-9]+/)
|
333
|
+
ss
|
334
|
+
return true unless tok(/n/)
|
335
|
+
else
|
336
|
+
return unless tok(/[+-]?n/i)
|
337
|
+
end
|
338
|
+
ss
|
339
|
+
|
340
|
+
return true unless tok(/[+-]/)
|
341
|
+
ss
|
342
|
+
@expected = "number"
|
343
|
+
tok!(/[0-9]+/)
|
344
|
+
true
|
345
|
+
end
|
346
|
+
|
347
|
+
def keyframes_selector
|
348
|
+
ss
|
349
|
+
str do
|
350
|
+
return unless keyframes_selector_component
|
351
|
+
ss
|
352
|
+
while tok(/,/)
|
353
|
+
ss
|
354
|
+
expr!(:keyframes_selector_component)
|
355
|
+
ss
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def keyframes_selector_component
|
361
|
+
tok(/from|to/i) || tok(PERCENTAGE)
|
362
|
+
end
|
363
|
+
|
67
364
|
@sass_script_parser = Class.new(Sass::Script::CssParser)
|
68
365
|
@sass_script_parser.send(:include, ScriptParser)
|
69
366
|
end
|
@@ -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_s`
|
7
|
+
# method that returns the string representation of the selector.
|
8
8
|
class AbstractSequence
|
9
9
|
# The line of the Sass template on which this selector was declared.
|
10
10
|
#
|
@@ -62,22 +62,26 @@ module Sass
|
|
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
|
-
|
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
|
67
70
|
end
|
68
71
|
|
69
|
-
#
|
70
|
-
# string, along with any SassScript interpolation that may exist.
|
72
|
+
# Returns the selector string.
|
71
73
|
#
|
72
74
|
# @return [String]
|
73
75
|
def to_s
|
74
|
-
|
76
|
+
Sass::Util.abstract(self)
|
75
77
|
end
|
76
78
|
|
77
|
-
# Returns the specificity of the selector
|
78
|
-
# by {Sass::Selector::SPECIFICITY_BASE}.
|
79
|
+
# Returns the specificity of the selector.
|
79
80
|
#
|
80
|
-
#
|
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]
|
81
85
|
def specificity
|
82
86
|
_specificity(members)
|
83
87
|
end
|
@@ -85,9 +89,20 @@ module Sass
|
|
85
89
|
protected
|
86
90
|
|
87
91
|
def _specificity(arr)
|
88
|
-
|
89
|
-
|
90
|
-
|
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)
|
91
106
|
end
|
92
107
|
end
|
93
108
|
end
|
@@ -53,20 +53,101 @@ module Sass
|
|
53
53
|
# The extensions to perform on this selector
|
54
54
|
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
|
55
55
|
# 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.
|
56
64
|
# @return [CommaSequence] A copy of this selector,
|
57
65
|
# with extensions made according to `extends`
|
58
|
-
def do_extend(extends, parent_directives
|
66
|
+
def do_extend(extends, parent_directives = [], replace = false, seen = Set.new,
|
67
|
+
original = true)
|
59
68
|
CommaSequence.new(members.map do |seq|
|
60
|
-
|
61
|
-
# First Law of Extend: the result of extending a selector should
|
62
|
-
# always contain the base selector.
|
63
|
-
#
|
64
|
-
# See https://github.com/nex3/sass/issues/324.
|
65
|
-
extended.unshift seq unless seq.has_placeholder? || extended.include?(seq)
|
66
|
-
extended
|
69
|
+
seq.do_extend(extends, parent_directives, replace, seen, original)
|
67
70
|
end.flatten)
|
68
71
|
end
|
69
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)
|
149
|
+
end
|
150
|
+
|
70
151
|
# Returns a string representation of the sequence.
|
71
152
|
# This is basically the selector string.
|
72
153
|
#
|
@@ -75,11 +156,9 @@ module Sass
|
|
75
156
|
members.map {|m| m.inspect}.join(", ")
|
76
157
|
end
|
77
158
|
|
78
|
-
# @see
|
79
|
-
def
|
80
|
-
|
81
|
-
arr.delete("\n")
|
82
|
-
arr
|
159
|
+
# @see AbstractSequence#to_s
|
160
|
+
def to_s
|
161
|
+
@members.join(", ").gsub(", \n", ",\n")
|
83
162
|
end
|
84
163
|
|
85
164
|
private
|