sass 3.6.0 → 3.7.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 +5 -5
- data/CONTRIBUTING.md +1 -1
- data/README.md +10 -0
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass/script/css_parser.rb +4 -1
- data/lib/sass/script/functions.rb +15 -25
- data/lib/sass/script/lexer.rb +45 -7
- data/lib/sass/script/parser.rb +259 -93
- data/lib/sass/scss/parser.rb +43 -16
- data/lib/sass/scss/static_parser.rb +15 -15
- data/lib/sass/util.rb +31 -0
- metadata +8 -156
- data/Rakefile +0 -364
- data/test/sass-spec.yml +0 -3
- data/test/sass/cache_test.rb +0 -130
- data/test/sass/callbacks_test.rb +0 -60
- data/test/sass/compiler_test.rb +0 -225
- data/test/sass/conversion_test.rb +0 -2138
- data/test/sass/css2sass_test.rb +0 -523
- data/test/sass/css_variable_test.rb +0 -237
- data/test/sass/data/hsl-rgb.txt +0 -319
- data/test/sass/encoding_test.rb +0 -188
- data/test/sass/engine_test.rb +0 -3499
- data/test/sass/exec_test.rb +0 -95
- data/test/sass/extend_test.rb +0 -1679
- data/test/sass/fixtures/test_staleness_check_across_importers.css +0 -1
- data/test/sass/fixtures/test_staleness_check_across_importers.scss +0 -1
- data/test/sass/functions_test.rb +0 -2021
- data/test/sass/importer_test.rb +0 -420
- data/test/sass/logger_test.rb +0 -57
- data/test/sass/mock_importer.rb +0 -49
- data/test/sass/more_results/more1.css +0 -9
- data/test/sass/more_results/more1_with_line_comments.css +0 -26
- data/test/sass/more_results/more_import.css +0 -29
- data/test/sass/more_templates/_more_partial.sass +0 -2
- data/test/sass/more_templates/more1.sass +0 -23
- data/test/sass/more_templates/more_import.sass +0 -11
- data/test/sass/plugin_test.rb +0 -552
- data/test/sass/results/alt.css +0 -4
- data/test/sass/results/basic.css +0 -9
- data/test/sass/results/cached_import_option.css +0 -3
- data/test/sass/results/compact.css +0 -5
- data/test/sass/results/complex.css +0 -86
- data/test/sass/results/compressed.css +0 -1
- data/test/sass/results/expanded.css +0 -19
- data/test/sass/results/filename_fn.css +0 -3
- data/test/sass/results/if.css +0 -3
- data/test/sass/results/import.css +0 -31
- data/test/sass/results/import_charset.css +0 -5
- data/test/sass/results/import_charset_ibm866.css +0 -5
- data/test/sass/results/import_content.css +0 -1
- data/test/sass/results/line_numbers.css +0 -49
- data/test/sass/results/mixins.css +0 -95
- data/test/sass/results/multiline.css +0 -24
- data/test/sass/results/nested.css +0 -22
- data/test/sass/results/options.css +0 -1
- data/test/sass/results/parent_ref.css +0 -13
- data/test/sass/results/script.css +0 -16
- data/test/sass/results/scss_import.css +0 -31
- data/test/sass/results/scss_importee.css +0 -2
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +0 -1
- data/test/sass/results/subdir/subdir.css +0 -3
- data/test/sass/results/units.css +0 -11
- data/test/sass/results/warn.css +0 -0
- data/test/sass/results/warn_imported.css +0 -0
- data/test/sass/script_conversion_test.rb +0 -365
- data/test/sass/script_test.rb +0 -1427
- data/test/sass/scss/css_test.rb +0 -1266
- data/test/sass/scss/rx_test.rb +0 -159
- data/test/sass/scss/scss_test.rb +0 -4238
- data/test/sass/scss/test_helper.rb +0 -37
- data/test/sass/source_map_test.rb +0 -1063
- data/test/sass/superselector_test.rb +0 -209
- data/test/sass/templates/_cached_import_option_partial.scss +0 -1
- data/test/sass/templates/_double_import_loop2.sass +0 -1
- data/test/sass/templates/_filename_fn_import.scss +0 -11
- data/test/sass/templates/_imported_charset_ibm866.sass +0 -4
- data/test/sass/templates/_imported_charset_utf8.sass +0 -4
- data/test/sass/templates/_imported_content.sass +0 -3
- data/test/sass/templates/_partial.sass +0 -2
- data/test/sass/templates/_same_name_different_partiality.scss +0 -1
- data/test/sass/templates/alt.sass +0 -16
- data/test/sass/templates/basic.sass +0 -23
- data/test/sass/templates/bork1.sass +0 -2
- data/test/sass/templates/bork2.sass +0 -2
- data/test/sass/templates/bork3.sass +0 -2
- data/test/sass/templates/bork4.sass +0 -2
- data/test/sass/templates/bork5.sass +0 -3
- data/test/sass/templates/cached_import_option.scss +0 -3
- data/test/sass/templates/compact.sass +0 -17
- data/test/sass/templates/complex.sass +0 -305
- data/test/sass/templates/compressed.sass +0 -15
- data/test/sass/templates/double_import_loop1.sass +0 -1
- data/test/sass/templates/expanded.sass +0 -17
- data/test/sass/templates/filename_fn.scss +0 -18
- data/test/sass/templates/if.sass +0 -11
- data/test/sass/templates/import.sass +0 -12
- data/test/sass/templates/import_charset.sass +0 -9
- data/test/sass/templates/import_charset_ibm866.sass +0 -11
- data/test/sass/templates/import_content.sass +0 -4
- data/test/sass/templates/importee.less +0 -2
- data/test/sass/templates/importee.sass +0 -19
- data/test/sass/templates/line_numbers.sass +0 -13
- data/test/sass/templates/mixin_bork.sass +0 -5
- data/test/sass/templates/mixins.sass +0 -76
- data/test/sass/templates/multiline.sass +0 -20
- data/test/sass/templates/nested.sass +0 -25
- data/test/sass/templates/nested_bork1.sass +0 -2
- data/test/sass/templates/nested_bork2.sass +0 -2
- data/test/sass/templates/nested_bork3.sass +0 -2
- data/test/sass/templates/nested_bork4.sass +0 -2
- data/test/sass/templates/nested_import.sass +0 -2
- data/test/sass/templates/nested_mixin_bork.sass +0 -6
- data/test/sass/templates/options.sass +0 -2
- data/test/sass/templates/parent_ref.sass +0 -25
- data/test/sass/templates/same_name_different_ext.sass +0 -2
- data/test/sass/templates/same_name_different_ext.scss +0 -1
- data/test/sass/templates/same_name_different_partiality.scss +0 -1
- data/test/sass/templates/script.sass +0 -101
- data/test/sass/templates/scss_import.scss +0 -12
- data/test/sass/templates/scss_importee.scss +0 -1
- data/test/sass/templates/single_import_loop.sass +0 -1
- data/test/sass/templates/subdir/import_up1.scss +0 -1
- data/test/sass/templates/subdir/import_up2.scss +0 -1
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +0 -2
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +0 -3
- data/test/sass/templates/subdir/subdir.sass +0 -6
- data/test/sass/templates/units.sass +0 -11
- data/test/sass/templates/warn.sass +0 -3
- data/test/sass/templates/warn_imported.sass +0 -4
- data/test/sass/test_helper.rb +0 -8
- data/test/sass/util/multibyte_string_scanner_test.rb +0 -152
- data/test/sass/util/normalized_map_test.rb +0 -50
- data/test/sass/util/subset_map_test.rb +0 -90
- data/test/sass/util_test.rb +0 -393
- data/test/sass/value_helpers_test.rb +0 -178
- data/test/test_helper.rb +0 -149
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: e9abf6343867b108ab17ccfa5170666b2a45f904
|
|
4
|
+
data.tar.gz: b5744bc72de099acd9ead2a0df936c122b80eab7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a4f75cb603cc8c249e13a5d20d35b84302a98834e3a768a38c3fefffdc8b3ab52217c118855ea087ce12ec24a5327302e5516e02112c122456299ad7aacf446
|
|
7
|
+
data.tar.gz: a56c6e88b050deba0d2d2b6c74c784e5c71337e6b6a1f0ce5432aa9d75395fe27654b6ff9e9d5f3fa91abc5d02a800337c67710f6bb06143f514a024f24e995e
|
data/CONTRIBUTING.md
CHANGED
|
@@ -18,7 +18,7 @@ is regularly merged into the one below: `stable` into `next`, `next` into
|
|
|
18
18
|
`master`.
|
|
19
19
|
|
|
20
20
|
* The `stable` branch is the default—it's what GitHub shows if you go to
|
|
21
|
-
[sass/sass](https://github.com/sass/sass), and it's the default place for pull
|
|
21
|
+
[sass/ruby-sass](https://github.com/sass/ruby-sass), and it's the default place for pull
|
|
22
22
|
requests to go. This branch is where we work on the next patch release. Bug
|
|
23
23
|
fixes and documentation improvements belong here, but not new features.
|
|
24
24
|
|
data/README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## Ruby Sass is Deprecated!
|
|
2
|
+
|
|
3
|
+
Ruby Sass is in a sunset period where only critical bugs and CSS compatibility
|
|
4
|
+
issues will be fixed. It will be completely unmaintained as of 26 March 2019.
|
|
5
|
+
See [the Sass website][] for details, and consider switching to the [`sassc`
|
|
6
|
+
gem][]
|
|
7
|
+
|
|
8
|
+
[the Sass website]: https://sass-lang.com/ruby-sass
|
|
9
|
+
[`sassc` gem]: https://rubygems.org/gems/sassc
|
|
10
|
+
|
|
1
11
|
# Sass [](https://travis-ci.org/sass/ruby-sass) [](http://badge.fury.io/rb/sass) [](http://inch-ci.org/github/sass/sass)
|
|
2
12
|
|
|
3
13
|
**Sass makes CSS fun again**. Sass is an extension of CSS,
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.7.0
|
data/VERSION_DATE
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
06 November 2018 23:08:31 UTC
|
|
@@ -24,7 +24,10 @@ module Sass
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# Short-circuit all the SassScript-only productions
|
|
27
|
-
|
|
27
|
+
def interpolation(first: nil, inner: :space)
|
|
28
|
+
first || send(inner)
|
|
29
|
+
end
|
|
30
|
+
|
|
28
31
|
alias_method :or_expr, :div
|
|
29
32
|
alias_method :unary_div, :ident
|
|
30
33
|
alias_method :paren, :string
|
|
@@ -664,15 +664,11 @@ module Sass::Script
|
|
|
664
664
|
assert_type green, :Number, :green
|
|
665
665
|
assert_type blue, :Number, :blue
|
|
666
666
|
|
|
667
|
-
color_attrs = [
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
else
|
|
673
|
-
raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
|
|
674
|
-
end
|
|
675
|
-
end
|
|
667
|
+
color_attrs = [
|
|
668
|
+
percentage_or_unitless(red, 255, "red"),
|
|
669
|
+
percentage_or_unitless(green, 255, "green"),
|
|
670
|
+
percentage_or_unitless(blue, 255, "blue")
|
|
671
|
+
]
|
|
676
672
|
|
|
677
673
|
# Don't store the string representation for function-created colors, both
|
|
678
674
|
# because it's not very useful and because some functions aren't supported
|
|
@@ -737,8 +733,7 @@ module Sass::Script
|
|
|
737
733
|
unquoted_string("rgba(#{color.red}, #{color.green}, #{color.blue}, #{alpha})")
|
|
738
734
|
else
|
|
739
735
|
assert_type alpha, :Number, :alpha
|
|
740
|
-
|
|
741
|
-
color.with(:alpha => alpha.value)
|
|
736
|
+
color.with(:alpha => percentage_or_unitless(alpha, 1, "alpha"))
|
|
742
737
|
end
|
|
743
738
|
when 3
|
|
744
739
|
if var?(args[0]) || var?(args[1]) || var?(args[2])
|
|
@@ -840,7 +835,6 @@ module Sass::Script
|
|
|
840
835
|
assert_type saturation, :Number, :saturation
|
|
841
836
|
assert_type lightness, :Number, :lightness
|
|
842
837
|
assert_type alpha, :Number, :alpha
|
|
843
|
-
check_alpha_unit alpha, 'hsla'
|
|
844
838
|
|
|
845
839
|
h = hue.value
|
|
846
840
|
s = saturation.value
|
|
@@ -850,7 +844,8 @@ module Sass::Script
|
|
|
850
844
|
# because it's not very useful and because some functions aren't supported
|
|
851
845
|
# on older browsers.
|
|
852
846
|
Sass::Script::Value::Color.new(
|
|
853
|
-
:hue => h, :saturation => s, :lightness => l,
|
|
847
|
+
:hue => h, :saturation => s, :lightness => l,
|
|
848
|
+
:alpha => percentage_or_unitless(alpha, 1, "alpha"))
|
|
854
849
|
end
|
|
855
850
|
declare :hsla, [:hue, :saturation, :lightness, :alpha]
|
|
856
851
|
declare :hsla, [:hue, :saturation, :lightness]
|
|
@@ -2911,19 +2906,14 @@ WARNING
|
|
|
2911
2906
|
color.with(attr => color.send(attr).send(op, amount.value))
|
|
2912
2907
|
end
|
|
2913
2908
|
|
|
2914
|
-
def
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
DEPRECATION WARNING: Passing a percentage as the alpha value to #{function}() will be
|
|
2920
|
-
interpreted differently in future versions of Sass. For now, use #{alpha.value} instead.
|
|
2921
|
-
WARNING
|
|
2909
|
+
def percentage_or_unitless(number, max, name)
|
|
2910
|
+
if number.unitless?
|
|
2911
|
+
value = number.value
|
|
2912
|
+
elsif number.is_unit?("%")
|
|
2913
|
+
value = max * number.value / 100.0;
|
|
2922
2914
|
else
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
deprecated and will be an error in future versions of Sass. Use #{alpha.value} instead.
|
|
2926
|
-
WARNING
|
|
2915
|
+
raise ArgumentError.new(
|
|
2916
|
+
"$#{name}: Expected #{number} to have no units or \"%\"");
|
|
2927
2917
|
end
|
|
2928
2918
|
end
|
|
2929
2919
|
end
|
data/lib/sass/script/lexer.rb
CHANGED
|
@@ -190,6 +190,14 @@ module Sass
|
|
|
190
190
|
@scanner.string[pos, 1]
|
|
191
191
|
end
|
|
192
192
|
|
|
193
|
+
# Consumes and returns single raw character from the input stream.
|
|
194
|
+
#
|
|
195
|
+
# @return [String]
|
|
196
|
+
def next_char
|
|
197
|
+
unpeek!
|
|
198
|
+
scan(/./)
|
|
199
|
+
end
|
|
200
|
+
|
|
193
201
|
# Returns the next token without moving the lexer forward.
|
|
194
202
|
#
|
|
195
203
|
# @return [Token] The next token
|
|
@@ -200,6 +208,7 @@ module Sass
|
|
|
200
208
|
# Rewinds the underlying StringScanner
|
|
201
209
|
# to before the token returned by \{#peek}.
|
|
202
210
|
def unpeek!
|
|
211
|
+
raise "[BUG] Can't unpeek before a queued token!" if @next_tok
|
|
203
212
|
return unless @tok
|
|
204
213
|
@scanner.pos = @tok.pos
|
|
205
214
|
@line = @tok.source_range.start_pos.line
|
|
@@ -243,6 +252,30 @@ module Sass
|
|
|
243
252
|
@scanner.string[old_pos...new_pos]
|
|
244
253
|
end
|
|
245
254
|
|
|
255
|
+
# Runs a block, and rewinds the state of the lexer to the beginning of the
|
|
256
|
+
# block if it returns `nil` or `false`.
|
|
257
|
+
def try
|
|
258
|
+
old_pos = @scanner.pos
|
|
259
|
+
old_line = @line
|
|
260
|
+
old_offset = @offset
|
|
261
|
+
old_interpolation_stack = @interpolation_stack.dup
|
|
262
|
+
old_prev = @prev
|
|
263
|
+
old_tok = @tok
|
|
264
|
+
old_next_tok = @next_tok
|
|
265
|
+
|
|
266
|
+
result = yield
|
|
267
|
+
return result if result
|
|
268
|
+
|
|
269
|
+
@scanner.pos = old_pos
|
|
270
|
+
@line = old_line
|
|
271
|
+
@offset = old_offset
|
|
272
|
+
@interpolation_stack = old_interpolation_stack
|
|
273
|
+
@prev = old_prev
|
|
274
|
+
@tok = old_tok
|
|
275
|
+
@next_tok = old_next_tok
|
|
276
|
+
nil
|
|
277
|
+
end
|
|
278
|
+
|
|
246
279
|
private
|
|
247
280
|
|
|
248
281
|
def read_token
|
|
@@ -266,10 +299,14 @@ module Sass
|
|
|
266
299
|
end
|
|
267
300
|
|
|
268
301
|
def token
|
|
269
|
-
if after_interpolation?
|
|
270
|
-
interp_type, interp_value =
|
|
302
|
+
if after_interpolation?
|
|
303
|
+
interp_type, interp_value = @interpolation_stack.pop
|
|
271
304
|
if interp_type == :special_fun
|
|
272
305
|
return special_fun_body(interp_value)
|
|
306
|
+
elsif interp_type.nil?
|
|
307
|
+
if @scanner.string[@scanner.pos - 1] == '}' && scan(REGULAR_EXPRESSIONS[:ident])
|
|
308
|
+
return [@scanner[2] ? :funcall : :ident, Sass::Util.normalize_ident_escapes(@scanner[1], start: false)]
|
|
309
|
+
end
|
|
273
310
|
else
|
|
274
311
|
raise "[BUG]: Unknown interp_type #{interp_type}" unless interp_type == :string
|
|
275
312
|
return string(interp_value, true)
|
|
@@ -287,13 +324,12 @@ module Sass
|
|
|
287
324
|
|
|
288
325
|
def _variable(rx)
|
|
289
326
|
return unless scan(rx)
|
|
290
|
-
|
|
291
|
-
[:const, @scanner[2]]
|
|
327
|
+
[:const, Sass::Util.normalize_ident_escapes(@scanner[2])]
|
|
292
328
|
end
|
|
293
329
|
|
|
294
330
|
def ident
|
|
295
331
|
return unless scan(REGULAR_EXPRESSIONS[:ident])
|
|
296
|
-
[@scanner[2] ? :funcall : :ident, @scanner[1]]
|
|
332
|
+
[@scanner[2] ? :funcall : :ident, Sass::Util.normalize_ident_escapes(@scanner[1])]
|
|
297
333
|
end
|
|
298
334
|
|
|
299
335
|
def string(re, open)
|
|
@@ -349,7 +385,9 @@ MESSAGE
|
|
|
349
385
|
|
|
350
386
|
value = (@scanner[1] ? @scanner[1].to_f : @scanner[2].to_i) * (minus ? -1 : 1)
|
|
351
387
|
value *= 10**@scanner[3].to_i if @scanner[3]
|
|
352
|
-
|
|
388
|
+
units = @scanner[4]
|
|
389
|
+
units = Sass::Util::normalize_ident_escapes(units) if units
|
|
390
|
+
script_number = Script::Value::Number.new(value, Array(units))
|
|
353
391
|
[:number, script_number]
|
|
354
392
|
end
|
|
355
393
|
|
|
@@ -373,7 +411,7 @@ MESSAGE
|
|
|
373
411
|
@scanner[0].length == 7 || @scanner[0].length == 9)
|
|
374
412
|
return [:color, Script::Value::Color.from_hex(@scanner[0])]
|
|
375
413
|
end
|
|
376
|
-
[:ident, @scanner[0]]
|
|
414
|
+
[:ident, Sass::Util.normalize_ident_escapes(@scanner[0])]
|
|
377
415
|
end
|
|
378
416
|
|
|
379
417
|
def color
|
data/lib/sass/script/parser.rb
CHANGED
|
@@ -78,7 +78,7 @@ module Sass
|
|
|
78
78
|
# Parses a SassScript expression,
|
|
79
79
|
# ending it when it encounters one of the given identifier tokens.
|
|
80
80
|
#
|
|
81
|
-
# @param tokens [#include?(String)] A set of strings that delimit the expression.
|
|
81
|
+
# @param tokens [#include?(String | Symbol)] A set of strings or symbols that delimit the expression.
|
|
82
82
|
# @return [Script::Tree::Node] The root node of the parse tree
|
|
83
83
|
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
|
|
84
84
|
def parse_until(tokens)
|
|
@@ -270,7 +270,11 @@ module Sass
|
|
|
270
270
|
interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect})
|
|
271
271
|
return interp if interp
|
|
272
272
|
return unless e = #{sub}
|
|
273
|
-
|
|
273
|
+
|
|
274
|
+
while tok = peek_toks(#{ops.map {|o| o.inspect}.join(', ')})
|
|
275
|
+
return e if @stop_at && @stop_at.include?(tok.type)
|
|
276
|
+
@lexer.next
|
|
277
|
+
|
|
274
278
|
if interp = try_op_before_interp(tok, e)
|
|
275
279
|
other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp)
|
|
276
280
|
return interp unless other_interp
|
|
@@ -386,8 +390,11 @@ RUBY
|
|
|
386
390
|
|
|
387
391
|
def try_ops_after_interp(ops, name, prev = nil)
|
|
388
392
|
return unless @lexer.after_interpolation?
|
|
389
|
-
op =
|
|
393
|
+
op = peek_toks(*ops)
|
|
390
394
|
return unless op
|
|
395
|
+
return if @stop_at && @stop_at.include?(op.type)
|
|
396
|
+
@lexer.next
|
|
397
|
+
|
|
391
398
|
interp = try_op_before_interp(op, prev, :after_interp)
|
|
392
399
|
return interp if interp
|
|
393
400
|
|
|
@@ -416,7 +423,7 @@ RUBY
|
|
|
416
423
|
while (interp = try_tok(:begin_interpolation))
|
|
417
424
|
wb = @lexer.whitespace?(interp)
|
|
418
425
|
char_before = @lexer.char(interp.pos - 1)
|
|
419
|
-
mid = assert_expr :expr
|
|
426
|
+
mid = without_stop_at {assert_expr :expr}
|
|
420
427
|
assert_tok :end_interpolation
|
|
421
428
|
wa = @lexer.whitespace?
|
|
422
429
|
char_after = @lexer.char
|
|
@@ -497,7 +504,7 @@ RUBY
|
|
|
497
504
|
unary :not, :ident
|
|
498
505
|
|
|
499
506
|
def ident
|
|
500
|
-
return
|
|
507
|
+
return css_min_max unless @lexer.peek && @lexer.peek.type == :ident
|
|
501
508
|
return if @stop_at && @stop_at.include?(@lexer.peek.value)
|
|
502
509
|
|
|
503
510
|
name = @lexer.next
|
|
@@ -514,6 +521,122 @@ RUBY
|
|
|
514
521
|
end
|
|
515
522
|
end
|
|
516
523
|
|
|
524
|
+
def css_min_max
|
|
525
|
+
@lexer.try do
|
|
526
|
+
next unless tok = try_tok(:funcall)
|
|
527
|
+
next unless %w[min max].include?(tok.value.downcase)
|
|
528
|
+
next unless contents = min_max_contents
|
|
529
|
+
node(array_to_interpolation(["#{tok.value}(", *contents]),
|
|
530
|
+
tok.source_range.start_pos, source_position)
|
|
531
|
+
end || funcall
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def min_max_contents(allow_comma: true)
|
|
535
|
+
result = []
|
|
536
|
+
loop do
|
|
537
|
+
if tok = try_tok(:number)
|
|
538
|
+
result << tok.value.to_s
|
|
539
|
+
elsif value = min_max_interpolation
|
|
540
|
+
result << value
|
|
541
|
+
elsif value = min_max_calc
|
|
542
|
+
result << value.value
|
|
543
|
+
elsif value = min_max_function ||
|
|
544
|
+
min_max_parens ||
|
|
545
|
+
nested_min_max
|
|
546
|
+
result.concat value
|
|
547
|
+
else
|
|
548
|
+
return
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
if try_tok(:rparen)
|
|
552
|
+
result << ")"
|
|
553
|
+
return result
|
|
554
|
+
elsif tok = try_tok(:plus) || try_tok(:minus) || try_tok(:times) || try_tok(:div)
|
|
555
|
+
result << " #{Lexer::OPERATORS_REVERSE[tok.type]} "
|
|
556
|
+
elsif allow_comma && try_tok(:comma)
|
|
557
|
+
result << ", "
|
|
558
|
+
else
|
|
559
|
+
return
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def min_max_interpolation
|
|
565
|
+
without_stop_at do
|
|
566
|
+
tok = try_tok(:begin_interpolation)
|
|
567
|
+
return unless tok
|
|
568
|
+
expr = without_stop_at {assert_expr :expr}
|
|
569
|
+
assert_tok :end_interpolation
|
|
570
|
+
expr
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def min_max_function
|
|
575
|
+
return unless tok = peek_tok(:funcall)
|
|
576
|
+
return unless %w[calc env var].include?(tok.value.downcase)
|
|
577
|
+
@lexer.next
|
|
578
|
+
result = [tok.value, '(', *declaration_value, ')']
|
|
579
|
+
assert_tok :rparen
|
|
580
|
+
result
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def min_max_calc
|
|
584
|
+
return unless tok = peek_tok(:special_fun)
|
|
585
|
+
return unless tok.value.value.downcase.start_with?("calc(")
|
|
586
|
+
@lexer.next.value
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def min_max_parens
|
|
590
|
+
return unless try_tok :lparen
|
|
591
|
+
return unless contents = min_max_contents(allow_comma: false)
|
|
592
|
+
['(', *contents]
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def nested_min_max
|
|
596
|
+
return unless tok = peek_tok(:funcall)
|
|
597
|
+
return unless %w[min max].include?(tok.value.downcase)
|
|
598
|
+
@lexer.next
|
|
599
|
+
return unless contents = min_max_contents
|
|
600
|
+
[tok.value, '(', *contents]
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def declaration_value
|
|
604
|
+
result = []
|
|
605
|
+
brackets = []
|
|
606
|
+
loop do
|
|
607
|
+
result << @lexer.str do
|
|
608
|
+
until @lexer.done? ||
|
|
609
|
+
peek_toks(:begin_interpolation,
|
|
610
|
+
:end_interpolation,
|
|
611
|
+
:lcurly,
|
|
612
|
+
:lparen,
|
|
613
|
+
:lsquare,
|
|
614
|
+
:rparen,
|
|
615
|
+
:rsquare)
|
|
616
|
+
@lexer.next || @lexer.next_char
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
if try_tok(:begin_interpolation)
|
|
621
|
+
result << assert_expr(:expr)
|
|
622
|
+
assert_tok :end_interpolation
|
|
623
|
+
elsif tok = try_toks(:lcurly, :lparen, :lsquare)
|
|
624
|
+
brackets << case tok.type
|
|
625
|
+
when :lcurly; :end_interpolation
|
|
626
|
+
when :lparen; :rparen
|
|
627
|
+
when :lsquare; :rsquare
|
|
628
|
+
end
|
|
629
|
+
result << Lexer::OPERATORS_REVERSE[tok.type]
|
|
630
|
+
elsif brackets.empty?
|
|
631
|
+
return result
|
|
632
|
+
else
|
|
633
|
+
bracket = brackets.pop
|
|
634
|
+
assert_tok bracket
|
|
635
|
+
result << Lexer::OPERATORS_REVERSE[bracket]
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
|
|
517
640
|
def funcall
|
|
518
641
|
tok = try_tok(:funcall)
|
|
519
642
|
return raw unless tok
|
|
@@ -530,28 +653,30 @@ RUBY
|
|
|
530
653
|
return [], nil unless try_tok(:lparen)
|
|
531
654
|
end
|
|
532
655
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
splat
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
656
|
+
without_stop_at do
|
|
657
|
+
res = []
|
|
658
|
+
splat = nil
|
|
659
|
+
must_have_default = false
|
|
660
|
+
loop do
|
|
661
|
+
break if peek_tok(:rparen)
|
|
662
|
+
c = assert_tok(:const)
|
|
663
|
+
var = node(Script::Tree::Variable.new(c.value), c.source_range)
|
|
664
|
+
if try_tok(:colon)
|
|
665
|
+
val = assert_expr(:space)
|
|
666
|
+
must_have_default = true
|
|
667
|
+
elsif try_tok(:splat)
|
|
668
|
+
splat = var
|
|
669
|
+
break
|
|
670
|
+
elsif must_have_default
|
|
671
|
+
raise SyntaxError.new(
|
|
672
|
+
"Required argument #{var.inspect} must come before any optional arguments.")
|
|
673
|
+
end
|
|
674
|
+
res << [var, val]
|
|
675
|
+
break unless try_tok(:comma)
|
|
549
676
|
end
|
|
550
|
-
|
|
551
|
-
|
|
677
|
+
assert_tok(:rparen)
|
|
678
|
+
return res, splat
|
|
552
679
|
end
|
|
553
|
-
assert_tok(:rparen)
|
|
554
|
-
return res, splat
|
|
555
680
|
end
|
|
556
681
|
|
|
557
682
|
def fn_arglist
|
|
@@ -563,36 +688,38 @@ RUBY
|
|
|
563
688
|
end
|
|
564
689
|
|
|
565
690
|
def arglist(subexpr, description)
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
691
|
+
without_stop_at do
|
|
692
|
+
args = []
|
|
693
|
+
keywords = Sass::Util::NormalizedMap.new
|
|
694
|
+
splat = nil
|
|
695
|
+
while (e = send(subexpr))
|
|
696
|
+
if @lexer.peek && @lexer.peek.type == :colon
|
|
697
|
+
name = e
|
|
698
|
+
@lexer.expected!("comma") unless name.is_a?(Tree::Variable)
|
|
699
|
+
assert_tok(:colon)
|
|
700
|
+
value = assert_expr(subexpr, description)
|
|
701
|
+
|
|
702
|
+
if keywords[name.name]
|
|
703
|
+
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
|
|
704
|
+
end
|
|
579
705
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
706
|
+
keywords[name.name] = value
|
|
707
|
+
else
|
|
708
|
+
if try_tok(:splat)
|
|
709
|
+
return args, keywords, splat, e if splat
|
|
710
|
+
splat, e = e, nil
|
|
711
|
+
elsif splat
|
|
712
|
+
raise SyntaxError.new("Only keyword arguments may follow variable arguments (...).")
|
|
713
|
+
elsif !keywords.empty?
|
|
714
|
+
raise SyntaxError.new("Positional arguments must come before keyword arguments.")
|
|
715
|
+
end
|
|
716
|
+
args << e if e
|
|
589
717
|
end
|
|
590
|
-
args << e if e
|
|
591
|
-
end
|
|
592
718
|
|
|
593
|
-
|
|
719
|
+
return args, keywords, splat unless try_tok(:comma)
|
|
720
|
+
end
|
|
721
|
+
return args, keywords
|
|
594
722
|
end
|
|
595
|
-
return args, keywords
|
|
596
723
|
end
|
|
597
724
|
|
|
598
725
|
def raw
|
|
@@ -606,7 +733,7 @@ RUBY
|
|
|
606
733
|
return square_list unless first
|
|
607
734
|
str = literal_node(first.value, first.source_range)
|
|
608
735
|
return str unless try_tok(:string_interpolation)
|
|
609
|
-
mid = assert_expr :expr
|
|
736
|
+
mid = without_stop_at {assert_expr :expr}
|
|
610
737
|
assert_tok :end_interpolation
|
|
611
738
|
last = assert_expr(:special_fun)
|
|
612
739
|
node(
|
|
@@ -618,54 +745,58 @@ RUBY
|
|
|
618
745
|
start_pos = source_position
|
|
619
746
|
return paren unless try_tok(:lsquare)
|
|
620
747
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
while (e = interpolation(inner: :or_expr))
|
|
627
|
-
elements << e
|
|
628
|
-
end
|
|
629
|
-
|
|
630
|
-
# If there's a comma after a space-separated list, it's actually a
|
|
631
|
-
# space-separated list nested in a comma-separated list.
|
|
632
|
-
if try_tok(:comma)
|
|
633
|
-
e = if elements.length == 1
|
|
634
|
-
elements.first
|
|
635
|
-
else
|
|
636
|
-
node(
|
|
637
|
-
Sass::Script::Tree::ListLiteral.new(elements, separator: :space),
|
|
638
|
-
space_start_pos)
|
|
639
|
-
end
|
|
748
|
+
without_stop_at do
|
|
749
|
+
space_start_pos = source_position
|
|
750
|
+
e = interpolation(inner: :or_expr)
|
|
751
|
+
separator = nil
|
|
752
|
+
if e
|
|
640
753
|
elements = [e]
|
|
641
|
-
|
|
642
|
-
while (e = space)
|
|
754
|
+
while (e = interpolation(inner: :or_expr))
|
|
643
755
|
elements << e
|
|
644
|
-
break unless try_tok(:comma)
|
|
645
756
|
end
|
|
646
|
-
|
|
757
|
+
|
|
758
|
+
# If there's a comma after a space-separated list, it's actually a
|
|
759
|
+
# space-separated list nested in a comma-separated list.
|
|
760
|
+
if try_tok(:comma)
|
|
761
|
+
e = if elements.length == 1
|
|
762
|
+
elements.first
|
|
763
|
+
else
|
|
764
|
+
node(
|
|
765
|
+
Sass::Script::Tree::ListLiteral.new(elements, separator: :space),
|
|
766
|
+
space_start_pos)
|
|
767
|
+
end
|
|
768
|
+
elements = [e]
|
|
769
|
+
|
|
770
|
+
while (e = space)
|
|
771
|
+
elements << e
|
|
772
|
+
break unless try_tok(:comma)
|
|
773
|
+
end
|
|
774
|
+
separator = :comma
|
|
775
|
+
else
|
|
776
|
+
separator = :space if elements.length > 1
|
|
777
|
+
end
|
|
647
778
|
else
|
|
648
|
-
|
|
779
|
+
elements = []
|
|
649
780
|
end
|
|
650
|
-
else
|
|
651
|
-
elements = []
|
|
652
|
-
end
|
|
653
781
|
|
|
654
|
-
|
|
655
|
-
|
|
782
|
+
assert_tok(:rsquare)
|
|
783
|
+
end_pos = source_position
|
|
656
784
|
|
|
657
|
-
|
|
658
|
-
|
|
785
|
+
node(Sass::Script::Tree::ListLiteral.new(elements, separator: separator, bracketed: true),
|
|
786
|
+
start_pos, end_pos)
|
|
787
|
+
end
|
|
659
788
|
end
|
|
660
789
|
|
|
661
790
|
def paren
|
|
662
791
|
return variable unless try_tok(:lparen)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
792
|
+
without_stop_at do
|
|
793
|
+
start_pos = source_position
|
|
794
|
+
e = map
|
|
795
|
+
e.force_division! if e
|
|
796
|
+
end_pos = source_position
|
|
797
|
+
assert_tok(:rparen)
|
|
798
|
+
e || node(Sass::Script::Tree::ListLiteral.new([]), start_pos, end_pos)
|
|
799
|
+
end
|
|
669
800
|
end
|
|
670
801
|
|
|
671
802
|
def variable
|
|
@@ -682,7 +813,7 @@ RUBY
|
|
|
682
813
|
return str unless try_tok(:string_interpolation)
|
|
683
814
|
mid = assert_expr :expr
|
|
684
815
|
assert_tok :end_interpolation
|
|
685
|
-
last = assert_expr(:string)
|
|
816
|
+
last = without_stop_at {assert_expr(:string)}
|
|
686
817
|
node(Tree::StringInterpolation.new(str, mid, last), first.source_range.start_pos)
|
|
687
818
|
end
|
|
688
819
|
|
|
@@ -740,7 +871,12 @@ RUBY
|
|
|
740
871
|
def peek_tok(name)
|
|
741
872
|
# Avoids an array allocation caused by argument globbing in the try_toks method.
|
|
742
873
|
peeked = @lexer.peek
|
|
743
|
-
peeked && name == peeked.type
|
|
874
|
+
peeked && name == peeked.type && peeked
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def peek_toks(*names)
|
|
878
|
+
peeked = @lexer.peek
|
|
879
|
+
peeked && names.include?(peeked.type) && peeked
|
|
744
880
|
end
|
|
745
881
|
|
|
746
882
|
def try_tok(name)
|
|
@@ -748,8 +884,7 @@ RUBY
|
|
|
748
884
|
end
|
|
749
885
|
|
|
750
886
|
def try_toks(*names)
|
|
751
|
-
|
|
752
|
-
peeked && names.include?(peeked.type) && @lexer.next
|
|
887
|
+
peek_toks(*names) && @lexer.next
|
|
753
888
|
end
|
|
754
889
|
|
|
755
890
|
def assert_done
|
|
@@ -763,6 +898,14 @@ RUBY
|
|
|
763
898
|
end
|
|
764
899
|
end
|
|
765
900
|
|
|
901
|
+
def without_stop_at
|
|
902
|
+
old_stop_at = @stop_at
|
|
903
|
+
@stop_at = nil
|
|
904
|
+
yield
|
|
905
|
+
ensure
|
|
906
|
+
@stop_at = old_stop_at
|
|
907
|
+
end
|
|
908
|
+
|
|
766
909
|
# @overload node(value, source_range)
|
|
767
910
|
# @param value [Sass::Script::Value::Base]
|
|
768
911
|
# @param source_range [Sass::Source::Range]
|
|
@@ -795,6 +938,29 @@ RUBY
|
|
|
795
938
|
node
|
|
796
939
|
end
|
|
797
940
|
|
|
941
|
+
# Converts an array of strings and expressions to a string interoplation
|
|
942
|
+
# object.
|
|
943
|
+
#
|
|
944
|
+
# @param array [Array<Script::Tree:Node | String>]
|
|
945
|
+
# @return [Script::Tree::StringInterpolation]
|
|
946
|
+
def array_to_interpolation(array)
|
|
947
|
+
Sass::Util.merge_adjacent_strings(array).reverse.inject(nil) do |after, value|
|
|
948
|
+
if value.is_a?(::String)
|
|
949
|
+
literal = Sass::Script::Tree::Literal.new(
|
|
950
|
+
Sass::Script::Value::String.new(value))
|
|
951
|
+
next literal unless after
|
|
952
|
+
Sass::Script::Tree::StringInterpolation.new(literal, after.mid, after.after)
|
|
953
|
+
else
|
|
954
|
+
Sass::Script::Tree::StringInterpolation.new(
|
|
955
|
+
Sass::Script::Tree::Literal.new(
|
|
956
|
+
Sass::Script::Value::String.new('')),
|
|
957
|
+
value,
|
|
958
|
+
after || Sass::Script::Tree::Literal.new(
|
|
959
|
+
Sass::Script::Value::String.new('')))
|
|
960
|
+
end
|
|
961
|
+
end
|
|
962
|
+
end
|
|
963
|
+
|
|
798
964
|
# Checks a script node for any immediately-deprecated interpolations, and
|
|
799
965
|
# emits warnings for them.
|
|
800
966
|
#
|