oreorenasass 3.4.4 → 3.4.5
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 +50 -70
- data/Rakefile +5 -26
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass.rb +12 -19
- data/lib/sass/cache_stores/base.rb +2 -2
- data/lib/sass/cache_stores/chain.rb +1 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -1
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +0 -1
- data/lib/sass/css.rb +13 -11
- data/lib/sass/engine.rb +173 -424
- data/lib/sass/environment.rb +58 -148
- data/lib/sass/error.rb +14 -11
- data/lib/sass/exec.rb +703 -5
- data/lib/sass/importers/base.rb +6 -49
- data/lib/sass/importers/filesystem.rb +19 -44
- data/lib/sass/logger.rb +4 -1
- data/lib/sass/logger/base.rb +4 -2
- data/lib/sass/logger/log_level.rb +7 -3
- data/lib/sass/media.rb +23 -20
- data/lib/sass/plugin.rb +7 -7
- data/lib/sass/plugin/compiler.rb +145 -304
- data/lib/sass/plugin/configuration.rb +23 -18
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script.rb +8 -35
- data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +606 -0
- data/lib/sass/script/css_lexer.rb +4 -8
- data/lib/sass/script/css_parser.rb +2 -5
- data/lib/sass/script/funcall.rb +245 -0
- data/lib/sass/script/functions.rb +408 -1491
- data/lib/sass/script/interpolation.rb +79 -0
- data/lib/sass/script/lexer.rb +68 -172
- data/lib/sass/script/list.rb +85 -0
- data/lib/sass/script/literal.rb +221 -0
- data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
- data/lib/sass/script/{value/null.rb → null.rb} +7 -14
- data/lib/sass/script/{value/number.rb → number.rb} +75 -152
- data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
- data/lib/sass/script/parser.rb +110 -245
- data/lib/sass/script/string.rb +51 -0
- data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
- data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
- data/lib/sass/script/variable.rb +58 -0
- data/lib/sass/scss/css_parser.rb +3 -9
- data/lib/sass/scss/parser.rb +421 -450
- data/lib/sass/scss/rx.rb +11 -19
- data/lib/sass/scss/static_parser.rb +7 -321
- data/lib/sass/selector.rb +194 -68
- data/lib/sass/selector/abstract_sequence.rb +14 -29
- data/lib/sass/selector/comma_sequence.rb +25 -108
- data/lib/sass/selector/sequence.rb +66 -159
- data/lib/sass/selector/simple.rb +25 -23
- data/lib/sass/selector/simple_sequence.rb +63 -173
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/supports.rb +15 -13
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +3 -3
- data/lib/sass/tree/css_import_node.rb +11 -11
- data/lib/sass/tree/debug_node.rb +2 -2
- data/lib/sass/tree/directive_node.rb +4 -21
- data/lib/sass/tree/each_node.rb +8 -8
- data/lib/sass/tree/extend_node.rb +7 -14
- data/lib/sass/tree/for_node.rb +4 -4
- data/lib/sass/tree/function_node.rb +4 -9
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +5 -4
- data/lib/sass/tree/media_node.rb +14 -4
- data/lib/sass/tree/mixin_def_node.rb +4 -4
- data/lib/sass/tree/mixin_node.rb +8 -21
- data/lib/sass/tree/node.rb +12 -54
- data/lib/sass/tree/prop_node.rb +20 -39
- data/lib/sass/tree/return_node.rb +2 -3
- data/lib/sass/tree/root_node.rb +3 -19
- data/lib/sass/tree/rule_node.rb +22 -35
- data/lib/sass/tree/supports_node.rb +13 -0
- data/lib/sass/tree/trace_node.rb +1 -2
- data/lib/sass/tree/variable_node.rb +3 -9
- data/lib/sass/tree/visitors/base.rb +8 -5
- data/lib/sass/tree/visitors/check_nesting.rb +19 -49
- data/lib/sass/tree/visitors/convert.rb +56 -74
- data/lib/sass/tree/visitors/cssize.rb +74 -202
- data/lib/sass/tree/visitors/deep_copy.rb +5 -10
- data/lib/sass/tree/visitors/extend.rb +7 -7
- data/lib/sass/tree/visitors/perform.rb +185 -278
- data/lib/sass/tree/visitors/set_options.rb +6 -20
- data/lib/sass/tree/visitors/to_css.rb +81 -234
- data/lib/sass/tree/warn_node.rb +2 -2
- data/lib/sass/tree/while_node.rb +2 -2
- data/lib/sass/util.rb +152 -522
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/subset_map.rb +3 -4
- data/lib/sass/util/test.rb +1 -0
- data/lib/sass/version.rb +22 -20
- data/test/Gemfile +3 -0
- data/test/Gemfile.lock +10 -0
- data/test/sass/cache_test.rb +20 -62
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/conversion_test.rb +2 -296
- data/test/sass/css2sass_test.rb +4 -23
- data/test/sass/engine_test.rb +354 -411
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +145 -324
- data/test/sass/functions_test.rb +86 -873
- data/test/sass/importer_test.rb +21 -241
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +26 -16
- 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 -36
- data/test/sass/script_test.rb +53 -485
- data/test/sass/scss/css_test.rb +28 -143
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +325 -2119
- data/test/sass/templates/scss_import.scss +1 -2
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +1 -86
- data/test/test_helper.rb +8 -37
- metadata +19 -66
- data/lib/sass/exec/base.rb +0 -187
- data/lib/sass/exec/sass_convert.rb +0 -264
- data/lib/sass/exec/sass_scss.rb +0 -424
- data/lib/sass/features.rb +0 -47
- data/lib/sass/script/tree.rb +0 -16
- data/lib/sass/script/tree/funcall.rb +0 -306
- data/lib/sass/script/tree/interpolation.rb +0 -118
- data/lib/sass/script/tree/list_literal.rb +0 -77
- data/lib/sass/script/tree/literal.rb +0 -45
- data/lib/sass/script/tree/map_literal.rb +0 -64
- data/lib/sass/script/tree/selector.rb +0 -26
- data/lib/sass/script/tree/variable.rb +0 -57
- data/lib/sass/script/value.rb +0 -11
- data/lib/sass/script/value/base.rb +0 -240
- data/lib/sass/script/value/bool.rb +0 -35
- data/lib/sass/script/value/color.rb +0 -680
- data/lib/sass/script/value/helpers.rb +0 -262
- data/lib/sass/script/value/list.rb +0 -113
- data/lib/sass/script/value/map.rb +0 -70
- data/lib/sass/script/value/string.rb +0 -97
- data/lib/sass/selector/pseudo.rb +0 -256
- data/lib/sass/source/map.rb +0 -210
- data/lib/sass/source/position.rb +0 -39
- data/lib/sass/source/range.rb +0 -41
- data/lib/sass/stack.rb +0 -120
- data/lib/sass/tree/at_root_node.rb +0 -83
- data/lib/sass/tree/error_node.rb +0 -18
- data/lib/sass/tree/keyframe_rule_node.rb +0 -15
- data/lib/sass/util/cross_platform_random.rb +0 -19
- data/lib/sass/util/normalized_map.rb +0 -130
- data/lib/sass/util/ordered_hash.rb +0 -192
- data/test/sass/compiler_test.rb +0 -232
- data/test/sass/encoding_test.rb +0 -219
- data/test/sass/source_map_test.rb +0 -977
- data/test/sass/superselector_test.rb +0 -191
- data/test/sass/util/normalized_map_test.rb +0 -51
- data/test/sass/value_helpers_test.rb +0 -179
data/lib/sass/engine.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'digest/sha1'
|
3
3
|
require 'sass/cache_stores'
|
4
|
-
require 'sass/source/position'
|
5
|
-
require 'sass/source/range'
|
6
|
-
require 'sass/source/map'
|
7
4
|
require 'sass/tree/node'
|
8
5
|
require 'sass/tree/root_node'
|
9
6
|
require 'sass/tree/rule_node'
|
@@ -29,9 +26,6 @@ require 'sass/tree/debug_node'
|
|
29
26
|
require 'sass/tree/warn_node'
|
30
27
|
require 'sass/tree/import_node'
|
31
28
|
require 'sass/tree/charset_node'
|
32
|
-
require 'sass/tree/at_root_node'
|
33
|
-
require 'sass/tree/keyframe_rule_node'
|
34
|
-
require 'sass/tree/error_node'
|
35
29
|
require 'sass/tree/visitors/base'
|
36
30
|
require 'sass/tree/visitors/perform'
|
37
31
|
require 'sass/tree/visitors/cssize'
|
@@ -45,7 +39,6 @@ require 'sass/selector'
|
|
45
39
|
require 'sass/environment'
|
46
40
|
require 'sass/script'
|
47
41
|
require 'sass/scss'
|
48
|
-
require 'sass/stack'
|
49
42
|
require 'sass/error'
|
50
43
|
require 'sass/importers'
|
51
44
|
require 'sass/shared'
|
@@ -53,17 +46,18 @@ require 'sass/media'
|
|
53
46
|
require 'sass/supports'
|
54
47
|
|
55
48
|
module Sass
|
49
|
+
|
56
50
|
# A Sass mixin or function.
|
57
51
|
#
|
58
52
|
# `name`: `String`
|
59
53
|
# : The name of the mixin/function.
|
60
54
|
#
|
61
|
-
# `args`: `Array<(Script::
|
55
|
+
# `args`: `Array<(Script::Node, Script::Node)>`
|
62
56
|
# : The arguments for the mixin/function.
|
63
57
|
# Each element is a tuple containing the variable node of the argument
|
64
58
|
# and the parse tree for the default value of the argument.
|
65
59
|
#
|
66
|
-
# `splat`: `Script::
|
60
|
+
# `splat`: `Script::Node?`
|
67
61
|
# : The variable node of the splat argument for this callable, or null.
|
68
62
|
#
|
69
63
|
# `environment`: {Sass::Environment}
|
@@ -89,6 +83,8 @@ module Sass
|
|
89
83
|
# output = sass_engine.render
|
90
84
|
# puts output
|
91
85
|
class Engine
|
86
|
+
include Sass::Util
|
87
|
+
|
92
88
|
# A line of Sass code.
|
93
89
|
#
|
94
90
|
# `text`: `String`
|
@@ -198,8 +194,6 @@ module Sass
|
|
198
194
|
when :alternate; options[:property_syntax] = :new
|
199
195
|
when :normal; options[:property_syntax] = :old
|
200
196
|
end
|
201
|
-
options[:sourcemap] = :auto if options[:sourcemap] == true
|
202
|
-
options[:sourcemap] = :none if options[:sourcemap] == false
|
203
197
|
|
204
198
|
options
|
205
199
|
end
|
@@ -252,7 +246,7 @@ module Sass
|
|
252
246
|
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
|
253
247
|
# @see {Sass::Engine.for_file}
|
254
248
|
# @see {Sass::Plugin}
|
255
|
-
def initialize(template, options
|
249
|
+
def initialize(template, options={})
|
256
250
|
@options = self.class.normalize_options(options)
|
257
251
|
@template = template
|
258
252
|
end
|
@@ -265,27 +259,9 @@ module Sass
|
|
265
259
|
# cannot be converted to UTF-8
|
266
260
|
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
267
261
|
def render
|
268
|
-
return
|
269
|
-
Sass::Util.silence_sass_warnings {
|
262
|
+
return _render unless @options[:quiet]
|
263
|
+
Sass::Util.silence_sass_warnings {_render}
|
270
264
|
end
|
271
|
-
|
272
|
-
# Render the template to CSS and return the source map.
|
273
|
-
#
|
274
|
-
# @param sourcemap_uri [String] The sourcemap URI to use in the
|
275
|
-
# `@sourceMappingURL` comment. If this is relative, it should be relative
|
276
|
-
# to the location of the CSS file.
|
277
|
-
# @return [(String, Sass::Source::Map)] The rendered CSS and the associated
|
278
|
-
# source map
|
279
|
-
# @raise [Sass::SyntaxError] if there's an error in the document, or if the
|
280
|
-
# public URL for this document couldn't be determined.
|
281
|
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
282
|
-
# cannot be converted to UTF-8
|
283
|
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
284
|
-
def render_with_sourcemap(sourcemap_uri)
|
285
|
-
return _render_with_sourcemap(sourcemap_uri) unless @options[:quiet]
|
286
|
-
Sass::Util.silence_sass_warnings {_render_with_sourcemap(sourcemap_uri)}
|
287
|
-
end
|
288
|
-
|
289
265
|
alias_method :to_css, :render
|
290
266
|
|
291
267
|
# Parses the document into its parse tree. Memoized.
|
@@ -293,11 +269,9 @@ module Sass
|
|
293
269
|
# @return [Sass::Tree::Node] The root of the parse tree.
|
294
270
|
# @raise [Sass::SyntaxError] if there's an error in the document
|
295
271
|
def to_tree
|
296
|
-
@tree ||=
|
297
|
-
|
298
|
-
|
299
|
-
_to_tree
|
300
|
-
end
|
272
|
+
@tree ||= @options[:quiet] ?
|
273
|
+
Sass::Util.silence_sass_warnings {_to_tree} :
|
274
|
+
_to_tree
|
301
275
|
end
|
302
276
|
|
303
277
|
# Returns the original encoding of the document,
|
@@ -309,7 +283,7 @@ module Sass
|
|
309
283
|
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
310
284
|
def source_encoding
|
311
285
|
check_encoding!
|
312
|
-
@
|
286
|
+
@original_encoding
|
313
287
|
end
|
314
288
|
|
315
289
|
# Gets a set of all the documents
|
@@ -326,8 +300,7 @@ module Sass
|
|
326
300
|
#
|
327
301
|
# @private
|
328
302
|
def _dependencies(seen, engines)
|
329
|
-
key = [@options[:filename], @options[:importer]]
|
330
|
-
return if seen.include?(key)
|
303
|
+
return if seen.include?(key = [@options[:filename], @options[:importer]])
|
331
304
|
seen << key
|
332
305
|
engines << self
|
333
306
|
to_tree.grep(Tree::ImportNode) do |n|
|
@@ -338,59 +311,35 @@ module Sass
|
|
338
311
|
|
339
312
|
private
|
340
313
|
|
341
|
-
def
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
No filename is available so there's nothing for the source map to link to.
|
350
|
-
ERR
|
351
|
-
elsif importer.nil?
|
352
|
-
raise Sass::SyntaxError.new(<<ERR)
|
353
|
-
Error generating source map: couldn't determine public URL for "#{filename}".
|
354
|
-
Without a public URL, there's nothing for the source map to link to.
|
355
|
-
An importer was not set for this file.
|
356
|
-
ERR
|
357
|
-
elsif Sass::Util.silence_warnings do
|
358
|
-
sourcemap_dir = nil if @options[:sourcemap] == :file
|
359
|
-
importer.public_url(filename, sourcemap_dir).nil?
|
360
|
-
end
|
361
|
-
raise Sass::SyntaxError.new(<<ERR)
|
362
|
-
Error generating source map: couldn't determine public URL for "#{filename}".
|
363
|
-
Without a public URL, there's nothing for the source map to link to.
|
364
|
-
Custom importers should define the #public_url method.
|
365
|
-
ERR
|
314
|
+
def _render
|
315
|
+
rendered = _to_tree.render
|
316
|
+
return rendered if ruby1_8?
|
317
|
+
begin
|
318
|
+
# Try to convert the result to the original encoding,
|
319
|
+
# but if that doesn't work fall back on UTF-8
|
320
|
+
rendered = rendered.encode(source_encoding)
|
321
|
+
rescue EncodingError
|
366
322
|
end
|
367
|
-
|
368
|
-
|
369
|
-
compressed = @options[:style] == :compressed
|
370
|
-
rendered << "\n" if rendered[-1] != ?\n
|
371
|
-
rendered << "\n" unless compressed
|
372
|
-
rendered << "/*# sourceMappingURL="
|
373
|
-
rendered << Sass::Util.escape_uri(sourcemap_uri)
|
374
|
-
rendered << " */\n"
|
375
|
-
return rendered, sourcemap
|
323
|
+
rendered.gsub(Regexp.new('\A@charset "(.*?)"'.encode(source_encoding)),
|
324
|
+
"@charset \"#{source_encoding.name}\"".encode(source_encoding))
|
376
325
|
end
|
377
326
|
|
378
327
|
def _to_tree
|
379
|
-
check_encoding!
|
380
|
-
|
381
328
|
if (@options[:cache] || @options[:read_cache]) &&
|
382
329
|
@options[:filename] && @options[:importer]
|
383
330
|
key = sassc_key
|
384
331
|
sha = Digest::SHA1.hexdigest(@template)
|
385
332
|
|
386
|
-
if
|
333
|
+
if root = @options[:cache_store].retrieve(key, sha)
|
387
334
|
root.options = @options
|
388
335
|
return root
|
389
336
|
end
|
390
337
|
end
|
391
338
|
|
339
|
+
check_encoding!
|
340
|
+
|
392
341
|
if @options[:syntax] == :scss
|
393
|
-
root = Sass::SCSS::Parser.new(@template, @options[:filename]
|
342
|
+
root = Sass::SCSS::Parser.new(@template, @options[:filename]).parse
|
394
343
|
else
|
395
344
|
root = Tree::RootNode.new(@template)
|
396
345
|
append_children(root, tree(tabulate(@template)).first, true)
|
@@ -420,7 +369,9 @@ ERR
|
|
420
369
|
def check_encoding!
|
421
370
|
return if @checked_encoding
|
422
371
|
@checked_encoding = true
|
423
|
-
@template, @
|
372
|
+
@template, @original_encoding = check_sass_encoding(@template) do |msg, line|
|
373
|
+
raise Sass::SyntaxError.new(msg, :line => line)
|
374
|
+
end
|
424
375
|
end
|
425
376
|
|
426
377
|
def tabulate(string)
|
@@ -428,7 +379,7 @@ ERR
|
|
428
379
|
comment_tab_str = nil
|
429
380
|
first = true
|
430
381
|
lines = []
|
431
|
-
string.scan(/^[^\n]*?$/).each_with_index do |line, index|
|
382
|
+
string.gsub(/\r\n|\r|\n/, "\n").scan(/^[^\n]*?$/).each_with_index do |line, index|
|
432
383
|
index += (@options[:line] || 1)
|
433
384
|
if line.strip.empty?
|
434
385
|
lines.last.text << "\n" if lines.last && lines.last.comment?
|
@@ -473,15 +424,12 @@ END
|
|
473
424
|
raise SyntaxError.new(message, :line => index)
|
474
425
|
end
|
475
426
|
|
476
|
-
lines << Line.new(line.strip, line_tabs, index,
|
427
|
+
lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
|
477
428
|
end
|
478
429
|
lines
|
479
430
|
end
|
480
431
|
|
481
|
-
# @comment
|
482
|
-
# rubocop:disable ParameterLists
|
483
432
|
def try_comment(line, last, tab_str, comment_tab_str, index)
|
484
|
-
# rubocop:enable ParameterLists
|
485
433
|
return unless last && last.comment?
|
486
434
|
# Nested comment stuff must be at least one whitespace char deeper
|
487
435
|
# than the normal indentation
|
@@ -506,8 +454,7 @@ MSG
|
|
506
454
|
nodes = []
|
507
455
|
while (line = arr[i]) && line.tabs >= base
|
508
456
|
if line.tabs > base
|
509
|
-
raise SyntaxError.new(
|
510
|
-
"The line was indented #{line.tabs - base} levels deeper than the previous line.",
|
457
|
+
raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
|
511
458
|
:line => line.index) if line.tabs > base + 1
|
512
459
|
|
513
460
|
nodes.last.children, i = tree(arr, i)
|
@@ -521,7 +468,6 @@ MSG
|
|
521
468
|
|
522
469
|
def build_tree(parent, line, root = false)
|
523
470
|
@line = line.index
|
524
|
-
@offset = line.offset
|
525
471
|
node_or_nodes = parse_line(parent, line, root)
|
526
472
|
|
527
473
|
Array(node_or_nodes).each do |node|
|
@@ -564,8 +510,6 @@ MSG
|
|
564
510
|
if continued_comment &&
|
565
511
|
child.line == continued_comment.line +
|
566
512
|
continued_comment.lines + 1
|
567
|
-
continued_comment.value.last.sub!(/ \*\/\Z/, '')
|
568
|
-
child.value.first.gsub!(/\A\/\*/, ' *')
|
569
513
|
continued_comment.value += ["\n"] + child.value
|
570
514
|
next
|
571
515
|
end
|
@@ -607,25 +551,12 @@ WARNING
|
|
607
551
|
# which begin with ::,
|
608
552
|
# as well as pseudo-classes
|
609
553
|
# if we're using the new property syntax
|
610
|
-
Tree::RuleNode.new(parse_interp(line.text)
|
554
|
+
Tree::RuleNode.new(parse_interp(line.text))
|
611
555
|
else
|
612
|
-
name_start_offset = line.offset + 1 # +1 for the leading ':'
|
613
556
|
name, value = line.text.scan(PROPERTY_OLD)[0]
|
614
557
|
raise SyntaxError.new("Invalid property: \"#{line.text}\".",
|
615
558
|
:line => @line) if name.nil? || value.nil?
|
616
|
-
|
617
|
-
value_start_offset = name_end_offset = name_start_offset + name.length
|
618
|
-
unless value.empty?
|
619
|
-
# +1 and -1 both compensate for the leading ':', which is part of line.text
|
620
|
-
value_start_offset = name_start_offset + line.text.index(value, name.length + 1) - 1
|
621
|
-
end
|
622
|
-
|
623
|
-
property = parse_property(name, parse_interp(name), value, :old, line, value_start_offset)
|
624
|
-
property.name_source_range = Sass::Source::Range.new(
|
625
|
-
Sass::Source::Position.new(@line, to_parser_offset(name_start_offset)),
|
626
|
-
Sass::Source::Position.new(@line, to_parser_offset(name_end_offset)),
|
627
|
-
@options[:filename], @options[:importer])
|
628
|
-
property
|
559
|
+
parse_property(name, parse_interp(name), value, :old, line)
|
629
560
|
end
|
630
561
|
when ?$
|
631
562
|
parse_variable(line)
|
@@ -634,12 +565,12 @@ WARNING
|
|
634
565
|
when DIRECTIVE_CHAR
|
635
566
|
parse_directive(parent, line, root)
|
636
567
|
when ESCAPE_CHAR
|
637
|
-
Tree::RuleNode.new(parse_interp(line.text[1..-1])
|
568
|
+
Tree::RuleNode.new(parse_interp(line.text[1..-1]))
|
638
569
|
when MIXIN_DEFINITION_CHAR
|
639
570
|
parse_mixin_definition(line)
|
640
571
|
when MIXIN_INCLUDE_CHAR
|
641
572
|
if line.text[1].nil? || line.text[1] == ?\s
|
642
|
-
Tree::RuleNode.new(parse_interp(line.text)
|
573
|
+
Tree::RuleNode.new(parse_interp(line.text))
|
643
574
|
else
|
644
575
|
parse_mixin_include(line, root)
|
645
576
|
end
|
@@ -651,70 +582,32 @@ WARNING
|
|
651
582
|
def parse_property_or_rule(line)
|
652
583
|
scanner = Sass::Util::MultibyteStringScanner.new(line.text)
|
653
584
|
hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
|
654
|
-
|
655
|
-
offset += hack_char.length if hack_char
|
656
|
-
parser = Sass::SCSS::Parser.new(scanner,
|
657
|
-
@options[:filename], @options[:importer],
|
658
|
-
@line, to_parser_offset(offset))
|
659
|
-
|
660
|
-
unless (res = parser.parse_interp_ident)
|
661
|
-
parsed = parse_interp(line.text, line.offset)
|
662
|
-
return Tree::RuleNode.new(parsed, full_line_range(line))
|
663
|
-
end
|
585
|
+
parser = Sass::SCSS::Parser.new(scanner, @options[:filename], @line)
|
664
586
|
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
@options[:filename], @options[:importer])
|
669
|
-
offset = parser.offset - 1
|
587
|
+
unless res = parser.parse_interp_ident
|
588
|
+
return Tree::RuleNode.new(parse_interp(line.text))
|
589
|
+
end
|
670
590
|
res.unshift(hack_char) if hack_char
|
671
|
-
|
672
|
-
# Handle comments after a property name but before the colon.
|
673
|
-
if (comment = scanner.scan(Sass::SCSS::RX::COMMENT))
|
591
|
+
if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
|
674
592
|
res << comment
|
675
|
-
offset += comment.length
|
676
593
|
end
|
677
594
|
|
678
595
|
name = line.text[0...scanner.pos]
|
679
|
-
if
|
680
|
-
|
681
|
-
property = parse_property(name, res, scanner.rest, :new, line, offset)
|
682
|
-
property.name_source_range = ident_range
|
683
|
-
property
|
596
|
+
if scanner.scan(/\s*:(?:\s|$)/)
|
597
|
+
parse_property(name, res, scanner.rest, :new, line)
|
684
598
|
else
|
685
599
|
res.pop if comment
|
686
|
-
|
687
|
-
if (trailing = (scanner.scan(/\s*#{Sass::SCSS::RX::COMMENT}/) ||
|
688
|
-
scanner.scan(/\s*#{Sass::SCSS::RX::SINGLE_LINE_COMMENT}/)))
|
689
|
-
trailing.strip!
|
690
|
-
end
|
691
|
-
interp_parsed = parse_interp(scanner.rest)
|
692
|
-
selector_range = Sass::Source::Range.new(
|
693
|
-
ident_range.start_pos,
|
694
|
-
Sass::Source::Position.new(@line, to_parser_offset(line.offset) + line.text.length),
|
695
|
-
@options[:filename], @options[:importer])
|
696
|
-
rule = Tree::RuleNode.new(res + interp_parsed, selector_range)
|
697
|
-
rule << Tree::CommentNode.new([trailing], :silent) if trailing
|
698
|
-
rule
|
600
|
+
Tree::RuleNode.new(res + parse_interp(scanner.rest))
|
699
601
|
end
|
700
602
|
end
|
701
603
|
|
702
|
-
|
703
|
-
# rubocop:disable ParameterLists
|
704
|
-
def parse_property(name, parsed_name, value, prop, line, start_offset)
|
705
|
-
# rubocop:enable ParameterLists
|
604
|
+
def parse_property(name, parsed_name, value, prop, line)
|
706
605
|
if value.strip.empty?
|
707
|
-
expr = Sass::Script::
|
708
|
-
end_offset = start_offset
|
606
|
+
expr = Sass::Script::String.new("")
|
709
607
|
else
|
710
|
-
expr = parse_script(value, :offset =>
|
711
|
-
end_offset = expr.source_range.end_pos.offset - 1
|
608
|
+
expr = parse_script(value, :offset => line.offset + line.text.index(value))
|
712
609
|
end
|
713
610
|
node = Tree::PropNode.new(parse_interp(name), expr, prop)
|
714
|
-
node.value_source_range = Sass::Source::Range.new(
|
715
|
-
Sass::Source::Position.new(line.index, to_parser_offset(start_offset)),
|
716
|
-
Sass::Source::Position.new(line.index, to_parser_offset(end_offset)),
|
717
|
-
@options[:filename], @options[:importer])
|
718
611
|
if value.strip.empty? && line.children.empty?
|
719
612
|
raise SyntaxError.new(
|
720
613
|
"Invalid property: \"#{node.declaration}\" (no value)." +
|
@@ -725,24 +618,15 @@ WARNING
|
|
725
618
|
end
|
726
619
|
|
727
620
|
def parse_variable(line)
|
728
|
-
name, value,
|
621
|
+
name, value, default = line.text.scan(Script::MATCH)[0]
|
729
622
|
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
|
730
623
|
:line => @line + 1) unless line.children.empty?
|
731
624
|
raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
|
732
625
|
:line => @line) unless name && value
|
733
|
-
flags = flags ? flags.split(/\s+/) : []
|
734
|
-
if (invalid_flag = flags.find {|f| f != '!default' && f != '!global'})
|
735
|
-
raise SyntaxError.new("Invalid flag \"#{invalid_flag}\".", :line => @line)
|
736
|
-
end
|
737
626
|
|
738
|
-
|
739
|
-
# otherwise we end up with the offset equal to the value index inside the name:
|
740
|
-
# $red_color: red;
|
741
|
-
var_lhs_length = 1 + name.length # 1 stands for '$'
|
742
|
-
index = line.text.index(value, line.offset + var_lhs_length) || 0
|
743
|
-
expr = parse_script(value, :offset => to_parser_offset(line.offset + index))
|
627
|
+
expr = parse_script(value, :offset => line.offset + line.text.index(value))
|
744
628
|
|
745
|
-
Tree::VariableNode.new(name, expr,
|
629
|
+
Tree::VariableNode.new(name, expr, default)
|
746
630
|
end
|
747
631
|
|
748
632
|
def parse_comment(line)
|
@@ -752,171 +636,108 @@ WARNING
|
|
752
636
|
if silent
|
753
637
|
value = [line.text]
|
754
638
|
else
|
755
|
-
value = self.class.parse_interp(
|
756
|
-
line.text, line.index, to_parser_offset(line.offset), :filename => @filename)
|
639
|
+
value = self.class.parse_interp(line.text, line.index, line.offset, :filename => @filename)
|
757
640
|
end
|
758
|
-
value =
|
641
|
+
value = with_extracted_values(value) do |str|
|
759
642
|
str = str.gsub(/^#{line.comment_tab_str}/m, '')[2..-1] # get rid of // or /*
|
760
643
|
format_comment_text(str, silent)
|
761
644
|
end
|
762
|
-
type = if silent
|
763
|
-
:silent
|
764
|
-
elsif loud
|
765
|
-
:loud
|
766
|
-
else
|
767
|
-
:normal
|
768
|
-
end
|
645
|
+
type = if silent then :silent elsif loud then :loud else :normal end
|
769
646
|
Tree::CommentNode.new(value, type)
|
770
647
|
else
|
771
|
-
Tree::RuleNode.new(parse_interp(line.text)
|
648
|
+
Tree::RuleNode.new(parse_interp(line.text))
|
772
649
|
end
|
773
650
|
end
|
774
651
|
|
775
|
-
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
|
776
|
-
:each, :while, :if, :else, :extend, :import, :media, :charset, :content,
|
777
|
-
:at_root, :error]
|
778
|
-
|
779
|
-
# @comment
|
780
|
-
# rubocop:disable MethodLength
|
781
652
|
def parse_directive(parent, line, root)
|
782
653
|
directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
|
783
|
-
raise SyntaxError.new("Invalid directive: '@'.") unless directive
|
784
654
|
offset = directive.size + whitespace.size + 1 if whitespace
|
785
655
|
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
656
|
+
# If value begins with url( or ",
|
657
|
+
# it's a CSS @import rule and we don't want to touch it.
|
658
|
+
case directive
|
659
|
+
when 'import'
|
660
|
+
parse_import(line, value, offset)
|
661
|
+
when 'mixin'
|
662
|
+
parse_mixin_definition(line)
|
663
|
+
when 'content'
|
664
|
+
parse_content_directive(line)
|
665
|
+
when 'include'
|
666
|
+
parse_mixin_include(line, root)
|
667
|
+
when 'function'
|
668
|
+
parse_function(line, root)
|
669
|
+
when 'for'
|
670
|
+
parse_for(line, root, value)
|
671
|
+
when 'each'
|
672
|
+
parse_each(line, root, value)
|
673
|
+
when 'else'
|
674
|
+
parse_else(parent, line, value)
|
675
|
+
when 'while'
|
676
|
+
raise SyntaxError.new("Invalid while directive '@while': expected expression.") unless value
|
677
|
+
Tree::WhileNode.new(parse_script(value, :offset => offset))
|
678
|
+
when 'if'
|
679
|
+
raise SyntaxError.new("Invalid if directive '@if': expected expression.") unless value
|
680
|
+
Tree::IfNode.new(parse_script(value, :offset => offset))
|
681
|
+
when 'debug'
|
682
|
+
raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
|
683
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
|
684
|
+
:line => @line + 1) unless line.children.empty?
|
685
|
+
offset = line.offset + line.text.index(value).to_i
|
686
|
+
Tree::DebugNode.new(parse_script(value, :offset => offset))
|
687
|
+
when 'extend'
|
688
|
+
raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
|
689
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
|
690
|
+
:line => @line + 1) unless line.children.empty?
|
691
|
+
optional = !!value.gsub!(/\s+#{Sass::SCSS::RX::OPTIONAL}$/, '')
|
692
|
+
offset = line.offset + line.text.index(value).to_i
|
693
|
+
Tree::ExtendNode.new(parse_interp(value, offset), optional)
|
694
|
+
when 'warn'
|
695
|
+
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
|
696
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
|
697
|
+
:line => @line + 1) unless line.children.empty?
|
698
|
+
offset = line.offset + line.text.index(value).to_i
|
699
|
+
Tree::WarnNode.new(parse_script(value, :offset => offset))
|
700
|
+
when 'return'
|
701
|
+
raise SyntaxError.new("Invalid @return: expected expression.") unless value
|
702
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath return directives.",
|
703
|
+
:line => @line + 1) unless line.children.empty?
|
704
|
+
offset = line.offset + line.text.index(value).to_i
|
705
|
+
Tree::ReturnNode.new(parse_script(value, :offset => offset))
|
706
|
+
when 'charset'
|
707
|
+
name = value && value[/\A(["'])(.*)\1\Z/, 2] #"
|
708
|
+
raise SyntaxError.new("Invalid charset directive '@charset': expected string.") unless name
|
709
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
|
710
|
+
:line => @line + 1) unless line.children.empty?
|
711
|
+
Tree::CharsetNode.new(name)
|
712
|
+
when 'media'
|
793
713
|
parser = Sass::SCSS::Parser.new(value, @options[:filename], @line)
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
Tree::WhileNode.new(parse_script(value, :offset => offset))
|
804
|
-
end
|
805
|
-
|
806
|
-
def parse_if_directive(parent, line, root, value, offset)
|
807
|
-
raise SyntaxError.new("Invalid if directive '@if': expected expression.") unless value
|
808
|
-
Tree::IfNode.new(parse_script(value, :offset => offset))
|
809
|
-
end
|
810
|
-
|
811
|
-
def parse_debug_directive(parent, line, root, value, offset)
|
812
|
-
raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
|
813
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
|
814
|
-
:line => @line + 1) unless line.children.empty?
|
815
|
-
offset = line.offset + line.text.index(value).to_i
|
816
|
-
Tree::DebugNode.new(parse_script(value, :offset => offset))
|
817
|
-
end
|
818
|
-
|
819
|
-
def parse_error_directive(parent, line, root, value, offset)
|
820
|
-
raise SyntaxError.new("Invalid error directive '@error': expected expression.") unless value
|
821
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath error directives.",
|
822
|
-
:line => @line + 1) unless line.children.empty?
|
823
|
-
offset = line.offset + line.text.index(value).to_i
|
824
|
-
Tree::ErrorNode.new(parse_script(value, :offset => offset))
|
825
|
-
end
|
826
|
-
|
827
|
-
def parse_extend_directive(parent, line, root, value, offset)
|
828
|
-
raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
|
829
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
|
830
|
-
:line => @line + 1) unless line.children.empty?
|
831
|
-
optional = !!value.gsub!(/\s+#{Sass::SCSS::RX::OPTIONAL}$/, '')
|
832
|
-
offset = line.offset + line.text.index(value).to_i
|
833
|
-
interp_parsed = parse_interp(value, offset)
|
834
|
-
selector_range = Sass::Source::Range.new(
|
835
|
-
Sass::Source::Position.new(@line, to_parser_offset(offset)),
|
836
|
-
Sass::Source::Position.new(@line, to_parser_offset(line.offset) + line.text.length),
|
837
|
-
@options[:filename], @options[:importer]
|
838
|
-
)
|
839
|
-
Tree::ExtendNode.new(interp_parsed, optional, selector_range)
|
840
|
-
end
|
841
|
-
# @comment
|
842
|
-
# rubocop:enable MethodLength
|
843
|
-
|
844
|
-
def parse_warn_directive(parent, line, root, value, offset)
|
845
|
-
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
|
846
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
|
847
|
-
:line => @line + 1) unless line.children.empty?
|
848
|
-
offset = line.offset + line.text.index(value).to_i
|
849
|
-
Tree::WarnNode.new(parse_script(value, :offset => offset))
|
850
|
-
end
|
851
|
-
|
852
|
-
def parse_return_directive(parent, line, root, value, offset)
|
853
|
-
raise SyntaxError.new("Invalid @return: expected expression.") unless value
|
854
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath return directives.",
|
855
|
-
:line => @line + 1) unless line.children.empty?
|
856
|
-
offset = line.offset + line.text.index(value).to_i
|
857
|
-
Tree::ReturnNode.new(parse_script(value, :offset => offset))
|
858
|
-
end
|
859
|
-
|
860
|
-
def parse_charset_directive(parent, line, root, value, offset)
|
861
|
-
name = value && value[/\A(["'])(.*)\1\Z/, 2] # "
|
862
|
-
raise SyntaxError.new("Invalid charset directive '@charset': expected string.") unless name
|
863
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
|
864
|
-
:line => @line + 1) unless line.children.empty?
|
865
|
-
Tree::CharsetNode.new(name)
|
866
|
-
end
|
867
|
-
|
868
|
-
def parse_media_directive(parent, line, root, value, offset)
|
869
|
-
parser = Sass::SCSS::Parser.new(value,
|
870
|
-
@options[:filename], @options[:importer],
|
871
|
-
@line, to_parser_offset(@offset))
|
872
|
-
offset = line.offset + line.text.index('media').to_i - 1
|
873
|
-
parsed_media_query_list = parser.parse_media_query_list.to_a
|
874
|
-
node = Tree::MediaNode.new(parsed_media_query_list)
|
875
|
-
node.source_range = Sass::Source::Range.new(
|
876
|
-
Sass::Source::Position.new(@line, to_parser_offset(offset)),
|
877
|
-
Sass::Source::Position.new(@line, to_parser_offset(line.offset) + line.text.length),
|
878
|
-
@options[:filename], @options[:importer])
|
879
|
-
node
|
880
|
-
end
|
881
|
-
|
882
|
-
def parse_at_root_directive(parent, line, root, value, offset)
|
883
|
-
return Sass::Tree::AtRootNode.new unless value
|
714
|
+
Tree::MediaNode.new(parser.parse_media_query_list.to_a)
|
715
|
+
when nil
|
716
|
+
raise SyntaxError.new("Invalid directive: '@'.")
|
717
|
+
else
|
718
|
+
unprefixed_directive = directive.gsub(/^-[a-z0-9]+-/i, '')
|
719
|
+
if unprefixed_directive == 'supports'
|
720
|
+
parser = Sass::SCSS::Parser.new(value, @options[:filename], @line)
|
721
|
+
return Tree::SupportsNode.new(directive, parser.parse_supports_condition)
|
722
|
+
end
|
884
723
|
|
885
|
-
|
886
|
-
|
887
|
-
@options[:filename], @options[:importer],
|
888
|
-
@line, to_parser_offset(@offset))
|
889
|
-
offset = line.offset + line.text.index('at-root').to_i - 1
|
890
|
-
return Tree::AtRootNode.new(parser.parse_at_root_query)
|
724
|
+
Tree::DirectiveNode.new(
|
725
|
+
value.nil? ? ["@#{directive}"] : ["@#{directive} "] + parse_interp(value, offset))
|
891
726
|
end
|
892
|
-
|
893
|
-
at_root_node = Tree::AtRootNode.new
|
894
|
-
parsed = parse_interp(value, offset)
|
895
|
-
rule_node = Tree::RuleNode.new(parsed, full_line_range(line))
|
896
|
-
|
897
|
-
# The caller expects to automatically add children to the returned node
|
898
|
-
# and we want it to add children to the rule node instead, so we
|
899
|
-
# manually handle the wiring here and return nil so the caller doesn't
|
900
|
-
# duplicate our efforts.
|
901
|
-
append_children(rule_node, line.children, false)
|
902
|
-
at_root_node << rule_node
|
903
|
-
parent << at_root_node
|
904
|
-
nil
|
905
727
|
end
|
906
728
|
|
907
|
-
def
|
908
|
-
var, from_expr, to_name, to_expr =
|
909
|
-
value.scan(/^([^\s]+)\s+from\s+(.+)\s+(to|through)\s+(.+)$/).first
|
729
|
+
def parse_for(line, root, text)
|
730
|
+
var, from_expr, to_name, to_expr = text.scan(/^([^\s]+)\s+from\s+(.+)\s+(to|through)\s+(.+)$/).first
|
910
731
|
|
911
732
|
if var.nil? # scan failed, try to figure out why for error message
|
912
|
-
if
|
733
|
+
if text !~ /^[^\s]+/
|
913
734
|
expected = "variable name"
|
914
|
-
elsif
|
735
|
+
elsif text !~ /^[^\s]+\s+from\s+.+/
|
915
736
|
expected = "'from <expr>'"
|
916
737
|
else
|
917
738
|
expected = "'to <expr>' or 'through <expr>'"
|
918
739
|
end
|
919
|
-
raise SyntaxError.new("Invalid for directive '@for #{
|
740
|
+
raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
|
920
741
|
end
|
921
742
|
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
|
922
743
|
|
@@ -926,35 +747,31 @@ WARNING
|
|
926
747
|
Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
|
927
748
|
end
|
928
749
|
|
929
|
-
def
|
930
|
-
|
750
|
+
def parse_each(line, root, text)
|
751
|
+
var, list_expr = text.scan(/^([^\s]+)\s+in\s+(.+)$/).first
|
931
752
|
|
932
|
-
if
|
933
|
-
if
|
753
|
+
if var.nil? # scan failed, try to figure out why for error message
|
754
|
+
if text !~ /^[^\s]+/
|
934
755
|
expected = "variable name"
|
935
|
-
elsif
|
756
|
+
elsif text !~ /^[^\s]+\s+from\s+.+/
|
936
757
|
expected = "'in <expr>'"
|
937
758
|
end
|
938
|
-
raise SyntaxError.new("Invalid
|
939
|
-
end
|
940
|
-
|
941
|
-
vars = vars.split(',').map do |var|
|
942
|
-
var.strip!
|
943
|
-
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
|
944
|
-
var[1..-1]
|
759
|
+
raise SyntaxError.new("Invalid for directive '@each #{text}': expected #{expected}.")
|
945
760
|
end
|
761
|
+
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
|
946
762
|
|
763
|
+
var = var[1..-1]
|
947
764
|
parsed_list = parse_script(list_expr, :offset => line.offset + line.text.index(list_expr))
|
948
|
-
Tree::EachNode.new(
|
765
|
+
Tree::EachNode.new(var, parsed_list)
|
949
766
|
end
|
950
767
|
|
951
|
-
def
|
768
|
+
def parse_else(parent, line, text)
|
952
769
|
previous = parent.children.last
|
953
770
|
raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
|
954
771
|
|
955
|
-
if
|
956
|
-
if
|
957
|
-
raise SyntaxError.new("Invalid else directive '@else #{
|
772
|
+
if text
|
773
|
+
if text !~ /^if\s+(.+)/
|
774
|
+
raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
|
958
775
|
end
|
959
776
|
expr = parse_script($1, :offset => line.offset + line.text.index($1))
|
960
777
|
end
|
@@ -965,7 +782,7 @@ WARNING
|
|
965
782
|
nil
|
966
783
|
end
|
967
784
|
|
968
|
-
def
|
785
|
+
def parse_import(line, value, offset)
|
969
786
|
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
|
970
787
|
:line => @line + 1) unless line.children.empty?
|
971
788
|
|
@@ -973,9 +790,8 @@ WARNING
|
|
973
790
|
values = []
|
974
791
|
|
975
792
|
loop do
|
976
|
-
unless
|
977
|
-
raise SyntaxError.new(
|
978
|
-
"Invalid @import: expected file to import, was #{scanner.rest.inspect}",
|
793
|
+
unless node = parse_import_arg(scanner, offset + scanner.pos)
|
794
|
+
raise SyntaxError.new("Invalid @import: expected file to import, was #{scanner.rest.inspect}",
|
979
795
|
:line => @line)
|
980
796
|
end
|
981
797
|
values << node
|
@@ -987,81 +803,35 @@ WARNING
|
|
987
803
|
:line => @line)
|
988
804
|
end
|
989
805
|
|
990
|
-
values
|
806
|
+
return values
|
991
807
|
end
|
992
808
|
|
993
|
-
# @comment
|
994
|
-
# rubocop:disable MethodLength
|
995
809
|
def parse_import_arg(scanner, offset)
|
996
810
|
return if scanner.eos?
|
997
811
|
|
998
812
|
if scanner.match?(/url\(/i)
|
999
|
-
script_parser = Sass::Script::Parser.new(scanner, @line,
|
813
|
+
script_parser = Sass::Script::Parser.new(scanner, @line, offset, @options)
|
1000
814
|
str = script_parser.parse_string
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
node = Tree::CssImportNode.new(str)
|
1005
|
-
else
|
1006
|
-
media_parser = Sass::SCSS::Parser.new(scanner,
|
1007
|
-
@options[:filename], @options[:importer],
|
1008
|
-
@line, str.source_range.end_pos.offset)
|
1009
|
-
media = media_parser.parse_media_query_list
|
1010
|
-
end_pos = Sass::Source::Position.new(@line, media_parser.offset + 1)
|
1011
|
-
node = Tree::CssImportNode.new(str, media.to_a)
|
1012
|
-
end
|
1013
|
-
|
1014
|
-
node.source_range = Sass::Source::Range.new(
|
1015
|
-
str.source_range.start_pos, end_pos,
|
1016
|
-
@options[:filename], @options[:importer])
|
1017
|
-
return node
|
815
|
+
media_parser = Sass::SCSS::Parser.new(scanner, @options[:filename], @line)
|
816
|
+
media = media_parser.parse_media_query_list
|
817
|
+
return Tree::CssImportNode.new(str, media.to_a)
|
1018
818
|
end
|
1019
819
|
|
1020
|
-
unless
|
1021
|
-
|
1022
|
-
node = Tree::ImportNode.new(scanned)
|
1023
|
-
start_parser_offset = to_parser_offset(offset)
|
1024
|
-
node.source_range = Sass::Source::Range.new(
|
1025
|
-
Sass::Source::Position.new(@line, start_parser_offset),
|
1026
|
-
Sass::Source::Position.new(@line, start_parser_offset + scanned.length),
|
1027
|
-
@options[:filename], @options[:importer])
|
1028
|
-
return node
|
820
|
+
unless str = scanner.scan(Sass::SCSS::RX::STRING)
|
821
|
+
return Tree::ImportNode.new(scanner.scan(/[^,;]+/))
|
1029
822
|
end
|
1030
823
|
|
1031
|
-
|
1032
|
-
|
1033
|
-
val = Sass::Script::Value::String.value(scanner[1] || scanner[2])
|
1034
|
-
scanned = scanner.scan(/\s*/)
|
824
|
+
val = scanner[1] || scanner[2]
|
825
|
+
scanner.scan(/\s*/)
|
1035
826
|
if !scanner.match?(/[,;]|$/)
|
1036
|
-
|
1037
|
-
media_parser = Sass::SCSS::Parser.new(scanner,
|
1038
|
-
@options[:filename], @options[:importer], @line, offset)
|
827
|
+
media_parser = Sass::SCSS::Parser.new(scanner, @options[:filename], @line)
|
1039
828
|
media = media_parser.parse_media_query_list
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
Sass::Source::Position.new(@line, media_parser.offset),
|
1044
|
-
@options[:filename], @options[:importer])
|
1045
|
-
elsif val =~ %r{^(https?:)?//}
|
1046
|
-
node = Tree::CssImportNode.new(quoted_val)
|
1047
|
-
node.source_range = Sass::Source::Range.new(
|
1048
|
-
Sass::Source::Position.new(@line, to_parser_offset(start_offset)),
|
1049
|
-
Sass::Source::Position.new(@line, to_parser_offset(offset)),
|
1050
|
-
@options[:filename], @options[:importer])
|
829
|
+
Tree::CssImportNode.new(str || uri, media.to_a)
|
830
|
+
elsif val =~ /^(https?:)?\/\//
|
831
|
+
Tree::CssImportNode.new("url(#{val})")
|
1051
832
|
else
|
1052
|
-
|
1053
|
-
node.source_range = Sass::Source::Range.new(
|
1054
|
-
Sass::Source::Position.new(@line, to_parser_offset(start_offset)),
|
1055
|
-
Sass::Source::Position.new(@line, to_parser_offset(offset)),
|
1056
|
-
@options[:filename], @options[:importer])
|
833
|
+
Tree::ImportNode.new(val)
|
1057
834
|
end
|
1058
|
-
node
|
1059
|
-
end
|
1060
|
-
# @comment
|
1061
|
-
# rubocop:enable MethodLength
|
1062
|
-
|
1063
|
-
def parse_mixin_directive(parent, line, root, value, offset)
|
1064
|
-
parse_mixin_definition(line)
|
1065
835
|
end
|
1066
836
|
|
1067
837
|
MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
@@ -1070,53 +840,45 @@ WARNING
|
|
1070
840
|
raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
|
1071
841
|
|
1072
842
|
offset = line.offset + line.text.size - arg_string.size
|
1073
|
-
args, splat = Script::Parser.new(arg_string.strip, @line,
|
843
|
+
args, splat = Script::Parser.new(arg_string.strip, @line, offset, @options).
|
1074
844
|
parse_mixin_definition_arglist
|
1075
845
|
Tree::MixinDefNode.new(name, args, splat)
|
1076
846
|
end
|
1077
847
|
|
1078
848
|
CONTENT_RE = /^@content\s*(.+)?$/
|
1079
|
-
def parse_content_directive(
|
849
|
+
def parse_content_directive(line)
|
1080
850
|
trailing = line.text.scan(CONTENT_RE).first.first
|
1081
|
-
unless trailing.nil?
|
1082
|
-
raise SyntaxError.new(
|
1083
|
-
"Invalid content directive. Trailing characters found: \"#{trailing}\".")
|
1084
|
-
end
|
851
|
+
raise SyntaxError.new("Invalid content directive. Trailing characters found: \"#{trailing}\".") unless trailing.nil?
|
1085
852
|
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath @content directives.",
|
1086
853
|
:line => line.index + 1) unless line.children.empty?
|
1087
854
|
Tree::ContentNode.new
|
1088
855
|
end
|
1089
856
|
|
1090
|
-
def parse_include_directive(parent, line, root, value, offset)
|
1091
|
-
parse_mixin_include(line, root)
|
1092
|
-
end
|
1093
|
-
|
1094
857
|
MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
1095
858
|
def parse_mixin_include(line, root)
|
1096
859
|
name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
|
1097
860
|
raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
|
1098
861
|
|
1099
862
|
offset = line.offset + line.text.size - arg_string.size
|
1100
|
-
args, keywords, splat,
|
1101
|
-
|
1102
|
-
|
1103
|
-
Tree::MixinNode.new(name, args, keywords, splat, kwarg_splat)
|
863
|
+
args, keywords, splat = Script::Parser.new(arg_string.strip, @line, offset, @options).
|
864
|
+
parse_mixin_include_arglist
|
865
|
+
Tree::MixinNode.new(name, args, keywords, splat)
|
1104
866
|
end
|
1105
867
|
|
1106
868
|
FUNCTION_RE = /^@function\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
1107
|
-
def
|
869
|
+
def parse_function(line, root)
|
1108
870
|
name, arg_string = line.text.scan(FUNCTION_RE).first
|
1109
871
|
raise SyntaxError.new("Invalid function definition \"#{line.text}\".") if name.nil?
|
1110
872
|
|
1111
873
|
offset = line.offset + line.text.size - arg_string.size
|
1112
|
-
args, splat = Script::Parser.new(arg_string.strip, @line,
|
874
|
+
args, splat = Script::Parser.new(arg_string.strip, @line, offset, @options).
|
1113
875
|
parse_function_definition_arglist
|
1114
876
|
Tree::FunctionNode.new(name, args, splat)
|
1115
877
|
end
|
1116
878
|
|
1117
879
|
def parse_script(script, options = {})
|
1118
880
|
line = options[:line] || @line
|
1119
|
-
offset = options[:offset] ||
|
881
|
+
offset = options[:offset] || 0
|
1120
882
|
Script.parse(script, line, offset, @options)
|
1121
883
|
end
|
1122
884
|
|
@@ -1128,12 +890,12 @@ WARNING
|
|
1128
890
|
content.shift
|
1129
891
|
end
|
1130
892
|
|
1131
|
-
return "/* */" if content.empty?
|
1132
|
-
content.last.gsub!(
|
893
|
+
return silent ? "//" : "/* */" if content.empty?
|
894
|
+
content.last.gsub!(%r{ ?\*/ *$}, '')
|
1133
895
|
content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
|
1134
896
|
content.first.gsub!(/^ /, '') unless removed_first
|
1135
897
|
if silent
|
1136
|
-
"
|
898
|
+
"//" + content.join("\n//")
|
1137
899
|
else
|
1138
900
|
# The #gsub fixes the case of a trailing */
|
1139
901
|
"/*" + content.join("\n *").gsub(/ \*\Z/, '') + " */"
|
@@ -1144,20 +906,8 @@ WARNING
|
|
1144
906
|
self.class.parse_interp(text, @line, offset, :filename => @filename)
|
1145
907
|
end
|
1146
908
|
|
1147
|
-
# Parser tracks 1-based line and offset, so our offset should be converted.
|
1148
|
-
def to_parser_offset(offset)
|
1149
|
-
offset + 1
|
1150
|
-
end
|
1151
|
-
|
1152
|
-
def full_line_range(line)
|
1153
|
-
Sass::Source::Range.new(
|
1154
|
-
Sass::Source::Position.new(@line, to_parser_offset(line.offset)),
|
1155
|
-
Sass::Source::Position.new(@line, to_parser_offset(line.offset) + line.text.length),
|
1156
|
-
@options[:filename], @options[:importer])
|
1157
|
-
end
|
1158
|
-
|
1159
909
|
# It's important that this have strings (at least)
|
1160
|
-
# at the beginning, the end, and between each Script::
|
910
|
+
# at the beginning, the end, and between each Script::Node.
|
1161
911
|
#
|
1162
912
|
# @private
|
1163
913
|
def self.parse_interp(text, line, offset, options)
|
@@ -1165,13 +915,12 @@ WARNING
|
|
1165
915
|
rest = Sass::Shared.handle_interpolation text do |scan|
|
1166
916
|
escapes = scan[2].size
|
1167
917
|
res << scan.matched[0...-2 - escapes]
|
1168
|
-
if escapes
|
918
|
+
if escapes % 2 == 1
|
1169
919
|
res << "\\" * (escapes - 1) << '#{'
|
1170
920
|
else
|
1171
921
|
res << "\\" * [0, escapes - 1].max
|
1172
|
-
# Add 1 to emulate to_parser_offset.
|
1173
922
|
res << Script::Parser.new(
|
1174
|
-
scan, line, offset + scan.pos - scan.matched_size
|
923
|
+
scan, line, offset + scan.pos - scan.matched_size, options).
|
1175
924
|
parse_interpolated
|
1176
925
|
end
|
1177
926
|
end
|