sass 3.3.0.alpha.253 → 3.3.0.alpha.255
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/REVISION +1 -1
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass/engine.rb +4 -3
- data/lib/sass/script/functions.rb +30 -1
- data/lib/sass/script/parser.rb +15 -7
- data/lib/sass/script/tree/funcall.rb +28 -15
- data/lib/sass/script/value/arg_list.rb +0 -5
- data/lib/sass/scss/parser.rb +2 -2
- data/lib/sass/tree/mixin_node.rb +16 -3
- data/lib/sass/tree/visitors/convert.rb +1 -0
- data/lib/sass/tree/visitors/perform.rb +117 -71
- data/test/sass/conversion_test.rb +56 -0
- data/test/sass/engine_test.rb +20 -0
- data/test/sass/functions_test.rb +6 -0
- data/test/sass/scss/scss_test.rb +336 -2
- metadata +10 -10
data/REVISION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
a4e9233341aeaf94bd00b4c85b95e004c94df2f7
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.3.0.alpha.
|
1
|
+
3.3.0.alpha.255
|
data/VERSION_DATE
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
18 September 2013 22:11:11 GMT
|
data/lib/sass/engine.rb
CHANGED
@@ -1060,9 +1060,10 @@ WARNING
|
|
1060
1060
|
raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
|
1061
1061
|
|
1062
1062
|
offset = line.offset + line.text.size - arg_string.size
|
1063
|
-
args, keywords, splat
|
1064
|
-
|
1065
|
-
|
1063
|
+
args, keywords, splat, kwarg_splat =
|
1064
|
+
Script::Parser.new(arg_string.strip, @line, to_parser_offset(offset), @options).
|
1065
|
+
parse_mixin_include_arglist
|
1066
|
+
Tree::MixinNode.new(name, args, keywords, splat, kwarg_splat)
|
1066
1067
|
end
|
1067
1068
|
|
1068
1069
|
FUNCTION_RE = /^@function\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
@@ -194,6 +194,9 @@ module Sass::Script
|
|
194
194
|
# \{#map_has_key map-has-key($key)}
|
195
195
|
# : Returns whether a map has a value associated with a given key.
|
196
196
|
#
|
197
|
+
# \{#keywords keywords($args)}
|
198
|
+
# : Returns the keywords passed to a function that takes variable arguments.
|
199
|
+
#
|
197
200
|
# ## Introspection Functions
|
198
201
|
#
|
199
202
|
# \{#feature_exists feature-exists($feature)}
|
@@ -367,6 +370,11 @@ module Sass::Script
|
|
367
370
|
class EvaluationContext
|
368
371
|
include Functions
|
369
372
|
|
373
|
+
# The human-readable names for [Sass::Script::Value::Base]. The default is
|
374
|
+
# just the downcased name of the type. The default is the downcased type
|
375
|
+
# name.
|
376
|
+
TYPE_NAMES = {:ArgList => 'variable argument list'}
|
377
|
+
|
370
378
|
# The global environment.
|
371
379
|
#
|
372
380
|
# @return [Environment]
|
@@ -401,7 +409,7 @@ module Sass::Script
|
|
401
409
|
klass = Sass::Script::Value.const_get(type)
|
402
410
|
return if value.is_a?(klass)
|
403
411
|
return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.is_pseudo_map?
|
404
|
-
err = "#{value.inspect} is not a #{type.to_s.downcase}"
|
412
|
+
err = "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
|
405
413
|
err = "$#{name.to_s.gsub('_', '-')}: " + err if name
|
406
414
|
raise ArgumentError.new(err)
|
407
415
|
end
|
@@ -1859,6 +1867,26 @@ module Sass::Script
|
|
1859
1867
|
end
|
1860
1868
|
declare :map_has_key, [:map, :key]
|
1861
1869
|
|
1870
|
+
# Returns the map of named arguments passed to a function or mixin that
|
1871
|
+
# takes a variable argument list. The argument names are strings, and they
|
1872
|
+
# do not contain the leading `$`.
|
1873
|
+
#
|
1874
|
+
# @example
|
1875
|
+
# @mixin foo($args...) {
|
1876
|
+
# @debug keywords($args); //=> (arg1: val, arg2: val)
|
1877
|
+
# }
|
1878
|
+
#
|
1879
|
+
# @include foo($arg1: val, $arg2: val);
|
1880
|
+
# @overload keywords($args)
|
1881
|
+
# @param $args [Sass::Script::Value::ArgList]
|
1882
|
+
# @return [Sass::Script::Value::Map]
|
1883
|
+
# @raise [ArgumentError] if `$args` isn't a variable argument list
|
1884
|
+
def keywords(args)
|
1885
|
+
assert_type args, :ArgList
|
1886
|
+
Sass::Script::Value::Map.new(Sass::Util.map_keys(args.keywords) {|k| Sass::Script::String.new(k)})
|
1887
|
+
end
|
1888
|
+
declare :keywords, [:args]
|
1889
|
+
|
1862
1890
|
# Returns one of two values, depending on whether or not `$condition` is
|
1863
1891
|
# true. Just like in `@if`, all values other than `false` and `null` are
|
1864
1892
|
# considered to be true.
|
@@ -1917,6 +1945,7 @@ module Sass::Script
|
|
1917
1945
|
name.value,
|
1918
1946
|
args.map {|a| Sass::Script::Tree::Literal.new(a)},
|
1919
1947
|
Sass::Util.map_vals(kwargs) {|v| Sass::Script::Tree::Literal.new(v)},
|
1948
|
+
nil,
|
1920
1949
|
nil)
|
1921
1950
|
funcall.options = options
|
1922
1951
|
funcall.perform(environment)
|
data/lib/sass/script/parser.rb
CHANGED
@@ -81,14 +81,14 @@ module Sass
|
|
81
81
|
|
82
82
|
# Parses the argument list for a mixin include.
|
83
83
|
#
|
84
|
-
# @return [(Array<Script::Tree::Node>, {String => Script::Tree::Node}, Script::Tree::Node)]
|
84
|
+
# @return [(Array<Script::Tree::Node>, {String => Script::Tree::Node}, Script::Tree::Node, Script::Tree::Node)]
|
85
85
|
# The root nodes of the positional arguments, keyword arguments, and
|
86
|
-
# splat argument. Keyword arguments are in a hash from names to values.
|
86
|
+
# splat argument(s). Keyword arguments are in a hash from names to values.
|
87
87
|
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
|
88
88
|
def parse_mixin_include_arglist
|
89
89
|
args, keywords = [], {}
|
90
90
|
if try_tok(:lparen)
|
91
|
-
args, keywords, splat = mixin_arglist
|
91
|
+
args, keywords, splat, kwarg_splat = mixin_arglist
|
92
92
|
assert_tok(:rparen)
|
93
93
|
end
|
94
94
|
assert_done
|
@@ -96,7 +96,8 @@ module Sass
|
|
96
96
|
args.each {|a| a.options = @options}
|
97
97
|
keywords.each {|k, v| v.options = @options}
|
98
98
|
splat.options = @options if splat
|
99
|
-
|
99
|
+
kwarg_splat.options = @options if kwarg_splat
|
100
|
+
return args, keywords, splat, kwarg_splat
|
100
101
|
rescue Sass::SyntaxError => e
|
101
102
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
102
103
|
raise e
|
@@ -366,9 +367,9 @@ RUBY
|
|
366
367
|
|
367
368
|
def funcall
|
368
369
|
return raw unless tok = try_tok(:funcall)
|
369
|
-
args, keywords, splat = fn_arglist
|
370
|
+
args, keywords, splat, kwarg_splat = fn_arglist
|
370
371
|
assert_tok(:rparen)
|
371
|
-
node(Script::Tree::Funcall.new(tok.value, args, keywords, splat),
|
372
|
+
node(Script::Tree::Funcall.new(tok.value, args, keywords, splat, kwarg_splat),
|
372
373
|
tok.source_range.start_pos, source_position)
|
373
374
|
end
|
374
375
|
|
@@ -433,7 +434,13 @@ RUBY
|
|
433
434
|
raise SyntaxError.new("Positional arguments must come before keyword arguments.")
|
434
435
|
end
|
435
436
|
|
436
|
-
|
437
|
+
if try_tok(:splat)
|
438
|
+
splat = e
|
439
|
+
return args, keywords, splat unless try_tok(:comma)
|
440
|
+
kwarg_splat = assert_expr(subexpr, description)
|
441
|
+
assert_tok(:splat)
|
442
|
+
return args, keywords, splat, kwarg_splat
|
443
|
+
end
|
437
444
|
args << e
|
438
445
|
end
|
439
446
|
|
@@ -517,6 +524,7 @@ RUBY
|
|
517
524
|
:default => "expression (e.g. 1px, bold)",
|
518
525
|
:mixin_arglist => "mixin argument",
|
519
526
|
:fn_arglist => "function argument",
|
527
|
+
:splat => "...",
|
520
528
|
}
|
521
529
|
|
522
530
|
def assert_expr(name, expected = nil)
|
@@ -23,20 +23,33 @@ module Sass::Script::Tree
|
|
23
23
|
# @return [{String => Node}]
|
24
24
|
attr_reader :keywords
|
25
25
|
|
26
|
-
# The splat argument for this function, if one exists.
|
26
|
+
# The first splat argument for this function, if one exists.
|
27
|
+
#
|
28
|
+
# This could be a list of positional arguments, a map of keyword
|
29
|
+
# arguments, or an arglist containing both.
|
27
30
|
#
|
28
31
|
# @return [Node?]
|
29
32
|
attr_accessor :splat
|
30
33
|
|
34
|
+
# The second splat argument for this function, if one exists.
|
35
|
+
#
|
36
|
+
# If this exists, it's always a map of keyword arguments, and
|
37
|
+
# \{#splat} is always either a list or an arglist.
|
38
|
+
#
|
39
|
+
# @return [Node?]
|
40
|
+
attr_accessor :kwarg_splat
|
41
|
+
|
31
42
|
# @param name [String] See \{#name}
|
32
43
|
# @param args [Array<Node>] See \{#args}
|
33
44
|
# @param keywords [Sass::Util::NormalizedMap<Node>] See \{#keywords}
|
34
45
|
# @param splat [Node] See \{#splat}
|
35
|
-
|
46
|
+
# @param kwarg_splat [Node] See \{#kwarg_splat}
|
47
|
+
def initialize(name, args, keywords, splat, kwarg_splat)
|
36
48
|
@name = name
|
37
49
|
@args = args
|
38
50
|
@keywords = keywords
|
39
51
|
@splat = splat
|
52
|
+
@kwarg_splat = kwarg_splat
|
40
53
|
super()
|
41
54
|
end
|
42
55
|
|
@@ -48,6 +61,7 @@ module Sass::Script::Tree
|
|
48
61
|
if self.splat
|
49
62
|
splat = (args.empty? && keywords.empty?) ? "" : ", "
|
50
63
|
splat = "#{splat}#{self.splat.inspect}..."
|
64
|
+
splat = "#{splat}, #{kwarg_splat.inspect}..." if kwarg_splat
|
51
65
|
end
|
52
66
|
"#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
|
53
67
|
end
|
@@ -66,6 +80,7 @@ module Sass::Script::Tree
|
|
66
80
|
if self.splat
|
67
81
|
splat = (args.empty? && keywords.empty?) ? "" : ", "
|
68
82
|
splat = "#{splat}#{arg_to_sass[self.splat]}..."
|
83
|
+
splat = "#{splat}, #{arg_to_sass[kwarg_splat]}..." if kwarg_splat
|
69
84
|
end
|
70
85
|
"#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
|
71
86
|
end
|
@@ -77,6 +92,7 @@ module Sass::Script::Tree
|
|
77
92
|
def children
|
78
93
|
res = @args + @keywords.values
|
79
94
|
res << @splat if @splat
|
95
|
+
res << @kwarg_splat if @kwarg_splat
|
80
96
|
res
|
81
97
|
end
|
82
98
|
|
@@ -99,14 +115,14 @@ module Sass::Script::Tree
|
|
99
115
|
# @raise [Sass::SyntaxError] if the function call raises an ArgumentError
|
100
116
|
def _perform(environment)
|
101
117
|
args = @args.map {|a| a.perform(environment)}
|
102
|
-
splat = @splat
|
118
|
+
splat = Sass::Tree::Visitors::Perform.perform_splat(@splat, @kwarg_splat, environment)
|
119
|
+
keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
|
103
120
|
if fn = environment.function(@name)
|
104
|
-
keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
|
105
121
|
return perform_sass_fn(fn, args, keywords, splat, environment)
|
106
122
|
end
|
107
123
|
|
108
124
|
ruby_name = @name.tr('-', '_')
|
109
|
-
args = construct_ruby_args(ruby_name, args, splat, environment)
|
125
|
+
args = construct_ruby_args(ruby_name, args, keywords, splat, environment)
|
110
126
|
|
111
127
|
unless Sass::Script::Functions.callable?(ruby_name)
|
112
128
|
opts(to_literal(args))
|
@@ -179,23 +195,20 @@ module Sass::Script::Tree
|
|
179
195
|
|
180
196
|
private
|
181
197
|
|
182
|
-
def construct_ruby_args(name, args, splat, environment)
|
198
|
+
def construct_ruby_args(name, args, keywords, splat, environment)
|
183
199
|
args += splat.to_a if splat
|
184
200
|
|
185
201
|
# If variable arguments were passed, there won't be any explicit keywords.
|
186
|
-
if splat.
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
kwargs_size = @keywords.size
|
202
|
+
if splat && !splat.keywords.empty?
|
203
|
+
old_keywords_accessed = splat.keywords_accessed
|
204
|
+
keywords = splat.keywords
|
205
|
+
splat.keywords_accessed = old_keywords_accessed
|
191
206
|
end
|
192
207
|
|
193
|
-
unless signature = Sass::Script::Functions.signature(name.to_sym, args.size,
|
194
|
-
return args if
|
208
|
+
unless signature = Sass::Script::Functions.signature(name.to_sym, args.size, keywords.size)
|
209
|
+
return args if keywords.empty?
|
195
210
|
raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
|
196
211
|
end
|
197
|
-
keywords = splat.is_a?(Sass::Script::Value::ArgList) ? splat.keywords :
|
198
|
-
Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
|
199
212
|
|
200
213
|
# If the user passes more non-keyword args than the function expects,
|
201
214
|
# but it does expect keyword args, Ruby's arg handling won't raise an error.
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -209,9 +209,9 @@ module Sass
|
|
209
209
|
|
210
210
|
def include_directive(start_pos)
|
211
211
|
name = tok! IDENT
|
212
|
-
args, keywords, splat = sass_script(:parse_mixin_include_arglist)
|
212
|
+
args, keywords, splat, kwarg_splat = sass_script(:parse_mixin_include_arglist)
|
213
213
|
ss
|
214
|
-
include_node = node(Sass::Tree::MixinNode.new(name, args, keywords, splat), start_pos)
|
214
|
+
include_node = node(Sass::Tree::MixinNode.new(name, args, keywords, splat, kwarg_splat), start_pos)
|
215
215
|
if tok?(/\{/)
|
216
216
|
include_node.has_children = true
|
217
217
|
block(include_node, :directive)
|
data/lib/sass/tree/mixin_node.rb
CHANGED
@@ -19,20 +19,33 @@ module Sass::Tree
|
|
19
19
|
# @return [{String => Script::Tree::Node}]
|
20
20
|
attr_accessor :keywords
|
21
21
|
|
22
|
-
# The splat argument for this mixin, if one exists.
|
22
|
+
# The first splat argument for this mixin, if one exists.
|
23
23
|
#
|
24
|
-
#
|
24
|
+
# This could be a list of positional arguments, a map of keyword
|
25
|
+
# arguments, or an arglist containing both.
|
26
|
+
#
|
27
|
+
# @return [Node?]
|
25
28
|
attr_accessor :splat
|
26
29
|
|
30
|
+
# The second splat argument for this mixin, if one exists.
|
31
|
+
#
|
32
|
+
# If this exists, it's always a map of keyword arguments, and
|
33
|
+
# \{#splat} is always either a list or an arglist.
|
34
|
+
#
|
35
|
+
# @return [Node?]
|
36
|
+
attr_accessor :kwarg_splat
|
37
|
+
|
27
38
|
# @param name [String] The name of the mixin
|
28
39
|
# @param args [Array<Script::Tree::Node>] See \{#args}
|
29
40
|
# @param splat [Script::Tree::Node] See \{#splat}
|
41
|
+
# @param kwarg_splat [Script::Tree::Node] See \{#kwarg_splat}
|
30
42
|
# @param keywords [{String => Script::Tree::Node}] See \{#keywords}
|
31
|
-
def initialize(name, args, keywords, splat)
|
43
|
+
def initialize(name, args, keywords, splat, kwarg_splat)
|
32
44
|
@name = name
|
33
45
|
@args = args
|
34
46
|
@keywords = keywords
|
35
47
|
@splat = splat
|
48
|
+
@kwarg_splat = kwarg_splat
|
36
49
|
super()
|
37
50
|
end
|
38
51
|
end
|
@@ -208,6 +208,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
208
208
|
if node.splat
|
209
209
|
splat = (args.empty? && keywords.empty?) ? "" : ", "
|
210
210
|
splat = "#{splat}#{arg_to_sass[node.splat]}..."
|
211
|
+
splat = "#{splat}, #{node.kwarg_splat.inspect}..." if node.kwarg_splat
|
211
212
|
end
|
212
213
|
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
|
213
214
|
end
|
@@ -1,89 +1,135 @@
|
|
1
1
|
# A visitor for converting a dynamic Sass tree into a static Sass tree.
|
2
2
|
class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
class << self
|
4
|
+
# @param root [Tree::Node] The root node of the tree to visit.
|
5
|
+
# @param environment [Sass::Environment] The lexical environment.
|
6
|
+
# @return [Tree::Node] The resulting tree of static nodes.
|
7
|
+
def visit(root, environment = nil)
|
8
|
+
new(environment).send(:visit, root)
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
# @api private
|
12
|
+
def perform_arguments(callable, args, keywords, splat)
|
13
|
+
desc = "#{callable.type.capitalize} #{callable.name}"
|
14
|
+
downcase_desc = "#{callable.type} #{callable.name}"
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
# If variable arguments were passed, there won't be any explicit keywords.
|
17
|
+
if splat && !splat.keywords.empty?
|
18
|
+
old_keywords_accessed = splat.keywords_accessed
|
19
|
+
keywords = splat.keywords
|
20
|
+
splat.keywords_accessed = old_keywords_accessed
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
unless keywords.empty?
|
25
|
+
unknown_args = Sass::Util.array_minus(keywords.keys,
|
26
|
+
callable.args.map {|var| var.first.underscored_name})
|
27
|
+
if callable.splat && unknown_args.include?(callable.splat.underscored_name)
|
28
|
+
raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc} cannot be used as a named argument.")
|
29
|
+
elsif unknown_args.any?
|
30
|
+
description = unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'
|
31
|
+
raise Sass::SyntaxError.new("#{desc} doesn't have #{description} #{unknown_args.map {|name| "$#{name}"}.join ', '}.")
|
32
|
+
end
|
24
33
|
end
|
34
|
+
rescue Sass::SyntaxError => keyword_exception
|
25
35
|
end
|
26
|
-
rescue Sass::SyntaxError => keyword_exception
|
27
|
-
end
|
28
36
|
|
29
|
-
|
30
|
-
|
31
|
-
|
37
|
+
# If there's no splat, raise the keyword exception immediately. The actual
|
38
|
+
# raising happens in the ensure clause at the end of this function.
|
39
|
+
return if keyword_exception && !callable.splat
|
32
40
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
41
|
+
if args.size > callable.args.size && !callable.splat
|
42
|
+
takes = callable.args.size
|
43
|
+
passed = args.size
|
44
|
+
raise Sass::SyntaxError.new(
|
45
|
+
"#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
|
46
|
+
"but #{passed} #{passed == 1 ? 'was' : 'were'} passed.")
|
47
|
+
end
|
40
48
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
keywords =
|
48
|
-
|
49
|
+
splat_sep = :comma
|
50
|
+
if splat
|
51
|
+
args += splat.to_a
|
52
|
+
splat_sep = splat.separator
|
53
|
+
end
|
54
|
+
|
55
|
+
keywords = keywords.dup
|
56
|
+
env = Sass::Environment.new(callable.environment)
|
57
|
+
callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
|
58
|
+
if value && keywords.include?(var.underscored_name)
|
59
|
+
raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name} both by position and by name.")
|
60
|
+
end
|
61
|
+
|
62
|
+
value ||= keywords.delete(var.underscored_name)
|
63
|
+
value ||= default && default.perform(env)
|
64
|
+
raise Sass::SyntaxError.new("#{desc} is missing argument #{var.inspect}.") unless value
|
65
|
+
env.set_local_var(var.name, value)
|
66
|
+
end
|
49
67
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
68
|
+
if callable.splat
|
69
|
+
rest = args[callable.args.length..-1] || []
|
70
|
+
arg_list = Sass::Script::Value::ArgList.new(rest, keywords.dup, splat_sep)
|
71
|
+
arg_list.options = env.options
|
72
|
+
env.set_local_var(callable.splat.name, arg_list)
|
55
73
|
end
|
56
74
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
75
|
+
yield env
|
76
|
+
rescue Exception => e
|
77
|
+
ensure
|
78
|
+
# If there's a keyword exception, we don't want to throw it immediately,
|
79
|
+
# because the invalid keywords may be part of a glob argument that should be
|
80
|
+
# passed on to another function. So we only raise it if we reach the end of
|
81
|
+
# this function *and* the keywords attached to the argument list glob object
|
82
|
+
# haven't been accessed.
|
83
|
+
#
|
84
|
+
# The keyword exception takes precedence over any Sass errors, but not over
|
85
|
+
# non-Sass exceptions.
|
86
|
+
if keyword_exception &&
|
87
|
+
!(arg_list && arg_list.keywords_accessed) &&
|
88
|
+
(e.nil? || e.is_a?(Sass::SyntaxError))
|
89
|
+
raise keyword_exception
|
90
|
+
elsif e
|
91
|
+
raise e
|
92
|
+
end
|
61
93
|
end
|
62
94
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
95
|
+
# @api private
|
96
|
+
# @return [Sass::Script::Value::ArgList]
|
97
|
+
def perform_splat(splat, kwarg_splat, environment)
|
98
|
+
return unless splat
|
99
|
+
splat = splat.perform(environment)
|
100
|
+
unless kwarg_splat
|
101
|
+
return splat if splat.is_a?(Sass::Script::Value::ArgList)
|
102
|
+
if splat.is_a?(Sass::Script::Value::Map)
|
103
|
+
args = []
|
104
|
+
kwargs = arg_hash(splat)
|
105
|
+
else
|
106
|
+
args = splat.to_a
|
107
|
+
kwargs = {}
|
108
|
+
end
|
109
|
+
return Sass::Script::Value::ArgList.new(args, kwargs, splat.separator || :comma)
|
110
|
+
end
|
111
|
+
|
112
|
+
kwarg_splat = kwarg_splat.perform(environment)
|
113
|
+
unless kwarg_splat.is_a?(Sass::Script::Value::Map)
|
114
|
+
raise Sass::SyntaxError.new("Variable keyword arguments must be a map (was #{kwarg_splat.inspect}).")
|
115
|
+
end
|
116
|
+
|
117
|
+
if splat.is_a?(Sass::Script::Value::ArgList)
|
118
|
+
return Sass::Script::Value::ArgList.new(
|
119
|
+
splat.value, splat.keywords.merge(arg_hash(kwarg_splat)), splat.separator)
|
120
|
+
else
|
121
|
+
return Sass::Script::Value::ArgList.new(splat.to_a, arg_hash(kwarg_splat), splat.separator)
|
122
|
+
end
|
68
123
|
end
|
69
124
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#
|
79
|
-
# The keyword exception takes precedence over any Sass errors, but not over
|
80
|
-
# non-Sass exceptions.
|
81
|
-
if keyword_exception &&
|
82
|
-
!(arg_list && arg_list.keywords_accessed) &&
|
83
|
-
(e.nil? || e.is_a?(Sass::SyntaxError))
|
84
|
-
raise keyword_exception
|
85
|
-
elsif e
|
86
|
-
raise e
|
125
|
+
private
|
126
|
+
|
127
|
+
def arg_hash(map)
|
128
|
+
Sass::Util.map_keys(map.to_h) do |key|
|
129
|
+
next key.value if key.is_a?(Sass::Script::Value::String)
|
130
|
+
raise Sass::SyntaxError.new("Variable keyword argument map must have string keys.\n" +
|
131
|
+
"#{key.inspect} is not a string in #{map.inspect}.");
|
132
|
+
end
|
87
133
|
end
|
88
134
|
end
|
89
135
|
|
@@ -267,7 +313,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
267
313
|
|
268
314
|
args = node.args.map {|a| a.perform(@environment)}
|
269
315
|
keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
|
270
|
-
splat = node.splat.
|
316
|
+
splat = self.class.perform_splat(node.splat, node.kwarg_splat, @environment)
|
271
317
|
|
272
318
|
self.class.perform_arguments(mixin, args, keywords, splat) do |env|
|
273
319
|
env.caller = Sass::Environment.new(@environment)
|
@@ -1621,6 +1621,26 @@ SASS
|
|
1621
1621
|
SCSS
|
1622
1622
|
end
|
1623
1623
|
|
1624
|
+
def test_mixin_var_kwargs
|
1625
|
+
assert_scss_to_sass <<SASS, <<SCSS
|
1626
|
+
=foo($a: b, $c: d)
|
1627
|
+
a: $a
|
1628
|
+
c: $c
|
1629
|
+
|
1630
|
+
.foo
|
1631
|
+
+foo($list..., $map...)
|
1632
|
+
SASS
|
1633
|
+
@mixin foo($a: b, $c: d) {
|
1634
|
+
a: $a;
|
1635
|
+
c: $c;
|
1636
|
+
}
|
1637
|
+
|
1638
|
+
.foo {
|
1639
|
+
@include foo($list..., $map...);
|
1640
|
+
}
|
1641
|
+
SCSS
|
1642
|
+
end
|
1643
|
+
|
1624
1644
|
def test_function_var_args
|
1625
1645
|
assert_scss_to_sass <<SASS, <<SCSS
|
1626
1646
|
@function foo($args...)
|
@@ -1648,6 +1668,24 @@ SASS
|
|
1648
1668
|
SCSS
|
1649
1669
|
end
|
1650
1670
|
|
1671
|
+
def test_function_var_kwargs
|
1672
|
+
assert_scss_to_sass <<SASS, <<SCSS
|
1673
|
+
@function foo($a: b, $c: d)
|
1674
|
+
@return foo
|
1675
|
+
|
1676
|
+
.foo
|
1677
|
+
a: foo($list..., $map...)
|
1678
|
+
SASS
|
1679
|
+
@function foo($a: b, $c: d) {
|
1680
|
+
@return foo;
|
1681
|
+
}
|
1682
|
+
|
1683
|
+
.foo {
|
1684
|
+
a: foo($list..., $map...);
|
1685
|
+
}
|
1686
|
+
SCSS
|
1687
|
+
end
|
1688
|
+
|
1651
1689
|
def test_at_root
|
1652
1690
|
assert_scss_to_sass <<SASS, <<SCSS
|
1653
1691
|
.foo
|
@@ -1684,6 +1722,24 @@ SASS
|
|
1684
1722
|
SCSS
|
1685
1723
|
end
|
1686
1724
|
|
1725
|
+
def test_function_var_kwargs_with_list
|
1726
|
+
assert_scss_to_sass <<SASS, <<SCSS
|
1727
|
+
@function foo($a: b, $c: d)
|
1728
|
+
@return $a, $c
|
1729
|
+
|
1730
|
+
.foo
|
1731
|
+
a: foo($list..., $map...)
|
1732
|
+
SASS
|
1733
|
+
@function foo($a: b, $c: d) {
|
1734
|
+
@return $a, $c;
|
1735
|
+
}
|
1736
|
+
|
1737
|
+
.foo {
|
1738
|
+
a: foo($list..., $map...);
|
1739
|
+
}
|
1740
|
+
SCSS
|
1741
|
+
end
|
1742
|
+
|
1687
1743
|
## Regression Tests
|
1688
1744
|
|
1689
1745
|
def test_list_in_args
|
data/test/sass/engine_test.rb
CHANGED
@@ -3282,6 +3282,26 @@ SASS
|
|
3282
3282
|
], e.sass_backtrace)
|
3283
3283
|
end
|
3284
3284
|
|
3285
|
+
def test_mixin_with_args_and_varargs_passed_no_var_args
|
3286
|
+
assert_equal <<CSS, render(<<SASS, :syntax => :scss)
|
3287
|
+
.foo {
|
3288
|
+
a: 1;
|
3289
|
+
b: 2;
|
3290
|
+
c: 3; }
|
3291
|
+
CSS
|
3292
|
+
@mixin three-or-more-args($a, $b, $c, $rest...) {
|
3293
|
+
a: $a;
|
3294
|
+
b: $b;
|
3295
|
+
c: $c;
|
3296
|
+
}
|
3297
|
+
|
3298
|
+
.foo {
|
3299
|
+
@include three-or-more-args($a: 1, $b: 2, $c: 3);
|
3300
|
+
}
|
3301
|
+
SASS
|
3302
|
+
|
3303
|
+
end
|
3304
|
+
|
3285
3305
|
private
|
3286
3306
|
|
3287
3307
|
def assert_hash_has(hash, expected)
|
data/test/sass/functions_test.rb
CHANGED
@@ -1350,6 +1350,12 @@ WARNING
|
|
1350
1350
|
end
|
1351
1351
|
end
|
1352
1352
|
|
1353
|
+
def test_keywords
|
1354
|
+
# The actual functionality is tested in tests where real arglists are passed.
|
1355
|
+
assert_error_message("12 is not a variable argument list for `keywords'", "keywords(12)")
|
1356
|
+
assert_error_message("(1 2 3) is not a variable argument list for `keywords'", "keywords(1 2 3)")
|
1357
|
+
end
|
1358
|
+
|
1353
1359
|
def test_partial_list_of_pairs_doesnt_work_as_a_map
|
1354
1360
|
assert_raises(Sass::SyntaxError) {evaluate("map-get((foo bar, baz bang, bip), 1)")}
|
1355
1361
|
assert_raises(Sass::SyntaxError) {evaluate("map-get((foo bar, baz bang, bip bap bop), 1)")}
|
data/test/sass/scss/scss_test.rb
CHANGED
@@ -954,6 +954,139 @@ CSS
|
|
954
954
|
SCSS
|
955
955
|
end
|
956
956
|
|
957
|
+
def test_mixin_var_keyword_args
|
958
|
+
assert_equal <<CSS, render(<<SCSS)
|
959
|
+
.foo {
|
960
|
+
a: 1;
|
961
|
+
b: 2;
|
962
|
+
c: 3; }
|
963
|
+
CSS
|
964
|
+
@mixin foo($args...) {
|
965
|
+
a: map-get(keywords($args), a);
|
966
|
+
b: map-get(keywords($args), b);
|
967
|
+
c: map-get(keywords($args), c);
|
968
|
+
}
|
969
|
+
|
970
|
+
.foo {@include foo($a: 1, $b: 2, $c: 3)}
|
971
|
+
SCSS
|
972
|
+
end
|
973
|
+
|
974
|
+
def test_mixin_empty_var_keyword_args
|
975
|
+
assert_equal <<CSS, render(<<SCSS)
|
976
|
+
.foo {
|
977
|
+
length: 0; }
|
978
|
+
CSS
|
979
|
+
@mixin foo($args...) {
|
980
|
+
length: length(keywords($args));
|
981
|
+
}
|
982
|
+
|
983
|
+
.foo {@include foo}
|
984
|
+
SCSS
|
985
|
+
end
|
986
|
+
|
987
|
+
def test_mixin_map_splat
|
988
|
+
assert_equal <<CSS, render(<<SCSS)
|
989
|
+
.foo {
|
990
|
+
a: 1;
|
991
|
+
b: 2;
|
992
|
+
c: 3; }
|
993
|
+
CSS
|
994
|
+
@mixin foo($a, $b, $c) {
|
995
|
+
a: $a;
|
996
|
+
b: $b;
|
997
|
+
c: $c;
|
998
|
+
}
|
999
|
+
|
1000
|
+
.foo {
|
1001
|
+
$map: (a: 1, b: 2, c: 3);
|
1002
|
+
@include foo($map...);
|
1003
|
+
}
|
1004
|
+
SCSS
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def test_mixin_map_and_list_splat
|
1008
|
+
assert_equal <<CSS, render(<<SCSS)
|
1009
|
+
.foo {
|
1010
|
+
a: x;
|
1011
|
+
b: y;
|
1012
|
+
c: z;
|
1013
|
+
d: 1;
|
1014
|
+
e: 2;
|
1015
|
+
f: 3; }
|
1016
|
+
CSS
|
1017
|
+
@mixin foo($a, $b, $c, $d, $e, $f) {
|
1018
|
+
a: $a;
|
1019
|
+
b: $b;
|
1020
|
+
c: $c;
|
1021
|
+
d: $d;
|
1022
|
+
e: $e;
|
1023
|
+
f: $f;
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
.foo {
|
1027
|
+
$list: x y z;
|
1028
|
+
$map: (d: 1, e: 2, f: 3);
|
1029
|
+
@include foo($list..., $map...);
|
1030
|
+
}
|
1031
|
+
SCSS
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def test_mixin_map_splat_takes_precedence_over_pass_through
|
1035
|
+
assert_equal <<CSS, render(<<SCSS)
|
1036
|
+
.foo {
|
1037
|
+
a: 1;
|
1038
|
+
b: 2;
|
1039
|
+
c: z; }
|
1040
|
+
CSS
|
1041
|
+
@mixin foo($args...) {
|
1042
|
+
$map: (c: z);
|
1043
|
+
@include bar($args..., $map...);
|
1044
|
+
}
|
1045
|
+
|
1046
|
+
@mixin bar($a, $b, $c) {
|
1047
|
+
a: $a;
|
1048
|
+
b: $b;
|
1049
|
+
c: $c;
|
1050
|
+
}
|
1051
|
+
|
1052
|
+
.foo {
|
1053
|
+
@include foo(1, $b: 2, $c: 3);
|
1054
|
+
}
|
1055
|
+
SCSS
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
def test_mixin_list_of_pairs_splat_treated_as_list
|
1059
|
+
assert_equal <<CSS, render(<<SCSS)
|
1060
|
+
.foo {
|
1061
|
+
a: a 1;
|
1062
|
+
b: b 2;
|
1063
|
+
c: c 3; }
|
1064
|
+
CSS
|
1065
|
+
@mixin foo($a, $b, $c) {
|
1066
|
+
a: $a;
|
1067
|
+
b: $b;
|
1068
|
+
c: $c;
|
1069
|
+
}
|
1070
|
+
|
1071
|
+
.foo {
|
1072
|
+
@include foo((a 1, b 2, c 3)...);
|
1073
|
+
}
|
1074
|
+
SCSS
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def test_mixin_keyword_splat_must_have_string_keys
|
1078
|
+
assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
|
1079
|
+
Variable keyword argument map must have string keys.
|
1080
|
+
12 is not a string in (12: 1).
|
1081
|
+
MESSAGE
|
1082
|
+
@mixin foo($a) {
|
1083
|
+
a: $a;
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
.foo {@include foo((12: 1)...)}
|
1087
|
+
SCSS
|
1088
|
+
end
|
1089
|
+
|
957
1090
|
def test_mixin_var_args_with_keyword
|
958
1091
|
assert_raise_message(Sass::SyntaxError, "Positional arguments must come before keyword arguments.") {render <<SCSS}
|
959
1092
|
@mixin foo($a, $b...) {
|
@@ -987,6 +1120,46 @@ SCSS
|
|
987
1120
|
SCSS
|
988
1121
|
end
|
989
1122
|
|
1123
|
+
def test_mixin_map_splat_before_list_splat
|
1124
|
+
assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was (2 3)).") {render <<SCSS}
|
1125
|
+
@mixin foo($a, $b, $c) {
|
1126
|
+
a: $a;
|
1127
|
+
b: $b;
|
1128
|
+
c: $c;
|
1129
|
+
}
|
1130
|
+
|
1131
|
+
.foo {
|
1132
|
+
@include foo((a: 1)..., (2 3)...);
|
1133
|
+
}
|
1134
|
+
SCSS
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
def test_mixin_map_splat_with_unknown_keyword
|
1138
|
+
assert_raise_message(Sass::SyntaxError, "Mixin foo doesn't have an argument named $c.") {render <<SCSS}
|
1139
|
+
@mixin foo($a, $b) {
|
1140
|
+
a: $a;
|
1141
|
+
b: $b;
|
1142
|
+
}
|
1143
|
+
|
1144
|
+
.foo {
|
1145
|
+
@include foo(1, 2, (c: 1)...);
|
1146
|
+
}
|
1147
|
+
SCSS
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
def test_mixin_map_splat_with_wrong_type
|
1151
|
+
assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was 12).") {render <<SCSS}
|
1152
|
+
@mixin foo($a, $b) {
|
1153
|
+
a: $a;
|
1154
|
+
b: $b;
|
1155
|
+
}
|
1156
|
+
|
1157
|
+
.foo {
|
1158
|
+
@include foo((1, 2)..., 12...);
|
1159
|
+
}
|
1160
|
+
SCSS
|
1161
|
+
end
|
1162
|
+
|
990
1163
|
def test_function_var_args
|
991
1164
|
assert_equal <<CSS, render(<<SCSS)
|
992
1165
|
.foo {
|
@@ -1112,10 +1285,122 @@ CSS
|
|
1112
1285
|
SCSS
|
1113
1286
|
end
|
1114
1287
|
|
1288
|
+
def test_function_var_keyword_args
|
1289
|
+
assert_equal <<CSS, render(<<SCSS)
|
1290
|
+
.foo {
|
1291
|
+
val: "a: 1, b: 2, c: 3"; }
|
1292
|
+
CSS
|
1293
|
+
@function foo($args...) {
|
1294
|
+
@return "a: \#{map-get(keywords($args), a)}, " +
|
1295
|
+
"b: \#{map-get(keywords($args), b)}, " +
|
1296
|
+
"c: \#{map-get(keywords($args), c)}";
|
1297
|
+
}
|
1298
|
+
|
1299
|
+
.foo {val: foo($a: 1, $b: 2, $c: 3)}
|
1300
|
+
SCSS
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
def test_function_empty_var_keyword_args
|
1304
|
+
assert_equal <<CSS, render(<<SCSS)
|
1305
|
+
.foo {
|
1306
|
+
length: 0; }
|
1307
|
+
CSS
|
1308
|
+
@function foo($args...) {
|
1309
|
+
@return length(keywords($args));
|
1310
|
+
}
|
1311
|
+
|
1312
|
+
.foo {length: foo()}
|
1313
|
+
SCSS
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
def test_function_map_splat
|
1317
|
+
assert_equal <<CSS, render(<<SCSS)
|
1318
|
+
.foo {
|
1319
|
+
val: "a: 1, b: 2, c: 3"; }
|
1320
|
+
CSS
|
1321
|
+
@function foo($a, $b, $c) {
|
1322
|
+
@return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
|
1323
|
+
}
|
1324
|
+
|
1325
|
+
.foo {
|
1326
|
+
$map: (a: 1, b: 2, c: 3);
|
1327
|
+
val: foo($map...);
|
1328
|
+
}
|
1329
|
+
SCSS
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
def test_function_map_and_list_splat
|
1333
|
+
assert_equal <<CSS, render(<<SCSS)
|
1334
|
+
.foo {
|
1335
|
+
val: "a: x, b: y, c: z, d: 1, e: 2, f: 3"; }
|
1336
|
+
CSS
|
1337
|
+
@function foo($a, $b, $c, $d, $e, $f) {
|
1338
|
+
@return "a: \#{$a}, b: \#{$b}, c: \#{$c}, d: \#{$d}, e: \#{$e}, f: \#{$f}";
|
1339
|
+
}
|
1340
|
+
|
1341
|
+
.foo {
|
1342
|
+
$list: x y z;
|
1343
|
+
$map: (d: 1, e: 2, f: 3);
|
1344
|
+
val: foo($list..., $map...);
|
1345
|
+
}
|
1346
|
+
SCSS
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
def test_function_map_splat_takes_precedence_over_pass_through
|
1350
|
+
assert_equal <<CSS, render(<<SCSS)
|
1351
|
+
.foo {
|
1352
|
+
val: "a: 1, b: 2, c: z"; }
|
1353
|
+
CSS
|
1354
|
+
@function foo($args...) {
|
1355
|
+
$map: (c: z);
|
1356
|
+
@return bar($args..., $map...);
|
1357
|
+
}
|
1358
|
+
|
1359
|
+
@function bar($a, $b, $c) {
|
1360
|
+
@return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
|
1361
|
+
}
|
1362
|
+
|
1363
|
+
.foo {
|
1364
|
+
val: foo(1, $b: 2, $c: 3);
|
1365
|
+
}
|
1366
|
+
SCSS
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
def test_ruby_function_map_splat_takes_precedence_over_pass_through
|
1370
|
+
assert_equal <<CSS, render(<<SCSS)
|
1371
|
+
.foo {
|
1372
|
+
val: 1 2 3 z; }
|
1373
|
+
CSS
|
1374
|
+
@function foo($args...) {
|
1375
|
+
$map: (val: z);
|
1376
|
+
@return append($args..., $map...);
|
1377
|
+
}
|
1378
|
+
|
1379
|
+
.foo {
|
1380
|
+
val: foo(1 2 3, $val: 4)
|
1381
|
+
}
|
1382
|
+
SCSS
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
def test_function_list_of_pairs_splat_treated_as_list
|
1386
|
+
assert_equal <<CSS, render(<<SCSS)
|
1387
|
+
.foo {
|
1388
|
+
val: "a: a 1, b: b 2, c: c 3"; }
|
1389
|
+
CSS
|
1390
|
+
@function foo($a, $b, $c) {
|
1391
|
+
@return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
|
1392
|
+
}
|
1393
|
+
|
1394
|
+
.foo {
|
1395
|
+
val: foo((a 1, b 2, c 3)...);
|
1396
|
+
}
|
1397
|
+
SCSS
|
1398
|
+
end
|
1399
|
+
|
1115
1400
|
def test_function_var_args_with_keyword
|
1116
1401
|
assert_raise_message(Sass::SyntaxError, "Positional arguments must come before keyword arguments.") {render <<SCSS}
|
1117
1402
|
@function foo($a, $b...) {
|
1118
|
-
@return "a: \#{$a}, b: $b";
|
1403
|
+
@return "a: \#{$a}, b: \#{$b}";
|
1119
1404
|
}
|
1120
1405
|
|
1121
1406
|
.foo {val: foo($a: 1, 2, 3, 4)}
|
@@ -1135,7 +1420,7 @@ SCSS
|
|
1135
1420
|
def test_function_keyword_for_unknown_arg_with_var_args
|
1136
1421
|
assert_raise_message(Sass::SyntaxError, "Function foo doesn't have an argument named $c.") {render <<SCSS}
|
1137
1422
|
@function foo($a, $b...) {
|
1138
|
-
@return "a: \#{$a}, b: \#{$b}";
|
1423
|
+
@return "a: \#{$a}, b: \#{length($b)}";
|
1139
1424
|
}
|
1140
1425
|
|
1141
1426
|
.foo {val: foo(1, $c: 2 3 4)}
|
@@ -1155,6 +1440,55 @@ CSS
|
|
1155
1440
|
SCSS
|
1156
1441
|
end
|
1157
1442
|
|
1443
|
+
def test_function_map_splat_before_list_splat
|
1444
|
+
assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was (2 3)).") {render <<SCSS}
|
1445
|
+
@function foo($a, $b, $c) {
|
1446
|
+
@return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
|
1447
|
+
}
|
1448
|
+
|
1449
|
+
.foo {
|
1450
|
+
val: foo((a: 1)..., (2 3)...);
|
1451
|
+
}
|
1452
|
+
SCSS
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
def test_function_map_splat_with_unknown_keyword
|
1456
|
+
assert_raise_message(Sass::SyntaxError, "Function foo doesn't have an argument named $c.") {render <<SCSS}
|
1457
|
+
@function foo($a, $b) {
|
1458
|
+
@return "a: \#{$a}, b: \#{$b}";
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
.foo {
|
1462
|
+
val: foo(1, 2, (c: 1)...);
|
1463
|
+
}
|
1464
|
+
SCSS
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
def test_function_map_splat_with_wrong_type
|
1468
|
+
assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was 12).") {render <<SCSS}
|
1469
|
+
@function foo($a, $b) {
|
1470
|
+
@return "a: \#{$a}, b: \#{$b}";
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
.foo {
|
1474
|
+
val: foo((1, 2)..., 12...);
|
1475
|
+
}
|
1476
|
+
SCSS
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
def test_function_keyword_splat_must_have_string_keys
|
1480
|
+
assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
|
1481
|
+
Variable keyword argument map must have string keys.
|
1482
|
+
12 is not a string in (12: 1).
|
1483
|
+
MESSAGE
|
1484
|
+
@function foo($a) {
|
1485
|
+
@return $a;
|
1486
|
+
}
|
1487
|
+
|
1488
|
+
.foo {val: foo((12: 1)...)}
|
1489
|
+
SCSS
|
1490
|
+
end
|
1491
|
+
|
1158
1492
|
## Interpolation
|
1159
1493
|
|
1160
1494
|
def test_basic_selector_interpolation
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 592302835
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 3
|
9
9
|
- 0
|
10
10
|
- alpha
|
11
|
-
-
|
12
|
-
version: 3.3.0.alpha.
|
11
|
+
- 255
|
12
|
+
version: 3.3.0.alpha.255
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Nathan Weizenbaum
|
@@ -19,7 +19,7 @@ autorequire:
|
|
19
19
|
bindir: bin
|
20
20
|
cert_chain: []
|
21
21
|
|
22
|
-
date: 2013-09-
|
22
|
+
date: 2013-09-18 00:00:00 -04:00
|
23
23
|
default_executable:
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
@@ -129,10 +129,10 @@ files:
|
|
129
129
|
- lib/sass/script/tree/map_literal.rb
|
130
130
|
- lib/sass/script/tree/node.rb
|
131
131
|
- lib/sass/script/tree/operation.rb
|
132
|
+
- lib/sass/script/tree/selector.rb
|
132
133
|
- lib/sass/script/tree/string_interpolation.rb
|
133
134
|
- lib/sass/script/tree/unary_operation.rb
|
134
135
|
- lib/sass/script/tree/variable.rb
|
135
|
-
- lib/sass/script/tree/selector.rb
|
136
136
|
- lib/sass/script/value.rb
|
137
137
|
- lib/sass/script/value/arg_list.rb
|
138
138
|
- lib/sass/script/value/base.rb
|
@@ -159,18 +159,18 @@ files:
|
|
159
159
|
- lib/sass/shared.rb
|
160
160
|
- lib/sass/supports.rb
|
161
161
|
- lib/sass/version.rb
|
162
|
-
- lib/sass/tree/charset_node.rb
|
163
162
|
- lib/sass/tree/comment_node.rb
|
163
|
+
- lib/sass/tree/charset_node.rb
|
164
|
+
- lib/sass/tree/debug_node.rb
|
164
165
|
- lib/sass/tree/content_node.rb
|
165
166
|
- lib/sass/tree/css_import_node.rb
|
166
|
-
- lib/sass/tree/debug_node.rb
|
167
167
|
- lib/sass/tree/directive_node.rb
|
168
168
|
- lib/sass/tree/each_node.rb
|
169
169
|
- lib/sass/tree/extend_node.rb
|
170
170
|
- lib/sass/tree/for_node.rb
|
171
171
|
- lib/sass/tree/function_node.rb
|
172
|
-
- lib/sass/tree/if_node.rb
|
173
172
|
- lib/sass/tree/import_node.rb
|
173
|
+
- lib/sass/tree/if_node.rb
|
174
174
|
- lib/sass/tree/media_node.rb
|
175
175
|
- lib/sass/tree/mixin_def_node.rb
|
176
176
|
- lib/sass/tree/mixin_node.rb
|
@@ -179,9 +179,10 @@ files:
|
|
179
179
|
- lib/sass/tree/return_node.rb
|
180
180
|
- lib/sass/tree/root_node.rb
|
181
181
|
- lib/sass/tree/rule_node.rb
|
182
|
-
- lib/sass/tree/supports_node.rb
|
183
182
|
- lib/sass/tree/trace_node.rb
|
183
|
+
- lib/sass/tree/supports_node.rb
|
184
184
|
- lib/sass/tree/variable_node.rb
|
185
|
+
- lib/sass/tree/warn_node.rb
|
185
186
|
- lib/sass/tree/visitors/base.rb
|
186
187
|
- lib/sass/tree/visitors/check_nesting.rb
|
187
188
|
- lib/sass/tree/visitors/convert.rb
|
@@ -191,7 +192,6 @@ files:
|
|
191
192
|
- lib/sass/tree/visitors/perform.rb
|
192
193
|
- lib/sass/tree/visitors/set_options.rb
|
193
194
|
- lib/sass/tree/visitors/to_css.rb
|
194
|
-
- lib/sass/tree/warn_node.rb
|
195
195
|
- lib/sass/tree/while_node.rb
|
196
196
|
- lib/sass/tree/at_root_node.rb
|
197
197
|
- lib/sass/source/map.rb
|