sass 3.3.0.alpha.256 → 3.3.0.alpha.353
Sign up to get free protection for your applications and to get access to all the features.
- data/REVISION +1 -1
- data/Rakefile +21 -1
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass.rb +6 -3
- data/lib/sass/cache_stores/base.rb +1 -1
- data/lib/sass/cache_stores/chain.rb +2 -1
- data/lib/sass/cache_stores/filesystem.rb +2 -6
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +1 -0
- data/lib/sass/css.rb +6 -6
- data/lib/sass/engine.rb +60 -34
- data/lib/sass/environment.rb +3 -1
- data/lib/sass/error.rb +5 -5
- data/lib/sass/exec.rb +52 -25
- data/lib/sass/features.rb +0 -2
- data/lib/sass/importers/deprecated_path.rb +1 -1
- data/lib/sass/importers/filesystem.rb +8 -6
- data/lib/sass/logger/base.rb +3 -3
- data/lib/sass/logger/log_level.rb +4 -6
- data/lib/sass/media.rb +2 -2
- data/lib/sass/plugin.rb +4 -2
- data/lib/sass/plugin/compiler.rb +28 -15
- data/lib/sass/plugin/configuration.rb +15 -7
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +24 -8
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script.rb +2 -1
- data/lib/sass/script/css_lexer.rb +8 -3
- data/lib/sass/script/css_parser.rb +6 -2
- data/lib/sass/script/functions.rb +164 -109
- data/lib/sass/script/lexer.rb +30 -20
- data/lib/sass/script/parser.rb +66 -37
- data/lib/sass/script/tree/funcall.rb +23 -14
- data/lib/sass/script/tree/interpolation.rb +5 -1
- data/lib/sass/script/tree/list_literal.rb +5 -4
- data/lib/sass/script/tree/map_literal.rb +1 -1
- data/lib/sass/script/tree/node.rb +2 -2
- data/lib/sass/script/tree/operation.rb +2 -1
- data/lib/sass/script/tree/selector.rb +3 -2
- data/lib/sass/script/tree/string_interpolation.rb +2 -1
- data/lib/sass/script/tree/variable.rb +4 -3
- data/lib/sass/script/value/base.rb +12 -14
- data/lib/sass/script/value/color.rb +35 -16
- data/lib/sass/script/value/helpers.rb +146 -0
- data/lib/sass/script/value/list.rb +24 -5
- data/lib/sass/script/value/map.rb +1 -1
- data/lib/sass/script/value/null.rb +13 -3
- data/lib/sass/script/value/number.rb +44 -35
- data/lib/sass/script/value/string.rb +2 -2
- data/lib/sass/scss/css_parser.rb +2 -1
- data/lib/sass/scss/parser.rb +143 -93
- data/lib/sass/scss/rx.rb +4 -4
- data/lib/sass/scss/script_lexer.rb +1 -0
- data/lib/sass/scss/script_parser.rb +1 -0
- data/lib/sass/scss/static_parser.rb +5 -5
- data/lib/sass/selector.rb +5 -2
- data/lib/sass/selector/abstract_sequence.rb +1 -1
- data/lib/sass/selector/comma_sequence.rb +16 -14
- data/lib/sass/selector/sequence.rb +38 -24
- data/lib/sass/selector/simple.rb +4 -4
- data/lib/sass/selector/simple_sequence.rb +21 -11
- data/lib/sass/source/map.rb +7 -2
- data/lib/sass/source/range.rb +1 -1
- data/lib/sass/supports.rb +3 -3
- data/lib/sass/tree/debug_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +2 -1
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +3 -4
- data/lib/sass/tree/prop_node.rb +4 -2
- data/lib/sass/tree/rule_node.rb +5 -2
- data/lib/sass/tree/visitors/base.rb +6 -6
- data/lib/sass/tree/visitors/check_nesting.rb +12 -9
- data/lib/sass/tree/visitors/convert.rb +34 -28
- data/lib/sass/tree/visitors/cssize.rb +4 -3
- data/lib/sass/tree/visitors/deep_copy.rb +1 -0
- data/lib/sass/tree/visitors/perform.rb +31 -16
- data/lib/sass/tree/visitors/to_css.rb +34 -16
- data/lib/sass/util.rb +88 -37
- data/lib/sass/util/multibyte_string_scanner.rb +2 -0
- data/lib/sass/util/ordered_hash.rb +20 -18
- data/lib/sass/util/subset_map.rb +3 -2
- data/lib/sass/util/test.rb +0 -1
- data/lib/sass/version.rb +9 -5
- data/test/rubocop_extensions.rb +70 -0
- data/test/sass/functions_test.rb +20 -1
- data/test/sass/importer_test.rb +2 -1
- data/test/sass/script_test.rb +4 -0
- data/test/sass/source_map_test.rb +1 -1
- data/test/sass/util_test.rb +49 -0
- data/test/sass/value_helpers_test.rb +181 -0
- metadata +13 -9
@@ -32,8 +32,8 @@ module Sass::Script::Value
|
|
32
32
|
# @see Value#eq
|
33
33
|
def eq(other)
|
34
34
|
Sass::Script::Value::Bool.new(
|
35
|
-
other.is_a?(List) &&
|
36
|
-
|
35
|
+
other.is_a?(List) && value == other.value &&
|
36
|
+
separator == other.separator)
|
37
37
|
end
|
38
38
|
|
39
39
|
def hash
|
@@ -43,7 +43,9 @@ module Sass::Script::Value
|
|
43
43
|
# @see Value#to_s
|
44
44
|
def to_s(opts = {})
|
45
45
|
raise Sass::SyntaxError.new("() isn't a valid CSS value.") if value.empty?
|
46
|
-
|
46
|
+
value.
|
47
|
+
reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.
|
48
|
+
map {|e| e.to_s(opts)}.join(sep_str)
|
47
49
|
end
|
48
50
|
|
49
51
|
# @see Value#to_sass
|
@@ -87,12 +89,29 @@ module Sass::Script::Value
|
|
87
89
|
"(#{value.map {|e| e.inspect}.join(sep_str(nil))})"
|
88
90
|
end
|
89
91
|
|
92
|
+
# Asserts an index is within the list.
|
93
|
+
#
|
94
|
+
# @private
|
95
|
+
#
|
96
|
+
# @param list [Sass::Script::Value::List] The list for which the index should be checked.
|
97
|
+
# @param n [Sass::Script::Value::Number] The index being checked.
|
98
|
+
def self.assert_valid_index(list, n)
|
99
|
+
if !n.int? || n.to_i == 0
|
100
|
+
raise ArgumentError.new("List index #{n} must be a non-zero integer")
|
101
|
+
elsif list.to_a.size == 0
|
102
|
+
raise ArgumentError.new("List index is #{n} but list has no items")
|
103
|
+
elsif n.to_i.abs > (size = list.to_a.size)
|
104
|
+
raise ArgumentError.new(
|
105
|
+
"List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
90
109
|
private
|
91
110
|
|
92
|
-
def sep_str(opts =
|
111
|
+
def sep_str(opts = options)
|
93
112
|
return ' ' if separator == :space
|
94
113
|
return ',' if opts && opts[:style] == :compressed
|
95
|
-
|
114
|
+
', '
|
96
115
|
end
|
97
116
|
end
|
98
117
|
end
|
@@ -1,9 +1,19 @@
|
|
1
1
|
module Sass::Script::Value
|
2
2
|
# A SassScript object representing a null value.
|
3
3
|
class Null < Base
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
# The null value in SassScript.
|
6
|
+
#
|
7
|
+
# This is assigned before new is overridden below so that we use the default implementation.
|
8
|
+
NULL = new(nil)
|
9
|
+
|
10
|
+
# We override object creation so that users of the core API
|
11
|
+
# will not need to know that null is a specific constant.
|
12
|
+
#
|
13
|
+
# @private
|
14
|
+
# @return [Null] the {NULL} constant.
|
15
|
+
def self.new
|
16
|
+
NULL
|
7
17
|
end
|
8
18
|
|
9
19
|
# @return [Boolean] `false` (the Ruby boolean value)
|
@@ -15,7 +15,7 @@ module Sass::Script::Value
|
|
15
15
|
|
16
16
|
# A list of units in the numerator of the number.
|
17
17
|
# For example, `1px*em/in*cm` would return `["px", "em"]`
|
18
|
-
# @return [Array<String>]
|
18
|
+
# @return [Array<String>]
|
19
19
|
attr_reader :numerator_units
|
20
20
|
|
21
21
|
# A list of units in the denominator of the number.
|
@@ -161,8 +161,8 @@ module Sass::Script::Value
|
|
161
161
|
def div(other)
|
162
162
|
if other.is_a? Number
|
163
163
|
res = operate(other, :/)
|
164
|
-
if
|
165
|
-
res.original = "#{
|
164
|
+
if original && other.original
|
165
|
+
res.original = "#{original}/#{other.original}"
|
166
166
|
end
|
167
167
|
res
|
168
168
|
else
|
@@ -179,7 +179,8 @@ module Sass::Script::Value
|
|
179
179
|
def mod(other)
|
180
180
|
if other.is_a?(Number)
|
181
181
|
unless other.unitless?
|
182
|
-
raise Sass::UnitConversionError.new(
|
182
|
+
raise Sass::UnitConversionError.new(
|
183
|
+
"Cannot modulo by a number with units: #{other.inspect}.")
|
183
184
|
end
|
184
185
|
operate(other, :%)
|
185
186
|
else
|
@@ -214,8 +215,8 @@ module Sass::Script::Value
|
|
214
215
|
# Hash-equality must be transitive, so it just compares the exact value,
|
215
216
|
# numerator units, and denominator units.
|
216
217
|
def eql?(other)
|
217
|
-
|
218
|
-
|
218
|
+
value == other.value && numerator_units == other.numerator_units &&
|
219
|
+
denominator_units == other.denominator_units
|
219
220
|
end
|
220
221
|
|
221
222
|
# The SassScript `>` operation.
|
@@ -283,7 +284,7 @@ module Sass::Script::Value
|
|
283
284
|
# @raise [Sass::SyntaxError] if the number isn't an integer
|
284
285
|
def to_i
|
285
286
|
super unless int?
|
286
|
-
|
287
|
+
value
|
287
288
|
end
|
288
289
|
|
289
290
|
# @return [Boolean] Whether or not this number is an integer.
|
@@ -303,7 +304,8 @@ module Sass::Script::Value
|
|
303
304
|
# number.is_unit?("px") => true
|
304
305
|
# number.is_unit?(nil) => false
|
305
306
|
#
|
306
|
-
# @param unit [::String, nil] The unit the number should have or nil if the number
|
307
|
+
# @param unit [::String, nil] The unit the number should have or nil if the number
|
308
|
+
# should be unitless.
|
307
309
|
# @see Number#unitless? The unitless? method may be more readable.
|
308
310
|
def is_unit?(unit)
|
309
311
|
if unit
|
@@ -337,9 +339,9 @@ module Sass::Script::Value
|
|
337
339
|
# current units
|
338
340
|
def coerce(num_units, den_units)
|
339
341
|
Number.new(if unitless?
|
340
|
-
|
342
|
+
value
|
341
343
|
else
|
342
|
-
|
344
|
+
value * coercion_factor(@numerator_units, num_units) /
|
343
345
|
coercion_factor(@denominator_units, den_units)
|
344
346
|
end, num_units, den_units)
|
345
347
|
end
|
@@ -347,12 +349,10 @@ module Sass::Script::Value
|
|
347
349
|
# @param other [Number] A number to decide if it can be compared with this number.
|
348
350
|
# @return [Boolean] Whether or not this number can be compared with the other.
|
349
351
|
def comparable_to?(other)
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
false
|
355
|
-
end
|
352
|
+
operate(other, :+)
|
353
|
+
true
|
354
|
+
rescue Sass::UnitConversionError
|
355
|
+
false
|
356
356
|
end
|
357
357
|
|
358
358
|
# Returns a human readable representation of the units in this number.
|
@@ -377,7 +377,7 @@ module Sass::Script::Value
|
|
377
377
|
elsif num % 1 == 0.0
|
378
378
|
num.to_i
|
379
379
|
else
|
380
|
-
((num *
|
380
|
+
((num * precision_factor).round / precision_factor).to_f
|
381
381
|
end
|
382
382
|
end
|
383
383
|
|
@@ -393,7 +393,7 @@ module Sass::Script::Value
|
|
393
393
|
end
|
394
394
|
end
|
395
395
|
# avoid integer division
|
396
|
-
value =
|
396
|
+
value = :/ == operation ? this.value.to_f : this.value
|
397
397
|
result = value.send(operation, other.value)
|
398
398
|
|
399
399
|
if result.is_a?(Numeric)
|
@@ -408,29 +408,33 @@ module Sass::Script::Value
|
|
408
408
|
from_units, to_units = sans_common_units(from_units, to_units)
|
409
409
|
|
410
410
|
if from_units.size != to_units.size || !convertable?(from_units | to_units)
|
411
|
-
raise Sass::UnitConversionError.new(
|
411
|
+
raise Sass::UnitConversionError.new(
|
412
|
+
"Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
|
412
413
|
end
|
413
414
|
|
414
|
-
from_units.zip(to_units).inject(1) {|m,p| m * conversion_factor(p[0], p[1])
|
415
|
+
from_units.zip(to_units).inject(1) {|m, p| m * conversion_factor(p[0], p[1])}
|
415
416
|
end
|
416
417
|
|
417
418
|
def compute_units(this, other, operation)
|
418
419
|
case operation
|
419
420
|
when :*
|
420
|
-
[this.numerator_units + other.numerator_units,
|
421
|
+
[this.numerator_units + other.numerator_units,
|
422
|
+
this.denominator_units + other.denominator_units]
|
421
423
|
when :/
|
422
|
-
[this.numerator_units + other.denominator_units,
|
423
|
-
|
424
|
+
[this.numerator_units + other.denominator_units,
|
425
|
+
this.denominator_units + other.numerator_units]
|
426
|
+
else
|
424
427
|
[this.numerator_units, this.denominator_units]
|
425
428
|
end
|
426
429
|
end
|
427
430
|
|
428
431
|
def normalize!
|
429
432
|
return if unitless?
|
430
|
-
@numerator_units, @denominator_units =
|
433
|
+
@numerator_units, @denominator_units =
|
434
|
+
sans_common_units(@numerator_units, @denominator_units)
|
431
435
|
|
432
436
|
@denominator_units.each_with_index do |d, i|
|
433
|
-
if convertable?(d) && (u = @numerator_units.
|
437
|
+
if convertable?(d) && (u = @numerator_units.find(&method(:convertable?)))
|
434
438
|
@value /= conversion_factor(d, u)
|
435
439
|
@denominator_units.delete_at(i)
|
436
440
|
@numerator_units.delete_at(@numerator_units.index(u))
|
@@ -439,13 +443,15 @@ module Sass::Script::Value
|
|
439
443
|
end
|
440
444
|
|
441
445
|
# A hash of unit names to their index in the conversion table
|
442
|
-
CONVERTABLE_UNITS =
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
[
|
447
|
-
[
|
448
|
-
[
|
446
|
+
CONVERTABLE_UNITS = %w(in cm pc mm pt px).inject({}) {|m, v| m[v] = m.size; m}
|
447
|
+
|
448
|
+
# in cm pc mm pt px
|
449
|
+
CONVERSION_TABLE = [[1, 2.54, 6, 25.4, 72 , 96], # in
|
450
|
+
[nil, 1, 2.36220473, 10, 28.3464567, 37.795275591], # cm
|
451
|
+
[nil, nil, 1, 4.23333333, 12 , 16], # pc
|
452
|
+
[nil, nil, nil, 1, 2.83464567, 3.7795275591], # mm
|
453
|
+
[nil, nil, nil, nil, 1 , 1.3333333333], # pt
|
454
|
+
[nil, nil, nil, nil, nil , 1]] # px
|
449
455
|
|
450
456
|
def conversion_factor(from_unit, to_unit)
|
451
457
|
res = CONVERSION_TABLE[CONVERTABLE_UNITS[from_unit]][CONVERTABLE_UNITS[to_unit]]
|
@@ -460,11 +466,14 @@ module Sass::Script::Value
|
|
460
466
|
def sans_common_units(units1, units2)
|
461
467
|
units2 = units2.dup
|
462
468
|
# Can't just use -, because we want px*px to coerce properly to px*mm
|
463
|
-
|
464
|
-
|
469
|
+
units1 = units1.map do |u|
|
470
|
+
j = units2.index(u)
|
471
|
+
next u unless j
|
465
472
|
units2.delete_at(j)
|
466
473
|
nil
|
467
|
-
end
|
474
|
+
end
|
475
|
+
units1.compact!
|
476
|
+
return units1, units2
|
468
477
|
end
|
469
478
|
end
|
470
479
|
end
|
@@ -25,7 +25,7 @@ module Sass::Script::Value
|
|
25
25
|
# @see Value#plus
|
26
26
|
def plus(other)
|
27
27
|
other_str = other.is_a?(Sass::Script::Value::String) ? other.value : other.to_s
|
28
|
-
Sass::Script::Value::String.new(
|
28
|
+
Sass::Script::Value::String.new(value + other_str, type)
|
29
29
|
end
|
30
30
|
|
31
31
|
# @see Value#to_s
|
@@ -38,7 +38,7 @@ module Sass::Script::Value
|
|
38
38
|
return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
|
39
39
|
return "\"#{value}\"" unless value.include?('"')
|
40
40
|
return "'#{value}'" unless value.include?("'")
|
41
|
-
"\"#{value.gsub('"', "\\\"")}\"" #'
|
41
|
+
"\"#{value.gsub('"', "\\\"")}\"" # '
|
42
42
|
end
|
43
43
|
|
44
44
|
# @see Value#to_sass
|
data/lib/sass/scss/css_parser.rb
CHANGED
@@ -7,6 +7,7 @@ module Sass
|
|
7
7
|
# parent references, nested selectors, and so forth.
|
8
8
|
# It does support all the same CSS hacks as the SCSS parser, though.
|
9
9
|
class CssParser < StaticParser
|
10
|
+
|
10
11
|
private
|
11
12
|
|
12
13
|
def placeholder_selector; nil; end
|
@@ -26,7 +27,7 @@ module Sass
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def nested_properties!(node, space)
|
29
|
-
expected('expression (e.g. 1px, bold)')
|
30
|
+
expected('expression (e.g. 1px, bold)')
|
30
31
|
end
|
31
32
|
|
32
33
|
@sass_script_parser = Class.new(Sass::Script::CssParser)
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -21,7 +21,10 @@ module Sass
|
|
21
21
|
# @param offset [Fixnum] The 1-based character (not byte) offset in the line on
|
22
22
|
# which the source string starts. Used for error reporting and sourcemap
|
23
23
|
# building.
|
24
|
+
# @comment
|
25
|
+
# rubocop:disable ParameterLists
|
24
26
|
def initialize(str, filename, importer, line = 1, offset = 1)
|
27
|
+
# rubocop:enable ParameterLists
|
25
28
|
@template = str
|
26
29
|
@filename = filename
|
27
30
|
@importer = importer
|
@@ -84,7 +87,7 @@ module Sass
|
|
84
87
|
Sass::Source::Position.new(@line, @offset)
|
85
88
|
end
|
86
89
|
|
87
|
-
def range(start_pos, end_pos=source_position)
|
90
|
+
def range(start_pos, end_pos = source_position)
|
88
91
|
Sass::Source::Range.new(start_pos, end_pos, @filename, @importer)
|
89
92
|
end
|
90
93
|
|
@@ -132,21 +135,28 @@ module Sass
|
|
132
135
|
end
|
133
136
|
|
134
137
|
def process_comment(text, node)
|
135
|
-
silent = text =~
|
138
|
+
silent = text =~ %r{^//}
|
136
139
|
loud = !silent && text =~ %r{^/[/*]!}
|
137
140
|
line = @line - text.count("\n")
|
138
141
|
|
139
142
|
if silent
|
140
|
-
value = [text.sub(
|
143
|
+
value = [text.sub(%r{^\s*//}, '/*').gsub(%r{^\s*//}, ' *') + ' */']
|
141
144
|
else
|
142
|
-
value = Sass::Engine.parse_interp(
|
145
|
+
value = Sass::Engine.parse_interp(
|
146
|
+
text, line, @scanner.pos - text.size, :filename => @filename)
|
143
147
|
value.unshift(@scanner.
|
144
148
|
string[0...@scanner.pos].
|
145
149
|
reverse[/.*?\*\/(.*?)($|\Z)/, 1].
|
146
150
|
reverse.gsub(/[^\s]/, ' '))
|
147
151
|
end
|
148
152
|
|
149
|
-
type = if silent
|
153
|
+
type = if silent
|
154
|
+
:silent
|
155
|
+
elsif loud
|
156
|
+
:loud
|
157
|
+
else
|
158
|
+
:normal
|
159
|
+
end
|
150
160
|
comment = Sass::Tree::CommentNode.new(value, type)
|
151
161
|
comment.line = line
|
152
162
|
node << comment
|
@@ -164,9 +174,9 @@ module Sass
|
|
164
174
|
name = tok!(IDENT)
|
165
175
|
ss
|
166
176
|
|
167
|
-
if dir = special_directive(name, start_pos)
|
177
|
+
if (dir = special_directive(name, start_pos))
|
168
178
|
return dir
|
169
|
-
elsif dir = prefixed_directive(name, start_pos)
|
179
|
+
elsif (dir = prefixed_directive(name, start_pos))
|
170
180
|
return dir
|
171
181
|
end
|
172
182
|
|
@@ -211,7 +221,8 @@ module Sass
|
|
211
221
|
name = tok! IDENT
|
212
222
|
args, keywords, splat, kwarg_splat = sass_script(:parse_mixin_include_arglist)
|
213
223
|
ss
|
214
|
-
include_node = node(
|
224
|
+
include_node = node(
|
225
|
+
Sass::Tree::MixinNode.new(name, args, keywords, splat, kwarg_splat), start_pos)
|
215
226
|
if tok?(/\{/)
|
216
227
|
include_node.has_children = true
|
217
228
|
block(include_node, :directive)
|
@@ -344,7 +355,7 @@ module Sass
|
|
344
355
|
ss
|
345
356
|
end
|
346
357
|
|
347
|
-
|
358
|
+
values
|
348
359
|
end
|
349
360
|
|
350
361
|
def import_arg
|
@@ -361,7 +372,7 @@ module Sass
|
|
361
372
|
ss
|
362
373
|
|
363
374
|
media = media_query_list
|
364
|
-
if path =~
|
375
|
+
if path =~ %r{^(https?:)?//} || media || use_css_import?
|
365
376
|
return node(Sass::Tree::CssImportNode.new(str, media.to_a), start_pos)
|
366
377
|
end
|
367
378
|
|
@@ -376,7 +387,8 @@ module Sass
|
|
376
387
|
|
377
388
|
# http://www.w3.org/TR/css3-mediaqueries/#syntax
|
378
389
|
def media_query_list
|
379
|
-
|
390
|
+
query = media_query
|
391
|
+
return unless query
|
380
392
|
queries = [query]
|
381
393
|
|
382
394
|
ss
|
@@ -389,7 +401,7 @@ module Sass
|
|
389
401
|
end
|
390
402
|
|
391
403
|
def media_query
|
392
|
-
if ident1 = interp_ident
|
404
|
+
if (ident1 = interp_ident)
|
393
405
|
ss
|
394
406
|
ident2 = interp_ident
|
395
407
|
ss
|
@@ -409,7 +421,8 @@ module Sass
|
|
409
421
|
if query
|
410
422
|
expr = expr!(:media_expr)
|
411
423
|
else
|
412
|
-
|
424
|
+
expr = media_expr
|
425
|
+
return unless expr
|
413
426
|
end
|
414
427
|
query ||= Sass::Media::Query.new([], [], [])
|
415
428
|
query.expressions << expr
|
@@ -423,7 +436,8 @@ module Sass
|
|
423
436
|
end
|
424
437
|
|
425
438
|
def media_expr
|
426
|
-
interp = interpolation
|
439
|
+
interp = interpolation
|
440
|
+
return interp if interp
|
427
441
|
return unless tok(/\(/)
|
428
442
|
res = ['(']
|
429
443
|
ss
|
@@ -457,25 +471,30 @@ module Sass
|
|
457
471
|
def _moz_document_directive(start_pos)
|
458
472
|
res = ["@-moz-document "]
|
459
473
|
loop do
|
460
|
-
res << str{ss} << expr!(:moz_document_function)
|
461
|
-
|
462
|
-
|
474
|
+
res << str {ss} << expr!(:moz_document_function)
|
475
|
+
if (c = tok(/,/))
|
476
|
+
res << c
|
477
|
+
else
|
478
|
+
break
|
479
|
+
end
|
463
480
|
end
|
464
481
|
directive_body(res.flatten, start_pos)
|
465
482
|
end
|
466
483
|
|
467
484
|
def moz_document_function
|
468
|
-
|
485
|
+
val = interp_uri || _interp_string(:url_prefix) ||
|
469
486
|
_interp_string(:domain) || function(!:allow_var) || interpolation
|
487
|
+
return unless val
|
470
488
|
ss
|
471
489
|
val
|
472
490
|
end
|
473
491
|
|
474
492
|
def at_root_directive(start_pos)
|
475
493
|
at_root_node = node(Sass::Tree::AtRootNode.new, start_pos)
|
476
|
-
|
494
|
+
rule_node = ruleset
|
495
|
+
return block(at_root_node, :stylesheet) unless rule_node
|
477
496
|
at_root_node << rule_node
|
478
|
-
|
497
|
+
at_root_node
|
479
498
|
end
|
480
499
|
|
481
500
|
# http://www.w3.org/TR/css3-conditional/
|
@@ -502,20 +521,21 @@ module Sass
|
|
502
521
|
end
|
503
522
|
|
504
523
|
def supports_operator
|
505
|
-
|
506
|
-
return
|
507
|
-
|
524
|
+
cond = supports_condition_in_parens
|
525
|
+
return unless cond
|
526
|
+
while (op = tok(/and|or/i))
|
508
527
|
ss
|
509
528
|
cond = Sass::Supports::Operator.new(
|
510
529
|
cond, expr!(:supports_condition_in_parens), op)
|
511
|
-
end
|
530
|
+
end
|
512
531
|
cond
|
513
532
|
end
|
514
533
|
|
515
534
|
def supports_condition_in_parens
|
516
|
-
interp = supports_interpolation
|
535
|
+
interp = supports_interpolation
|
536
|
+
return interp if interp
|
517
537
|
return unless tok(/\(/); ss
|
518
|
-
if cond = supports_condition
|
538
|
+
if (cond = supports_condition)
|
519
539
|
tok!(/\)/); ss
|
520
540
|
cond
|
521
541
|
else
|
@@ -533,7 +553,8 @@ module Sass
|
|
533
553
|
end
|
534
554
|
|
535
555
|
def supports_interpolation
|
536
|
-
|
556
|
+
interp = interpolation
|
557
|
+
return unless interp
|
537
558
|
ss
|
538
559
|
Sass::Supports::Interpolation.new(interp)
|
539
560
|
end
|
@@ -562,7 +583,8 @@ module Sass
|
|
562
583
|
start_pos = source_position
|
563
584
|
rules, source_range = selector_sequence
|
564
585
|
return unless rules
|
565
|
-
block(node(
|
586
|
+
block(node(
|
587
|
+
Sass::Tree::RuleNode.new(rules.flatten.compact, source_range), start_pos), :ruleset)
|
566
588
|
end
|
567
589
|
|
568
590
|
def block(node, context)
|
@@ -593,7 +615,7 @@ module Sass
|
|
593
615
|
def has_children?(child_or_array)
|
594
616
|
return false unless child_or_array
|
595
617
|
return child_or_array.last.has_children if child_or_array.is_a?(Array)
|
596
|
-
|
618
|
+
child_or_array.has_children
|
597
619
|
end
|
598
620
|
|
599
621
|
# This is a nasty hack, and the only place in the parser
|
@@ -632,18 +654,19 @@ module Sass
|
|
632
654
|
|
633
655
|
def selector_sequence
|
634
656
|
start_pos = source_position
|
635
|
-
if sel = tok(STATIC_SELECTOR, true)
|
657
|
+
if (sel = tok(STATIC_SELECTOR, true))
|
636
658
|
return [sel], range(start_pos)
|
637
659
|
end
|
638
660
|
|
639
661
|
rules = []
|
640
|
-
|
662
|
+
v = selector
|
663
|
+
return unless v
|
641
664
|
rules.concat v
|
642
665
|
|
643
666
|
ws = ''
|
644
667
|
while tok(/,/)
|
645
668
|
ws << str {ss}
|
646
|
-
if v = selector
|
669
|
+
if (v = selector)
|
647
670
|
rules << ',' << ws
|
648
671
|
rules.concat v
|
649
672
|
ws = ''
|
@@ -653,19 +676,23 @@ module Sass
|
|
653
676
|
end
|
654
677
|
|
655
678
|
def selector
|
656
|
-
|
679
|
+
sel = _selector
|
680
|
+
return unless sel
|
657
681
|
sel.to_a
|
658
682
|
end
|
659
683
|
|
660
684
|
def selector_comma_sequence
|
661
|
-
|
685
|
+
sel = _selector
|
686
|
+
return unless sel
|
662
687
|
selectors = [sel]
|
663
688
|
ws = ''
|
664
689
|
while tok(/,/)
|
665
|
-
ws << str{ss}
|
666
|
-
if sel = _selector
|
690
|
+
ws << str {ss}
|
691
|
+
if (sel = _selector)
|
667
692
|
selectors << sel
|
668
|
-
|
693
|
+
if ws.include?("\n")
|
694
|
+
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
|
695
|
+
end
|
669
696
|
ws = ''
|
670
697
|
end
|
671
698
|
end
|
@@ -674,15 +701,16 @@ module Sass
|
|
674
701
|
|
675
702
|
def _selector
|
676
703
|
# The combinator here allows the "> E" hack
|
677
|
-
|
678
|
-
|
704
|
+
val = combinator || simple_selector_sequence
|
705
|
+
return unless val
|
706
|
+
nl = str {ss}.include?("\n")
|
679
707
|
res = []
|
680
708
|
res << val
|
681
709
|
res << "\n" if nl
|
682
710
|
|
683
|
-
while val = combinator || simple_selector_sequence
|
711
|
+
while (val = combinator || simple_selector_sequence)
|
684
712
|
res << val
|
685
|
-
res << "\n" if str{ss}.include?("\n")
|
713
|
+
res << "\n" if str {ss}.include?("\n")
|
686
714
|
end
|
687
715
|
Selector::Sequence.new(res.compact)
|
688
716
|
end
|
@@ -707,21 +735,22 @@ module Sass
|
|
707
735
|
# http://www.w3.org/TR/css3-animations/#keyframes-
|
708
736
|
|
709
737
|
start_pos = source_position
|
710
|
-
|
738
|
+
e = element_name || id_selector ||
|
711
739
|
class_selector || placeholder_selector || attrib || pseudo ||
|
712
740
|
parent_selector || interpolation_selector
|
741
|
+
return expr(!:allow_var) unless e
|
713
742
|
res = [e]
|
714
743
|
|
715
744
|
# The tok(/\*/) allows the "E*" hack
|
716
|
-
while v = id_selector || class_selector || placeholder_selector ||
|
717
|
-
|
718
|
-
|
745
|
+
while (v = id_selector || class_selector || placeholder_selector ||
|
746
|
+
attrib || pseudo || interpolation_selector ||
|
747
|
+
(tok(/\*/) && Selector::Universal.new(nil)))
|
719
748
|
res << v
|
720
749
|
end
|
721
750
|
|
722
751
|
pos = @scanner.pos
|
723
752
|
line = @line
|
724
|
-
if sel = str? {simple_selector_sequence}
|
753
|
+
if (sel = str? {simple_selector_sequence})
|
725
754
|
@scanner.pos = pos
|
726
755
|
@line = line
|
727
756
|
begin
|
@@ -772,8 +801,9 @@ module Sass
|
|
772
801
|
end
|
773
802
|
end
|
774
803
|
|
775
|
-
def qualified_name(allow_star_name=false)
|
776
|
-
|
804
|
+
def qualified_name(allow_star_name = false)
|
805
|
+
name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
|
806
|
+
return unless name
|
777
807
|
return nil, name unless tok(/\|/)
|
778
808
|
|
779
809
|
return name, expr!(:interp_ident) unless allow_star_name
|
@@ -782,8 +812,9 @@ module Sass
|
|
782
812
|
end
|
783
813
|
|
784
814
|
def interpolation_selector
|
785
|
-
|
786
|
-
|
815
|
+
if (script = interpolation)
|
816
|
+
Selector::Interpolation.new(script)
|
817
|
+
end
|
787
818
|
end
|
788
819
|
|
789
820
|
def attrib
|
@@ -792,12 +823,13 @@ module Sass
|
|
792
823
|
ns, name = attrib_name!
|
793
824
|
ss
|
794
825
|
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
826
|
+
op = tok(/=/) ||
|
827
|
+
tok(INCLUDES) ||
|
828
|
+
tok(DASHMATCH) ||
|
829
|
+
tok(PREFIXMATCH) ||
|
830
|
+
tok(SUFFIXMATCH) ||
|
831
|
+
tok(SUBSTRINGMATCH)
|
832
|
+
if op
|
801
833
|
@expected = "identifier or string"
|
802
834
|
ss
|
803
835
|
val = interp_ident || expr!(:interp_string)
|
@@ -810,7 +842,7 @@ module Sass
|
|
810
842
|
end
|
811
843
|
|
812
844
|
def attrib_name!
|
813
|
-
if name_or_ns = interp_ident
|
845
|
+
if (name_or_ns = interp_ident)
|
814
846
|
# E, E|E
|
815
847
|
if tok(/\|(?!=)/)
|
816
848
|
ns = name_or_ns
|
@@ -828,14 +860,15 @@ module Sass
|
|
828
860
|
end
|
829
861
|
|
830
862
|
def pseudo
|
831
|
-
|
863
|
+
s = tok(/::?/)
|
864
|
+
return unless s
|
832
865
|
@expected = "pseudoclass or pseudoelement"
|
833
866
|
name = expr!(:interp_ident)
|
834
867
|
if tok(/\(/)
|
835
868
|
ss
|
836
869
|
arg = expr!(:pseudo_arg)
|
837
870
|
while tok(/,/)
|
838
|
-
arg << ',' << str{ss}
|
871
|
+
arg << ',' << str {ss}
|
839
872
|
arg.concat expr!(:pseudo_arg)
|
840
873
|
end
|
841
874
|
tok!(/\)/)
|
@@ -866,16 +899,19 @@ module Sass
|
|
866
899
|
return sel if sel
|
867
900
|
rethrow pseudo_err if pseudo_err
|
868
901
|
rethrow sel_err if sel_err
|
869
|
-
|
902
|
+
nil
|
903
|
+
end
|
904
|
+
|
905
|
+
def pseudo_expr_token
|
906
|
+
tok(PLUS) || tok(/[-*]/) || tok(NUMBER) || interp_string || tok(IDENT) || interpolation
|
870
907
|
end
|
871
908
|
|
872
909
|
def pseudo_expr
|
873
|
-
|
874
|
-
|
875
|
-
res = [e, str{ss}]
|
876
|
-
while e =
|
877
|
-
|
878
|
-
res << e << str{ss}
|
910
|
+
e = pseudo_expr_token
|
911
|
+
return unless e
|
912
|
+
res = [e, str {ss}]
|
913
|
+
while (e = pseudo_expr_token)
|
914
|
+
res << e << str {ss}
|
879
915
|
end
|
880
916
|
res
|
881
917
|
end
|
@@ -883,14 +919,15 @@ module Sass
|
|
883
919
|
def declaration
|
884
920
|
# This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
|
885
921
|
name_start_pos = source_position
|
886
|
-
if s = tok(/[:\*\.]|\#(?!\{)/)
|
922
|
+
if (s = tok(/[:\*\.]|\#(?!\{)/))
|
887
923
|
@use_property_exception = s !~ /[\.\#]/
|
888
|
-
name = [s, str{ss}, *expr!(:interp_ident)]
|
924
|
+
name = [s, str {ss}, *expr!(:interp_ident)]
|
889
925
|
else
|
890
|
-
|
926
|
+
name = interp_ident
|
927
|
+
return unless name
|
891
928
|
name = [name] if name.is_a?(String)
|
892
929
|
end
|
893
|
-
if comment = tok(COMMENT)
|
930
|
+
if (comment = tok(COMMENT))
|
894
931
|
name << comment
|
895
932
|
end
|
896
933
|
name_end_pos = source_position
|
@@ -902,7 +939,8 @@ module Sass
|
|
902
939
|
ss
|
903
940
|
require_block = tok?(/\{/)
|
904
941
|
|
905
|
-
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
|
942
|
+
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
|
943
|
+
name_start_pos, value_end_pos)
|
906
944
|
node.name_source_range = range(name_start_pos, name_end_pos)
|
907
945
|
node.value_source_range = range(value_start_pos, value_end_pos)
|
908
946
|
|
@@ -928,7 +966,7 @@ module Sass
|
|
928
966
|
# we don't parse it at all, and instead return a plain old string
|
929
967
|
# containing the value.
|
930
968
|
# This results in a dramatic speed increase.
|
931
|
-
if val = tok(STATIC_VALUE, true)
|
969
|
+
if (val = tok(STATIC_VALUE, true))
|
932
970
|
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(val.strip))
|
933
971
|
str.line = start_pos.line
|
934
972
|
str.source_range = range(start_pos)
|
@@ -949,18 +987,19 @@ MESSAGE
|
|
949
987
|
end
|
950
988
|
|
951
989
|
def expr(allow_var = true)
|
952
|
-
|
953
|
-
|
990
|
+
t = term(allow_var)
|
991
|
+
return unless t
|
992
|
+
res = [t, str {ss}]
|
954
993
|
|
955
994
|
while (o = operator) && (t = term(allow_var))
|
956
|
-
res << o << t << str{ss}
|
995
|
+
res << o << t << str {ss}
|
957
996
|
end
|
958
997
|
|
959
998
|
res.flatten
|
960
999
|
end
|
961
1000
|
|
962
1001
|
def term(allow_var)
|
963
|
-
|
1002
|
+
e = tok(NUMBER) ||
|
964
1003
|
interp_uri ||
|
965
1004
|
function(allow_var) ||
|
966
1005
|
interp_string ||
|
@@ -968,22 +1007,23 @@ MESSAGE
|
|
968
1007
|
interp_ident ||
|
969
1008
|
tok(HEXCOLOR) ||
|
970
1009
|
(allow_var && var_expr)
|
971
|
-
|
972
|
-
end
|
1010
|
+
return e if e
|
973
1011
|
|
974
|
-
|
1012
|
+
op = tok(/[+-]/)
|
1013
|
+
return unless op
|
975
1014
|
@expected = "number or function"
|
976
|
-
|
977
|
-
|
1015
|
+
[op,
|
1016
|
+
tok(NUMBER) || function(allow_var) || (allow_var && var_expr) || expr!(:interpolation)]
|
978
1017
|
end
|
979
1018
|
|
980
1019
|
def function(allow_var)
|
981
|
-
|
1020
|
+
name = tok(FUNCTION)
|
1021
|
+
return unless name
|
982
1022
|
if name == "expression(" || name == "calc("
|
983
1023
|
str, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
|
984
1024
|
[name, str]
|
985
1025
|
else
|
986
|
-
[name, str{ss}, expr(allow_var), tok!(/\)/)]
|
1026
|
+
[name, str {ss}, expr(allow_var), tok!(/\)/)]
|
987
1027
|
end
|
988
1028
|
end
|
989
1029
|
|
@@ -1009,7 +1049,8 @@ MESSAGE
|
|
1009
1049
|
end
|
1010
1050
|
|
1011
1051
|
def _interp_string(type)
|
1012
|
-
|
1052
|
+
start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
|
1053
|
+
return unless start
|
1013
1054
|
res = [start]
|
1014
1055
|
|
1015
1056
|
mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
|
@@ -1023,17 +1064,20 @@ MESSAGE
|
|
1023
1064
|
end
|
1024
1065
|
|
1025
1066
|
def interp_ident(start = IDENT)
|
1026
|
-
|
1067
|
+
val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
|
1068
|
+
return unless val
|
1027
1069
|
res = [val]
|
1028
|
-
while val = tok(NAME) || interpolation
|
1070
|
+
while (val = tok(NAME) || interpolation)
|
1029
1071
|
res << val
|
1030
1072
|
end
|
1031
1073
|
res
|
1032
1074
|
end
|
1033
1075
|
|
1034
1076
|
def interp_ident_or_var
|
1035
|
-
|
1036
|
-
|
1077
|
+
id = interp_ident
|
1078
|
+
return id if id
|
1079
|
+
var = var_expr
|
1080
|
+
return [var] if var
|
1037
1081
|
end
|
1038
1082
|
|
1039
1083
|
def interp_name
|
@@ -1071,11 +1115,15 @@ MESSAGE
|
|
1071
1115
|
|
1072
1116
|
@sass_script_parser = Class.new(Sass::Script::Parser)
|
1073
1117
|
@sass_script_parser.send(:include, ScriptParser)
|
1074
|
-
|
1075
|
-
|
1118
|
+
|
1119
|
+
class << self
|
1120
|
+
# @private
|
1121
|
+
attr_accessor :sass_script_parser
|
1122
|
+
end
|
1076
1123
|
|
1077
1124
|
def sass_script(*args)
|
1078
|
-
parser = self.class.sass_script_parser.new(@scanner, @line, @offset,
|
1125
|
+
parser = self.class.sass_script_parser.new(@scanner, @line, @offset,
|
1126
|
+
:filename => @filename, :importer => @importer)
|
1079
1127
|
result = parser.send(*args)
|
1080
1128
|
unless @strs.empty?
|
1081
1129
|
# Convert to CSS manually so that comments are ignored.
|
@@ -1121,12 +1169,14 @@ MESSAGE
|
|
1121
1169
|
end
|
1122
1170
|
|
1123
1171
|
def expr!(name)
|
1124
|
-
|
1172
|
+
e = send(name)
|
1173
|
+
return e if e
|
1125
1174
|
expected(EXPR_NAMES[name] || name.to_s)
|
1126
1175
|
end
|
1127
1176
|
|
1128
1177
|
def tok!(rx)
|
1129
|
-
|
1178
|
+
t = tok(rx)
|
1179
|
+
return t if t
|
1130
1180
|
name = TOK_NAMES[rx]
|
1131
1181
|
|
1132
1182
|
unless name
|