sass 3.3.14 → 3.4.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|