sass 3.3.14 → 3.4.0.rc.1
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 +5 -5
- 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.rb +0 -5
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +28 -39
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +419 -0
- data/lib/sass/features.rb +6 -0
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/importers/base.rb +5 -1
- data/lib/sass/importers/filesystem.rb +4 -21
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +32 -136
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +363 -39
- data/lib/sass/script/lexer.rb +68 -50
- data/lib/sass/script/parser.rb +29 -14
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +19 -1
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +32 -12
- 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 +60 -14
- data/lib/sass/script/value/string.rb +53 -9
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +175 -319
- data/lib/sass/scss/rx.rb +14 -5
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector.rb +56 -193
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +91 -12
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +99 -31
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +101 -37
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +23 -9
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +1 -1
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/directive_node.rb +7 -1
- data/lib/sass/tree/error_node.rb +18 -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 +11 -6
- data/lib/sass/tree/visitors/check_nesting.rb +3 -4
- data/lib/sass/tree/visitors/convert.rb +8 -17
- data/lib/sass/tree/visitors/cssize.rb +12 -24
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +43 -28
- data/lib/sass/tree/visitors/set_options.rb +5 -0
- data/lib/sass/tree/visitors/to_css.rb +14 -13
- data/lib/sass/util.rb +94 -90
- data/test/sass/cache_test.rb +1 -1
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +5 -14
- data/test/sass/conversion_test.rb +47 -1
- data/test/sass/css2sass_test.rb +3 -3
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +128 -191
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +234 -17
- data/test/sass/functions_test.rb +268 -213
- 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 +12 -11
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +7 -4
- data/test/sass/script_test.rb +202 -79
- data/test/sass/scss/css_test.rb +95 -25
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +363 -19
- data/test/sass/source_map_test.rb +48 -41
- 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 +1 -1
- data/test/sass/value_helpers_test.rb +3 -3
- data/test/test_helper.rb +2 -2
- metadata +30 -7
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
@@ -121,8 +121,115 @@ module Sass::Script::Value
|
|
121
121
|
end
|
122
122
|
alias_method :identifier, :unquoted_string
|
123
123
|
|
124
|
+
# Parses a user-provided selector.
|
125
|
+
#
|
126
|
+
# @param value [Sass::Script::Value::String, Sass::Script::Value::List]
|
127
|
+
# The selector to parse. This can be either a string, a list of
|
128
|
+
# strings, or a list of lists of strings as returned by `&`.
|
129
|
+
# @param name [Symbol, nil]
|
130
|
+
# If provided, the name of the selector argument. This is used
|
131
|
+
# for error reporting.
|
132
|
+
# @param allow_parent_ref [Boolean]
|
133
|
+
# Whether the parsed selector should allow parent references.
|
134
|
+
# @return [Sass::Selector::CommaSequence] The parsed selector.
|
135
|
+
# @throw [ArgumentError] if the parse failed for any reason.
|
136
|
+
def parse_selector(value, name = nil, allow_parent_ref = false)
|
137
|
+
str = normalize_selector(value, name)
|
138
|
+
begin
|
139
|
+
Sass::SCSS::StaticParser.new(str, nil, nil, 1, 1, allow_parent_ref).parse_selector
|
140
|
+
rescue Sass::SyntaxError => e
|
141
|
+
err = "#{value.inspect} is not a valid selector: #{e}"
|
142
|
+
err = "$#{name.to_s.gsub('_', '-')}: #{err}" if name
|
143
|
+
raise ArgumentError.new(err)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Parses a user-provided complex selector.
|
148
|
+
#
|
149
|
+
# A complex selector can contain combinators but cannot contain commas.
|
150
|
+
#
|
151
|
+
# @param value [Sass::Script::Value::String, Sass::Script::Value::List]
|
152
|
+
# The selector to parse. This can be either a string or a list of
|
153
|
+
# strings.
|
154
|
+
# @param name [Symbol, nil]
|
155
|
+
# If provided, the name of the selector argument. This is used
|
156
|
+
# for error reporting.
|
157
|
+
# @param allow_parent_ref [Boolean]
|
158
|
+
# Whether the parsed selector should allow parent references.
|
159
|
+
# @return [Sass::Selector::Sequence] The parsed selector.
|
160
|
+
# @throw [ArgumentError] if the parse failed for any reason.
|
161
|
+
def parse_complex_selector(value, name = nil, allow_parent_ref = false)
|
162
|
+
selector = parse_selector(value, name, allow_parent_ref)
|
163
|
+
return seq if selector.members.length == 1
|
164
|
+
|
165
|
+
err = "#{value.inspect} is not a complex selector"
|
166
|
+
err = "$#{name.to_s.gsub('_', '-')}: #{err}" if name
|
167
|
+
raise ArgumentError.new(err)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Parses a user-provided compound selector.
|
171
|
+
#
|
172
|
+
# A compound selector cannot contain combinators or commas.
|
173
|
+
#
|
174
|
+
# @param value [Sass::Script::Value::String] The selector to parse.
|
175
|
+
# @param name [Symbol, nil]
|
176
|
+
# If provided, the name of the selector argument. This is used
|
177
|
+
# for error reporting.
|
178
|
+
# @param allow_parent_ref [Boolean]
|
179
|
+
# Whether the parsed selector should allow parent references.
|
180
|
+
# @return [Sass::Selector::SimpleSequence] The parsed selector.
|
181
|
+
# @throw [ArgumentError] if the parse failed for any reason.
|
182
|
+
def parse_compound_selector(value, name = nil, allow_parent_ref = false)
|
183
|
+
assert_type value, :String, name
|
184
|
+
selector = parse_selector(value, name, allow_parent_ref)
|
185
|
+
seq = selector.members.first
|
186
|
+
sseq = seq.members.first
|
187
|
+
if selector.members.length == 1 && seq.members.length == 1 &&
|
188
|
+
sseq.is_a?(Sass::Selector::SimpleSequence)
|
189
|
+
return sseq
|
190
|
+
end
|
191
|
+
|
192
|
+
err = "#{value.inspect} is not a compound selector"
|
193
|
+
err = "$#{name.to_s.gsub('_', '-')}: #{err}" if name
|
194
|
+
raise ArgumentError.new(err)
|
195
|
+
end
|
196
|
+
|
124
197
|
private
|
125
198
|
|
199
|
+
# Converts a user-provided selector into string form or throws an
|
200
|
+
# ArgumentError if it's in an invalid format.
|
201
|
+
def normalize_selector(value, name)
|
202
|
+
if (str = selector_to_str(value))
|
203
|
+
return str
|
204
|
+
end
|
205
|
+
|
206
|
+
err = "#{value.inspect} is not a valid selector: it must be a string,\n" +
|
207
|
+
"a list of strings, or a list of lists of strings"
|
208
|
+
err = "$#{name.to_s.gsub('_', '-')}: #{err}" if name
|
209
|
+
raise ArgumentError.new(err)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Converts a user-provided selector into string form or returns
|
213
|
+
# `nil` if it's in an invalid format.
|
214
|
+
def selector_to_str(value)
|
215
|
+
return value.value if value.is_a?(Sass::Script::String)
|
216
|
+
return unless value.is_a?(Sass::Script::List)
|
217
|
+
|
218
|
+
if value.separator == :comma
|
219
|
+
return value.to_a.map do |complex|
|
220
|
+
next complex.value if complex.is_a?(Sass::Script::String)
|
221
|
+
return unless complex.is_a?(Sass::Script::List) && complex.separator == :space
|
222
|
+
return unless (str = selector_to_str(complex))
|
223
|
+
str
|
224
|
+
end.join(', ')
|
225
|
+
end
|
226
|
+
|
227
|
+
value.to_a.map do |compound|
|
228
|
+
return unless compound.is_a?(Sass::Script::String)
|
229
|
+
compound.value
|
230
|
+
end.join(' ')
|
231
|
+
end
|
232
|
+
|
126
233
|
# @private
|
127
234
|
VALID_UNIT = /#{Sass::SCSS::RX::NMSTART}#{Sass::SCSS::RX::NMCHAR}|%*/
|
128
235
|
|
@@ -65,24 +65,9 @@ module Sass::Script::Value
|
|
65
65
|
# @see Value#to_h
|
66
66
|
def to_h
|
67
67
|
return Sass::Util.ordered_hash if value.empty?
|
68
|
-
return @map ||= Sass::Util.to_hash(value.map {|e| e.to_a}) if is_pseudo_map?
|
69
68
|
super
|
70
69
|
end
|
71
70
|
|
72
|
-
# Returns whether a warning still needs to be printed for this list being used as a map.
|
73
|
-
#
|
74
|
-
# @return [Boolean]
|
75
|
-
def needs_map_warning?
|
76
|
-
!@value.empty? && !@map
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns whether this is a list of pairs that can be used as a map.
|
80
|
-
#
|
81
|
-
# @return [Boolean]
|
82
|
-
def is_pseudo_map?
|
83
|
-
@is_pseudo_map ||= value.all? {|e| e.is_a?(Sass::Script::Value::List) && e.to_a.length == 2}
|
84
|
-
end
|
85
|
-
|
86
71
|
# @see Value#inspect
|
87
72
|
def inspect
|
88
73
|
"(#{value.map {|e| e.inspect}.join(sep_str(nil))})"
|
@@ -447,25 +447,71 @@ module Sass::Script::Value
|
|
447
447
|
end
|
448
448
|
end
|
449
449
|
|
450
|
-
#
|
451
|
-
|
452
|
-
|
453
|
-
#
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
450
|
+
# This is the source data for all the unit logic. It's pre-processed to make
|
451
|
+
# it efficient to figure out whether a set of units is mutually compatible
|
452
|
+
# and what the conversion ratio is between two units.
|
453
|
+
#
|
454
|
+
# These come from http://www.w3.org/TR/2012/WD-css3-values-20120308/.
|
455
|
+
relative_sizes = [
|
456
|
+
{
|
457
|
+
'in' => Rational(1),
|
458
|
+
'cm' => Rational(1, 2.54),
|
459
|
+
'pc' => Rational(1, 6),
|
460
|
+
'mm' => Rational(1, 25.4),
|
461
|
+
'pt' => Rational(1, 72),
|
462
|
+
'px' => Rational(1, 96)
|
463
|
+
},
|
464
|
+
{
|
465
|
+
'deg' => Rational(1, 360),
|
466
|
+
'grad' => Rational(1, 400),
|
467
|
+
'rad' => Rational(1, 2 * Math::PI),
|
468
|
+
'turn' => Rational(1)
|
469
|
+
},
|
470
|
+
{
|
471
|
+
's' => Rational(1),
|
472
|
+
'ms' => Rational(1, 1000)
|
473
|
+
},
|
474
|
+
{
|
475
|
+
'Hz' => Rational(1),
|
476
|
+
'kHz' => Rational(1000)
|
477
|
+
},
|
478
|
+
{
|
479
|
+
'dpi' => Rational(1),
|
480
|
+
'dpcm' => Rational(1, 2.54),
|
481
|
+
'dppx' => Rational(1, 96)
|
482
|
+
}
|
483
|
+
]
|
484
|
+
|
485
|
+
# A hash from each known unit to the set of units that it's mutually
|
486
|
+
# convertible with.
|
487
|
+
MUTUALLY_CONVERTIBLE = {}
|
488
|
+
relative_sizes.map do |values|
|
489
|
+
set = values.keys.to_set
|
490
|
+
values.keys.each {|name| MUTUALLY_CONVERTIBLE[name] = set}
|
491
|
+
end
|
492
|
+
|
493
|
+
# A two-dimensional hash from two units to the conversion ratio between
|
494
|
+
# them. Multiply `X` by `CONVERSION_TABLE[X][Y]` to convert it to `Y`.
|
495
|
+
CONVERSION_TABLE = {}
|
496
|
+
relative_sizes.each do |values|
|
497
|
+
values.each do |(name1, value1)|
|
498
|
+
CONVERSION_TABLE[name1] ||= {}
|
499
|
+
values.each do |(name2, value2)|
|
500
|
+
value = value1 / value2
|
501
|
+
CONVERSION_TABLE[name1][name2] = value.denominator == 1 ? value.to_i : value.to_f
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
460
505
|
|
461
506
|
def conversion_factor(from_unit, to_unit)
|
462
|
-
|
463
|
-
return 1.0 / conversion_factor(to_unit, from_unit) if res.nil?
|
464
|
-
res
|
507
|
+
CONVERSION_TABLE[from_unit][to_unit]
|
465
508
|
end
|
466
509
|
|
467
510
|
def convertable?(units)
|
468
|
-
Array(units).
|
511
|
+
units = Array(units).to_set
|
512
|
+
return true if units.empty?
|
513
|
+
return false unless (mutually_convertible = MUTUALLY_CONVERTIBLE[units.first])
|
514
|
+
units.subset?(mutually_convertible)
|
469
515
|
end
|
470
516
|
|
471
517
|
def sans_common_units(units1, units2)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module Sass::Script::Value
|
2
3
|
# A SassScript object representing a CSS string *or* a CSS identifier.
|
3
4
|
class String < Base
|
@@ -13,6 +14,52 @@ module Sass::Script::Value
|
|
13
14
|
# @return [Symbol] `:string` or `:identifier`
|
14
15
|
attr_reader :type
|
15
16
|
|
17
|
+
def self.value(contents)
|
18
|
+
contents.gsub("\\\n", "").gsub(/\\(?:([0-9a-fA-F]{1,6})\s?|(.))/) do
|
19
|
+
next $2 if $2
|
20
|
+
# Handle unicode escapes as per CSS Syntax Level 3 section 4.3.8.
|
21
|
+
code_point = $1.to_i(16)
|
22
|
+
if code_point == 0 || code_point > 0x10FFFF ||
|
23
|
+
(code_point >= 0xD800 && code_point <= 0xDFFF)
|
24
|
+
'�'
|
25
|
+
else
|
26
|
+
[code_point].pack("U")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.quote(contents, quote = nil)
|
32
|
+
# Short-circuit if there are no characters that need quoting.
|
33
|
+
unless contents =~ /[\n\\"']/
|
34
|
+
quote ||= '"'
|
35
|
+
return "#{quote}#{contents}#{quote}"
|
36
|
+
end
|
37
|
+
|
38
|
+
if quote.nil?
|
39
|
+
if contents.include?('"')
|
40
|
+
if contents.include?("'")
|
41
|
+
quote = '"'
|
42
|
+
else
|
43
|
+
quote = "'"
|
44
|
+
end
|
45
|
+
else
|
46
|
+
quote = '"'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Replace single backslashes with multiples.
|
51
|
+
contents = contents.gsub("\\", "\\\\\\\\")
|
52
|
+
|
53
|
+
if quote == '"'
|
54
|
+
contents = contents.gsub('"', "\\\"")
|
55
|
+
else
|
56
|
+
contents = contents.gsub("'", "\\'")
|
57
|
+
end
|
58
|
+
|
59
|
+
contents = contents.gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")
|
60
|
+
"#{quote}#{contents}#{quote}"
|
61
|
+
end
|
62
|
+
|
16
63
|
# Creates a new string.
|
17
64
|
#
|
18
65
|
# @param value [String] See \{#value}
|
@@ -30,20 +77,17 @@ module Sass::Script::Value
|
|
30
77
|
|
31
78
|
# @see Value#to_s
|
32
79
|
def to_s(opts = {})
|
33
|
-
if @type == :identifier
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
|
38
|
-
return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
|
39
|
-
return "\"#{value}\"" unless value.include?('"')
|
40
|
-
return "'#{value}'" unless value.include?("'")
|
41
|
-
"\"#{value.gsub('"', "\\\"")}\"" # '
|
80
|
+
return @value.gsub(/\n\s*/, ' ') if @type == :identifier
|
81
|
+
Sass::Script::Value::String.quote(value, opts[:quote])
|
42
82
|
end
|
43
83
|
|
44
84
|
# @see Value#to_sass
|
45
85
|
def to_sass(opts = {})
|
46
86
|
to_s
|
47
87
|
end
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
String.quote(value)
|
91
|
+
end
|
48
92
|
end
|
49
93
|
end
|
data/lib/sass/scss/css_parser.rb
CHANGED
@@ -11,7 +11,7 @@ module Sass
|
|
11
11
|
|
12
12
|
def placeholder_selector; nil; end
|
13
13
|
def parent_selector; nil; end
|
14
|
-
def interpolation; nil; end
|
14
|
+
def interpolation(warn_for_color = false); nil; end
|
15
15
|
def use_css_import?; true; end
|
16
16
|
|
17
17
|
def block_child(context)
|
@@ -25,10 +25,16 @@ module Sass
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def nested_properties!(node
|
28
|
+
def nested_properties!(node)
|
29
29
|
expected('expression (e.g. 1px, bold)')
|
30
30
|
end
|
31
31
|
|
32
|
+
def ruleset
|
33
|
+
start_pos = source_position
|
34
|
+
return unless (selector = selector_comma_sequence)
|
35
|
+
block(node(Sass::Tree::RuleNode.new(selector, range(start_pos)), start_pos), :ruleset)
|
36
|
+
end
|
37
|
+
|
32
38
|
@sass_script_parser = Class.new(Sass::Script::CssParser)
|
33
39
|
@sass_script_parser.send(:include, ScriptParser)
|
34
40
|
end
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
require 'set'
|
2
3
|
|
3
4
|
module Sass
|
@@ -180,7 +181,7 @@ module Sass
|
|
180
181
|
|
181
182
|
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
|
182
183
|
:each, :while, :if, :else, :extend, :import, :media, :charset, :content,
|
183
|
-
:_moz_document, :at_root]
|
184
|
+
:_moz_document, :at_root, :error]
|
184
185
|
|
185
186
|
PREFIXED_DIRECTIVES = Set[:supports]
|
186
187
|
|
@@ -196,10 +197,7 @@ module Sass
|
|
196
197
|
return dir
|
197
198
|
end
|
198
199
|
|
199
|
-
|
200
|
-
# but some (e.g. @page) take selector-like arguments.
|
201
|
-
# Some take no arguments at all.
|
202
|
-
val = expr || selector
|
200
|
+
val = almost_any_value
|
203
201
|
val = val ? ["@#{name} "] + Sass::Util.strip_string_array(val) : ["@#{name}"]
|
204
202
|
directive_body(val, start_pos)
|
205
203
|
end
|
@@ -222,7 +220,7 @@ module Sass
|
|
222
220
|
end
|
223
221
|
|
224
222
|
def prefixed_directive(name, start_pos)
|
225
|
-
sym = name
|
223
|
+
sym = deprefix(name).gsub('-', '_').to_sym
|
226
224
|
PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name, start_pos)
|
227
225
|
end
|
228
226
|
|
@@ -355,10 +353,12 @@ module Sass
|
|
355
353
|
end
|
356
354
|
|
357
355
|
def extend_directive(start_pos)
|
358
|
-
|
356
|
+
selector_start_pos = source_position
|
357
|
+
@expected = "selector"
|
358
|
+
selector = Sass::Util.strip_string_array(expr!(:almost_any_value))
|
359
359
|
optional = tok(OPTIONAL)
|
360
360
|
ss
|
361
|
-
node(Sass::Tree::ExtendNode.new(selector, !!optional,
|
361
|
+
node(Sass::Tree::ExtendNode.new(selector, !!optional, range(selector_start_pos)), start_pos)
|
362
362
|
end
|
363
363
|
|
364
364
|
def import_directive(start_pos)
|
@@ -376,7 +376,7 @@ module Sass
|
|
376
376
|
|
377
377
|
def import_arg
|
378
378
|
start_pos = source_position
|
379
|
-
return unless (str =
|
379
|
+
return unless (str = string) || (uri = tok?(/url\(/i))
|
380
380
|
if uri
|
381
381
|
str = sass_script(:parse_string)
|
382
382
|
ss
|
@@ -384,16 +384,15 @@ module Sass
|
|
384
384
|
ss
|
385
385
|
return node(Tree::CssImportNode.new(str, media.to_a), start_pos)
|
386
386
|
end
|
387
|
-
|
388
|
-
path = @scanner[1] || @scanner[2]
|
389
387
|
ss
|
390
388
|
|
391
389
|
media = media_query_list
|
392
|
-
if
|
393
|
-
return node(Sass::Tree::CssImportNode.new(
|
390
|
+
if str =~ %r{^(https?:)?//} || media || use_css_import?
|
391
|
+
return node(Sass::Tree::CssImportNode.new(
|
392
|
+
Sass::Script::Value::String.quote(str), media.to_a), start_pos)
|
394
393
|
end
|
395
394
|
|
396
|
-
node(Sass::Tree::ImportNode.new(
|
395
|
+
node(Sass::Tree::ImportNode.new(str.strip), start_pos)
|
397
396
|
end
|
398
397
|
|
399
398
|
def use_css_import?; false; end
|
@@ -476,8 +475,7 @@ module Sass
|
|
476
475
|
alias_method :at_root_query, :query_expr
|
477
476
|
|
478
477
|
def charset_directive(start_pos)
|
479
|
-
|
480
|
-
name = @scanner[1] || @scanner[2]
|
478
|
+
name = expr!(:string)
|
481
479
|
ss
|
482
480
|
node(Sass::Tree::CharsetNode.new(name), start_pos)
|
483
481
|
end
|
@@ -534,6 +532,10 @@ module Sass
|
|
534
532
|
arr
|
535
533
|
end
|
536
534
|
|
535
|
+
def error_directive(start_pos)
|
536
|
+
node(Sass::Tree::ErrorNode.new(sass_script(:parse)), start_pos)
|
537
|
+
end
|
538
|
+
|
537
539
|
# http://www.w3.org/TR/css3-conditional/
|
538
540
|
def supports_directive(name, start_pos)
|
539
541
|
condition = expr!(:supports_condition)
|
@@ -629,10 +631,9 @@ module Sass
|
|
629
631
|
|
630
632
|
def ruleset
|
631
633
|
start_pos = source_position
|
632
|
-
rules
|
633
|
-
return unless rules
|
634
|
+
return unless (rules = almost_any_value)
|
634
635
|
block(node(
|
635
|
-
Sass::Tree::RuleNode.new(rules
|
636
|
+
Sass::Tree::RuleNode.new(rules, range(start_pos)), start_pos), :ruleset)
|
636
637
|
end
|
637
638
|
|
638
639
|
def block(node, context)
|
@@ -666,315 +667,171 @@ module Sass
|
|
666
667
|
child_or_array.has_children
|
667
668
|
end
|
668
669
|
|
669
|
-
#
|
670
|
-
#
|
671
|
-
#
|
672
|
-
#
|
673
|
-
#
|
674
|
-
#
|
670
|
+
# When parsing the contents of a ruleset, it can be difficult to tell
|
671
|
+
# declarations apart from nested rulesets. Since we don't thoroughly parse
|
672
|
+
# selectors until after resolving interpolation, we can share a bunch of
|
673
|
+
# the parsing of the two, but we need to disambiguate them first. We use
|
674
|
+
# the following criteria:
|
675
|
+
#
|
676
|
+
# * If the entity doesn't start with an identifier followed by a colon,
|
677
|
+
# it's a selector. There are some additional mostly-unimportant cases
|
678
|
+
# here to support various declaration hacks.
|
675
679
|
#
|
676
|
-
#
|
677
|
-
# (which is the most common case)
|
678
|
-
# and, if it doesn't, try it as a ruleset.
|
680
|
+
# * If the colon is followed by another colon, it's a selector.
|
679
681
|
#
|
680
|
-
#
|
681
|
-
#
|
682
|
-
#
|
683
|
-
#
|
682
|
+
# * Otherwise, if the colon is followed by anything other than
|
683
|
+
# interpolation or a character that's valid as the beginning of an
|
684
|
+
# identifier, it's a declaration.
|
685
|
+
#
|
686
|
+
# * If the colon is followed by interpolation or a valid identifier, try
|
687
|
+
# parsing it as a declaration value. If this fails, backtrack and parse
|
688
|
+
# it as a selector.
|
689
|
+
#
|
690
|
+
# * If the declaration value value valid but is followed by "{", backtrack
|
691
|
+
# and parse it as a selector anyway. This ensures that ".foo:bar {" is
|
692
|
+
# always parsed as a selector and never as a property with nested
|
693
|
+
# properties beneath it.
|
684
694
|
def declaration_or_ruleset
|
685
|
-
old_use_property_exception, @use_property_exception =
|
686
|
-
@use_property_exception, false
|
687
|
-
decl_err = catch_error do
|
688
|
-
decl = declaration
|
689
|
-
unless decl && decl.has_children
|
690
|
-
# We want an exception if it's not there,
|
691
|
-
# but we don't want to consume if it is
|
692
|
-
tok!(/[;}]/) unless tok?(/[;}]/)
|
693
|
-
end
|
694
|
-
return decl
|
695
|
-
end
|
696
|
-
|
697
|
-
ruleset_err = catch_error {return ruleset}
|
698
|
-
rethrow(@use_property_exception ? decl_err : ruleset_err)
|
699
|
-
ensure
|
700
|
-
@use_property_exception = old_use_property_exception
|
701
|
-
end
|
702
|
-
|
703
|
-
def selector_sequence
|
704
695
|
start_pos = source_position
|
705
|
-
|
706
|
-
return [sel], range(start_pos)
|
707
|
-
end
|
708
|
-
|
709
|
-
rules = []
|
710
|
-
v = selector
|
711
|
-
return unless v
|
712
|
-
rules.concat v
|
696
|
+
declaration = try_declaration
|
713
697
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
end
|
698
|
+
if declaration.nil?
|
699
|
+
return unless (selector = almost_any_value)
|
700
|
+
elsif declaration.is_a?(Array)
|
701
|
+
selector = declaration
|
702
|
+
else
|
703
|
+
# Declaration should be a PropNode.
|
704
|
+
return declaration
|
722
705
|
end
|
723
|
-
return rules, range(start_pos)
|
724
|
-
end
|
725
706
|
|
726
|
-
|
727
|
-
|
728
|
-
return unless sel
|
729
|
-
sel.to_a
|
730
|
-
end
|
731
|
-
|
732
|
-
def selector_comma_sequence
|
733
|
-
sel = _selector
|
734
|
-
return unless sel
|
735
|
-
selectors = [sel]
|
736
|
-
ws = ''
|
737
|
-
while tok(/,/)
|
738
|
-
ws << str {ss}
|
739
|
-
if (sel = _selector)
|
740
|
-
selectors << sel
|
741
|
-
if ws.include?("\n")
|
742
|
-
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
|
743
|
-
end
|
744
|
-
ws = ''
|
745
|
-
end
|
707
|
+
if (additional_selector = almost_any_value)
|
708
|
+
selector << additional_selector
|
746
709
|
end
|
747
|
-
Selector::CommaSequence.new(selectors)
|
748
|
-
end
|
749
710
|
|
750
|
-
|
751
|
-
|
752
|
-
val = combinator || simple_selector_sequence
|
753
|
-
return unless val
|
754
|
-
nl = str {ss}.include?("\n")
|
755
|
-
res = []
|
756
|
-
res << val
|
757
|
-
res << "\n" if nl
|
758
|
-
|
759
|
-
while (val = combinator || simple_selector_sequence)
|
760
|
-
res << val
|
761
|
-
res << "\n" if str {ss}.include?("\n")
|
762
|
-
end
|
763
|
-
Selector::Sequence.new(res.compact)
|
764
|
-
end
|
765
|
-
|
766
|
-
def combinator
|
767
|
-
tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
|
768
|
-
end
|
769
|
-
|
770
|
-
def reference_combinator
|
771
|
-
return unless tok(/\//)
|
772
|
-
res = ['/']
|
773
|
-
ns, name = expr!(:qualified_name)
|
774
|
-
res << ns << '|' if ns
|
775
|
-
res << name << tok!(/\//)
|
776
|
-
res = res.flatten
|
777
|
-
res = res.join '' if res.all? {|e| e.is_a?(String)}
|
778
|
-
res
|
711
|
+
block(node(
|
712
|
+
Sass::Tree::RuleNode.new(merge(selector), range(start_pos)), start_pos), :ruleset)
|
779
713
|
end
|
780
714
|
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
#
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
715
|
+
# Tries to parse a declaration, and returns the value parsed so far if it
|
716
|
+
# fails.
|
717
|
+
#
|
718
|
+
# This has three possible return types. It can return `nil`, indicating
|
719
|
+
# that parsing failed completely and the scanner hasn't moved forward at
|
720
|
+
# all. It can return an Array, indicating that parsing failed after
|
721
|
+
# consuming some text (possibly containing interpolation), which is
|
722
|
+
# returned. Or it can return a PropNode, indicating that parsing
|
723
|
+
# succeeded.
|
724
|
+
def try_declaration
|
725
|
+
# This allows the "*prop: val", ":prop: val", "#prop: val", and ".prop:
|
726
|
+
# val" hacks.
|
727
|
+
name_start_pos = source_position
|
728
|
+
if (s = tok(/[:\*\.]|\#(?!\{)/))
|
729
|
+
name = [s, str {ss}]
|
730
|
+
return name unless (ident = interp_ident)
|
731
|
+
name << ident
|
732
|
+
else
|
733
|
+
return unless (name = interp_ident)
|
734
|
+
name = Array(name)
|
797
735
|
end
|
798
736
|
|
799
|
-
|
800
|
-
|
801
|
-
if (sel = str? {simple_selector_sequence})
|
802
|
-
@scanner.pos = pos
|
803
|
-
@line = line
|
804
|
-
begin
|
805
|
-
# If we see "*E", don't force a throw because this could be the
|
806
|
-
# "*prop: val" hack.
|
807
|
-
expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
|
808
|
-
throw_error {expected('"{"')}
|
809
|
-
rescue Sass::SyntaxError => e
|
810
|
-
e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
|
811
|
-
raise e
|
812
|
-
end
|
737
|
+
if (comment = tok(COMMENT))
|
738
|
+
name << comment
|
813
739
|
end
|
740
|
+
name_end_pos = source_position
|
814
741
|
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
def class_selector
|
824
|
-
return unless tok(/\./)
|
825
|
-
@expected = "class name"
|
826
|
-
Selector::Class.new(merge(expr!(:interp_ident)))
|
827
|
-
end
|
828
|
-
|
829
|
-
def id_selector
|
830
|
-
return unless tok(/#(?!\{)/)
|
831
|
-
@expected = "id name"
|
832
|
-
Selector::Id.new(merge(expr!(:interp_name)))
|
833
|
-
end
|
834
|
-
|
835
|
-
def placeholder_selector
|
836
|
-
return unless tok(/%/)
|
837
|
-
@expected = "placeholder name"
|
838
|
-
Selector::Placeholder.new(merge(expr!(:interp_ident)))
|
839
|
-
end
|
840
|
-
|
841
|
-
def element_name
|
842
|
-
ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
|
843
|
-
return unless ns || name
|
742
|
+
mid = [str {ss}]
|
743
|
+
return name + mid unless tok(/:/)
|
744
|
+
mid << ':'
|
745
|
+
return name + mid + [':'] if tok(/:/)
|
746
|
+
mid << str {ss}
|
747
|
+
post_colon_whitespace = !mid.last.empty?
|
748
|
+
could_be_selector = !post_colon_whitespace && (tok?(IDENT_START) || tok?(INTERP_START))
|
844
749
|
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
750
|
+
value_start_pos = source_position
|
751
|
+
value = nil
|
752
|
+
error = catch_error do
|
753
|
+
value = value!
|
754
|
+
if tok?(/\{/)
|
755
|
+
# Properties that are ambiguous with selectors can't have additional
|
756
|
+
# properties nested beneath them.
|
757
|
+
tok!(/;/) if could_be_selector
|
758
|
+
elsif !tok?(/[;{}]/)
|
759
|
+
# We want an exception if there's no valid end-of-property character
|
760
|
+
# exists, but we don't want to consume it if it does.
|
761
|
+
tok!(/[;{}]/)
|
762
|
+
end
|
849
763
|
end
|
850
|
-
end
|
851
764
|
|
852
|
-
|
853
|
-
|
854
|
-
return unless name
|
855
|
-
return nil, name unless tok(/\|/)
|
765
|
+
if error
|
766
|
+
rethrow error unless could_be_selector
|
856
767
|
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
768
|
+
# If the value would be followed by a semicolon, it's definitely
|
769
|
+
# supposed to be a property, not a selector.
|
770
|
+
additional_selector = almost_any_value
|
771
|
+
rethrow error if tok?(/;/)
|
861
772
|
|
862
|
-
|
863
|
-
if (script = interpolation)
|
864
|
-
Selector::Interpolation.new(script)
|
773
|
+
return name + mid + (additional_selector || [])
|
865
774
|
end
|
866
|
-
end
|
867
775
|
|
868
|
-
|
869
|
-
return unless tok(/\[/)
|
870
|
-
ss
|
871
|
-
ns, name = attrib_name!
|
776
|
+
value_end_pos = source_position
|
872
777
|
ss
|
778
|
+
require_block = tok?(/\{/)
|
873
779
|
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
tok(SUFFIXMATCH) ||
|
879
|
-
tok(SUBSTRINGMATCH)
|
880
|
-
if op
|
881
|
-
@expected = "identifier or string"
|
882
|
-
ss
|
883
|
-
val = interp_ident || expr!(:interp_string)
|
884
|
-
ss
|
885
|
-
end
|
886
|
-
flags = interp_ident || interp_string
|
887
|
-
tok!(/\]/)
|
888
|
-
|
889
|
-
Selector::Attribute.new(merge(name), merge(ns), op, merge(val), merge(flags))
|
890
|
-
end
|
780
|
+
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
|
781
|
+
name_start_pos, value_end_pos)
|
782
|
+
node.name_source_range = range(name_start_pos, name_end_pos)
|
783
|
+
node.value_source_range = range(value_start_pos, value_end_pos)
|
891
784
|
|
892
|
-
|
893
|
-
|
894
|
-
# E, E|E
|
895
|
-
if tok(/\|(?!=)/)
|
896
|
-
ns = name_or_ns
|
897
|
-
name = interp_ident
|
898
|
-
else
|
899
|
-
name = name_or_ns
|
900
|
-
end
|
901
|
-
else
|
902
|
-
# *|E or |E
|
903
|
-
ns = [tok(/\*/) || ""]
|
904
|
-
tok!(/\|/)
|
905
|
-
name = expr!(:interp_ident)
|
906
|
-
end
|
907
|
-
return ns, name
|
785
|
+
return node unless require_block
|
786
|
+
nested_properties! node
|
908
787
|
end
|
909
788
|
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
end
|
926
|
-
|
927
|
-
def pseudo_arg
|
928
|
-
# In the CSS spec, every pseudo-class/element either takes a pseudo
|
929
|
-
# expression or a selector comma sequence as an argument. However, we
|
930
|
-
# don't want to have to know which takes which, so we handle both at
|
931
|
-
# once.
|
932
|
-
#
|
933
|
-
# However, there are some ambiguities between the two. For instance, "n"
|
934
|
-
# could start a pseudo expression like "n+1", or it could start a
|
935
|
-
# selector like "n|m". In order to handle this, we must regrettably
|
936
|
-
# backtrack.
|
937
|
-
expr, sel = nil, nil
|
938
|
-
pseudo_err = catch_error do
|
939
|
-
expr = pseudo_expr
|
940
|
-
next if tok?(/[,)]/)
|
941
|
-
expr = nil
|
942
|
-
expected '")"'
|
789
|
+
# This production is similar to the CSS [`<any-value>`][any-value]
|
790
|
+
# production, but as the name implies, not quite the same. It's meant to
|
791
|
+
# consume values that could be a selector, an expression, or a combination
|
792
|
+
# of both. It respects strings and comments and supports interpolation. It
|
793
|
+
# will consume up to "{", "}", ";", or "!".
|
794
|
+
#
|
795
|
+
# [any-value]: http://dev.w3.org/csswg/css-variables/#typedef-any-value
|
796
|
+
#
|
797
|
+
# Values consumed by this production will usually be parsed more
|
798
|
+
# thoroughly once interpolation has been resolved.
|
799
|
+
def almost_any_value
|
800
|
+
return unless (tok = almost_any_value_token)
|
801
|
+
sel = [tok]
|
802
|
+
while (tok = almost_any_value_token)
|
803
|
+
sel << tok
|
943
804
|
end
|
944
|
-
|
945
|
-
return expr if expr
|
946
|
-
sel_err = catch_error {sel = selector}
|
947
|
-
return sel if sel
|
948
|
-
rethrow pseudo_err if pseudo_err
|
949
|
-
rethrow sel_err if sel_err
|
950
|
-
nil
|
805
|
+
merge(sel)
|
951
806
|
end
|
952
807
|
|
953
|
-
def
|
954
|
-
tok(
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
808
|
+
def almost_any_value_token
|
809
|
+
tok(%r{
|
810
|
+
(
|
811
|
+
(?!url\()
|
812
|
+
[^"/\#!;\{\}] # "
|
813
|
+
|
|
814
|
+
/(?![/*])
|
815
|
+
|
|
816
|
+
\#(?!\{)
|
817
|
+
|
|
818
|
+
!(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed.
|
819
|
+
)+
|
820
|
+
}xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri ||
|
821
|
+
interpolation(:warn_for_color)
|
965
822
|
end
|
966
823
|
|
967
824
|
def declaration
|
968
|
-
# This allows the "*prop: val", ":prop: val",
|
825
|
+
# This allows the "*prop: val", ":prop: val", "#prop: val", and ".prop:
|
826
|
+
# val" hacks.
|
969
827
|
name_start_pos = source_position
|
970
828
|
if (s = tok(/[:\*\.]|\#(?!\{)/))
|
971
|
-
@use_property_exception = s !~ /[\.\#]/
|
972
829
|
name = [s, str {ss}, *expr!(:interp_ident)]
|
973
830
|
else
|
974
|
-
name = interp_ident
|
975
|
-
|
976
|
-
name = [name] if name.is_a?(String)
|
831
|
+
return unless (name = interp_ident)
|
832
|
+
name = Array(name)
|
977
833
|
end
|
834
|
+
|
978
835
|
if (comment = tok(COMMENT))
|
979
836
|
name << comment
|
980
837
|
end
|
@@ -982,7 +839,9 @@ module Sass
|
|
982
839
|
ss
|
983
840
|
|
984
841
|
tok!(/:/)
|
985
|
-
|
842
|
+
ss
|
843
|
+
value_start_pos = source_position
|
844
|
+
value = value!
|
986
845
|
value_end_pos = source_position
|
987
846
|
ss
|
988
847
|
require_block = tok?(/\{/)
|
@@ -993,19 +852,15 @@ module Sass
|
|
993
852
|
node.value_source_range = range(value_start_pos, value_end_pos)
|
994
853
|
|
995
854
|
return node unless require_block
|
996
|
-
nested_properties! node
|
855
|
+
nested_properties! node
|
997
856
|
end
|
998
857
|
|
999
858
|
def value!
|
1000
|
-
space = !str {ss}.empty?
|
1001
|
-
value_start_pos = source_position
|
1002
|
-
@use_property_exception ||= space || !tok?(IDENT)
|
1003
|
-
|
1004
859
|
if tok?(/\{/)
|
1005
860
|
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
|
1006
861
|
str.line = source_position.line
|
1007
862
|
str.source_range = range(source_position)
|
1008
|
-
return
|
863
|
+
return str
|
1009
864
|
end
|
1010
865
|
|
1011
866
|
start_pos = source_position
|
@@ -1018,18 +873,12 @@ module Sass
|
|
1018
873
|
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(val.strip))
|
1019
874
|
str.line = start_pos.line
|
1020
875
|
str.source_range = range(start_pos)
|
1021
|
-
return
|
876
|
+
return str
|
1022
877
|
end
|
1023
|
-
|
878
|
+
sass_script(:parse)
|
1024
879
|
end
|
1025
880
|
|
1026
|
-
def nested_properties!(node
|
1027
|
-
err(<<MESSAGE) unless space
|
1028
|
-
Invalid CSS: a space is required between a property and its definition
|
1029
|
-
when it has other properties nested beneath it.
|
1030
|
-
MESSAGE
|
1031
|
-
|
1032
|
-
@use_property_exception = true
|
881
|
+
def nested_properties!(node)
|
1033
882
|
@expected = 'expression (e.g. 1px, bold) or "{"'
|
1034
883
|
block(node, :property)
|
1035
884
|
end
|
@@ -1083,9 +932,14 @@ MESSAGE
|
|
1083
932
|
var
|
1084
933
|
end
|
1085
934
|
|
1086
|
-
def interpolation
|
935
|
+
def interpolation(warn_for_color = false)
|
1087
936
|
return unless tok(INTERP_START)
|
1088
|
-
sass_script(:parse_interpolated)
|
937
|
+
sass_script(:parse_interpolated, warn_for_color)
|
938
|
+
end
|
939
|
+
|
940
|
+
def string
|
941
|
+
return unless tok(STRING)
|
942
|
+
Sass::Script::Value::String.value(@scanner[1] || @scanner[2])
|
1089
943
|
end
|
1090
944
|
|
1091
945
|
def interp_string
|
@@ -1112,10 +966,10 @@ MESSAGE
|
|
1112
966
|
end
|
1113
967
|
|
1114
968
|
def interp_ident(start = IDENT)
|
1115
|
-
val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
|
969
|
+
val = tok(start) || interpolation(:warn_for_color) || tok(IDENT_HYPHEN_INTERP, true)
|
1116
970
|
return unless val
|
1117
971
|
res = [val]
|
1118
|
-
while (val = tok(NAME) || interpolation)
|
972
|
+
while (val = tok(NAME) || interpolation(:warn_for_color))
|
1119
973
|
res << val
|
1120
974
|
end
|
1121
975
|
res
|
@@ -1128,10 +982,6 @@ MESSAGE
|
|
1128
982
|
return [var] if var
|
1129
983
|
end
|
1130
984
|
|
1131
|
-
def interp_name
|
1132
|
-
interp_ident NAME
|
1133
|
-
end
|
1134
|
-
|
1135
985
|
def str
|
1136
986
|
@strs.push ""
|
1137
987
|
yield
|
@@ -1196,25 +1046,26 @@ MESSAGE
|
|
1196
1046
|
:media_expr => "media expression (e.g. (min-device-width: 800px))",
|
1197
1047
|
:at_root_query => "@at-root query (e.g. (without: media))",
|
1198
1048
|
:at_root_directive_list => '* or identifier',
|
1199
|
-
:
|
1049
|
+
:pseudo_args => "expression (e.g. fr, 2n+1)",
|
1200
1050
|
:interp_ident => "identifier",
|
1201
|
-
:interp_name => "identifier",
|
1202
1051
|
:qualified_name => "identifier",
|
1203
1052
|
:expr => "expression (e.g. 1px, bold)",
|
1204
|
-
:_selector => "selector",
|
1205
1053
|
:selector_comma_sequence => "selector",
|
1206
|
-
:
|
1054
|
+
:string => "string",
|
1207
1055
|
:import_arg => "file to import (string or url())",
|
1208
1056
|
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
|
1209
1057
|
:supports_condition => "@supports condition (e.g. (display: flexbox))",
|
1210
1058
|
:supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
|
1059
|
+
:a_n_plus_b => "An+B expression",
|
1060
|
+
:keyframes_selector_component => "from, to, or a percentage",
|
1061
|
+
:keyframes_selector => "keyframes selector (e.g. 10%)"
|
1211
1062
|
}
|
1212
1063
|
|
1213
1064
|
TOK_NAMES = Sass::Util.to_hash(Sass::SCSS::RX.constants.map do |c|
|
1214
1065
|
[Sass::SCSS::RX.const_get(c), c.downcase]
|
1215
1066
|
end).merge(
|
1216
1067
|
IDENT => "identifier",
|
1217
|
-
/[;}]/ => '";"',
|
1068
|
+
/[;{}]/ => '";"',
|
1218
1069
|
/\b(without|with)\b/ => '"with" or "without"'
|
1219
1070
|
)
|
1220
1071
|
|
@@ -1348,6 +1199,11 @@ MESSAGE
|
|
1348
1199
|
res
|
1349
1200
|
end
|
1350
1201
|
end
|
1202
|
+
|
1203
|
+
# Remove a vendor prefix from `str`.
|
1204
|
+
def deprefix(str)
|
1205
|
+
str.gsub(/^-[a-zA-Z0-9]+-/, '')
|
1206
|
+
end
|
1351
1207
|
end
|
1352
1208
|
end
|
1353
1209
|
end
|