sass 3.4.21 → 3.4.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +122 -0
- data/MIT-LICENSE +1 -1
- data/README.md +15 -9
- data/Rakefile +71 -18
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass.rb +7 -0
- data/lib/sass/callbacks.rb +2 -2
- data/lib/sass/css.rb +9 -8
- data/lib/sass/engine.rb +14 -8
- data/lib/sass/environment.rb +8 -1
- data/lib/sass/error.rb +5 -5
- data/lib/sass/exec/sass_convert.rb +14 -2
- data/lib/sass/exec/sass_scss.rb +2 -25
- data/lib/sass/features.rb +2 -2
- data/lib/sass/importers/filesystem.rb +6 -3
- data/lib/sass/plugin/compiler.rb +15 -7
- data/lib/sass/script/css_variable_warning.rb +52 -0
- data/lib/sass/script/functions.rb +5 -5
- data/lib/sass/script/lexer.rb +3 -1
- data/lib/sass/script/parser.rb +67 -15
- data/lib/sass/script/tree/funcall.rb +10 -3
- data/lib/sass/script/tree/node.rb +8 -0
- data/lib/sass/script/tree/operation.rb +7 -0
- data/lib/sass/script/value/base.rb +1 -0
- data/lib/sass/script/value/color.rb +6 -4
- data/lib/sass/script/value/helpers.rb +2 -2
- data/lib/sass/script/value/number.rb +5 -5
- data/lib/sass/scss.rb +0 -2
- data/lib/sass/scss/css_parser.rb +1 -2
- data/lib/sass/scss/parser.rb +20 -10
- data/lib/sass/scss/rx.rb +2 -2
- data/lib/sass/scss/static_parser.rb +11 -13
- data/lib/sass/selector/pseudo.rb +1 -1
- data/lib/sass/selector/sequence.rb +2 -2
- data/lib/sass/selector/simple_sequence.rb +4 -4
- data/lib/sass/stack.rb +2 -2
- data/lib/sass/tree/function_node.rb +1 -1
- data/lib/sass/tree/node.rb +2 -0
- data/lib/sass/tree/visitors/check_nesting.rb +2 -0
- data/lib/sass/tree/visitors/convert.rb +8 -7
- data/lib/sass/tree/visitors/perform.rb +4 -2
- data/lib/sass/tree/visitors/to_css.rb +10 -10
- data/lib/sass/util.rb +8 -7
- data/test/sass-spec.yml +3 -0
- data/test/sass/compiler_test.rb +1 -1
- data/test/sass/css_variable_test.rb +132 -0
- data/test/sass/exec_test.rb +10 -0
- data/test/sass/script_test.rb +1 -1
- data/test/sass/scss/scss_test.rb +10 -0
- metadata +79 -77
- data/lib/sass/scss/script_lexer.rb +0 -15
- data/lib/sass/scss/script_parser.rb +0 -25
@@ -128,12 +128,15 @@ module Sass::Script::Tree
|
|
128
128
|
splat = Sass::Tree::Visitors::Perform.perform_splat(
|
129
129
|
@splat, keywords, @kwarg_splat, environment)
|
130
130
|
if (fn = environment.function(@name))
|
131
|
+
css_variable_warning.warn! if css_variable_warning
|
131
132
|
return without_original(perform_sass_fn(fn, args, splat, environment))
|
132
133
|
end
|
133
134
|
|
134
135
|
args = construct_ruby_args(ruby_name, args, splat, environment)
|
135
136
|
|
136
137
|
if Sass::Script::Functions.callable?(ruby_name)
|
138
|
+
css_variable_warning.warn! if css_variable_warning
|
139
|
+
|
137
140
|
local_environment = Sass::Environment.new(environment.global_env, environment.options)
|
138
141
|
local_environment.caller = Sass::ReadOnlyEnvironment.new(environment, environment.options)
|
139
142
|
result = opts(Sass::Script::Functions::EvaluationContext.new(
|
@@ -208,7 +211,7 @@ module Sass::Script::Tree
|
|
208
211
|
|
209
212
|
argnames = signature.args[args.size..-1] || []
|
210
213
|
deprecated_argnames = (signature.deprecated && signature.deprecated[args.size..-1]) || []
|
211
|
-
args
|
214
|
+
args += argnames.zip(deprecated_argnames).map do |(argname, deprecated_argname)|
|
212
215
|
if keywords.has_key?(argname)
|
213
216
|
keywords.delete(argname)
|
214
217
|
elsif deprecated_argname && keywords.has_key?(deprecated_argname)
|
@@ -296,8 +299,12 @@ module Sass::Script::Tree
|
|
296
299
|
message = "wrong number of arguments (#{given} for #{expected})"
|
297
300
|
end
|
298
301
|
end
|
299
|
-
elsif
|
300
|
-
|
302
|
+
elsif (md = /^wrong number of arguments \(given (\d+), expected (\d+)\)/.match(e.message)) &&
|
303
|
+
e.backtrace[0] =~ /:in `#{ruby_name}'$/
|
304
|
+
# Handle ruby 2.3 error formatting
|
305
|
+
message = "wrong number of arguments (#{md[1]} for #{md[2]})"
|
306
|
+
elsif e.message =~ /^wrong number of arguments/ &&
|
307
|
+
e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
|
301
308
|
raise e
|
302
309
|
end
|
303
310
|
raise Sass::SyntaxError.new("#{message} for `#{name}'")
|
@@ -23,6 +23,14 @@ module Sass::Script::Tree
|
|
23
23
|
# @return [String]
|
24
24
|
attr_accessor :filename
|
25
25
|
|
26
|
+
# The warning that this node should emit if it executes in a way that's not
|
27
|
+
# safe for a CSS variable value.
|
28
|
+
#
|
29
|
+
# This is `nil` if this is not in a CSS variable value.
|
30
|
+
#
|
31
|
+
# @return [Sass::Script::CssVariableWarning]
|
32
|
+
attr_accessor :css_variable_warning
|
33
|
+
|
26
34
|
# Sets the options hash for this node,
|
27
35
|
# as well as for all child nodes.
|
28
36
|
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
|
@@ -78,6 +78,13 @@ module Sass::Script::Tree
|
|
78
78
|
"Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
|
79
79
|
end
|
80
80
|
|
81
|
+
if css_variable_warning && @operator == :div &&
|
82
|
+
!(value1.is_a?(Sass::Script::Value::Number) && value1.original &&
|
83
|
+
value2.is_a?(Sass::Script::Value::Number) && value2.original) &&
|
84
|
+
!(value1.is_a?(Sass::Script::Value::String) && value2.is_a?(Sass::Script::Value::String))
|
85
|
+
css_variable_warning.warn!
|
86
|
+
end
|
87
|
+
|
81
88
|
begin
|
82
89
|
result = opts(value1.send(@operator, value2))
|
83
90
|
rescue NoMethodError => e
|
@@ -25,7 +25,8 @@ module Sass::Script::Value
|
|
25
25
|
rgba
|
26
26
|
end
|
27
27
|
|
28
|
-
ALTERNATE_COLOR_NAMES = Sass::Util.map_vals(
|
28
|
+
ALTERNATE_COLOR_NAMES = Sass::Util.map_vals(
|
29
|
+
{
|
29
30
|
'aqua' => 0x00FFFFFF,
|
30
31
|
'darkgrey' => 0xA9A9A9FF,
|
31
32
|
'darkslategrey' => 0x2F4F4FFF,
|
@@ -35,10 +36,11 @@ module Sass::Script::Value
|
|
35
36
|
'lightgrey' => 0xD3D3D3FF,
|
36
37
|
'lightslategrey' => 0x778899FF,
|
37
38
|
'slategrey' => 0x708090FF,
|
38
|
-
|
39
|
+
}, &method(:int_to_rgba))
|
39
40
|
|
40
41
|
# A hash from color names to `[red, green, blue]` value arrays.
|
41
|
-
COLOR_NAMES = Sass::Util.map_vals(
|
42
|
+
COLOR_NAMES = Sass::Util.map_vals(
|
43
|
+
{
|
42
44
|
'aliceblue' => 0xF0F8FFFF,
|
43
45
|
'antiquewhite' => 0xFAEBD7FF,
|
44
46
|
'aquamarine' => 0x7FFFD4FF,
|
@@ -179,7 +181,7 @@ module Sass::Script::Value
|
|
179
181
|
'whitesmoke' => 0xF5F5F5FF,
|
180
182
|
'yellow' => 0xFFFF00FF,
|
181
183
|
'yellowgreen' => 0x9ACD32FF
|
182
|
-
|
184
|
+
}, &method(:int_to_rgba))
|
183
185
|
|
184
186
|
# A hash from `[red, green, blue, alpha]` value arrays to color names.
|
185
187
|
COLOR_NAMES_REVERSE = COLOR_NAMES.invert.freeze
|
@@ -252,14 +252,14 @@ module Sass::Script::Value
|
|
252
252
|
def parse_unit_string(unit_string)
|
253
253
|
denominator_units = numerator_units = Sass::Script::Value::Number::NO_UNITS
|
254
254
|
return numerator_units, denominator_units unless unit_string && unit_string.length > 0
|
255
|
-
num_over_denominator = unit_string.split(
|
255
|
+
num_over_denominator = unit_string.split(%r{ */ *})
|
256
256
|
unless (1..2).include?(num_over_denominator.size)
|
257
257
|
raise ArgumentError.new("Malformed unit string: #{unit_string}")
|
258
258
|
end
|
259
259
|
numerator_units = num_over_denominator[0].split(/ *\* */)
|
260
260
|
denominator_units = (num_over_denominator[1] || "").split(/ *\* */)
|
261
261
|
[[numerator_units, "numerator"], [denominator_units, "denominator"]].each do |units, name|
|
262
|
-
if unit_string =~
|
262
|
+
if unit_string =~ %r{/} && units.size == 0
|
263
263
|
raise ArgumentError.new("Malformed unit string: #{unit_string}")
|
264
264
|
end
|
265
265
|
if units.any? {|unit| unit !~ VALID_UNIT}
|
@@ -71,6 +71,7 @@ module Sass::Script::Value
|
|
71
71
|
super(value)
|
72
72
|
@numerator_units = numerator_units
|
73
73
|
@denominator_units = denominator_units
|
74
|
+
@options = nil
|
74
75
|
normalize!
|
75
76
|
end
|
76
77
|
|
@@ -469,11 +470,10 @@ module Sass::Script::Value
|
|
469
470
|
sans_common_units(@numerator_units, @denominator_units)
|
470
471
|
|
471
472
|
@denominator_units.each_with_index do |d, i|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
end
|
473
|
+
next unless convertable?(d) && (u = @numerator_units.find(&method(:convertable?)))
|
474
|
+
@value /= conversion_factor(d, u)
|
475
|
+
@denominator_units.delete_at(i)
|
476
|
+
@numerator_units.delete_at(@numerator_units.index(u))
|
477
477
|
end
|
478
478
|
end
|
479
479
|
|
data/lib/sass/scss.rb
CHANGED
data/lib/sass/scss/css_parser.rb
CHANGED
@@ -50,8 +50,7 @@ module Sass
|
|
50
50
|
block(node(Sass::Tree::KeyframeRuleNode.new(selector.strip), start_pos), :ruleset)
|
51
51
|
end
|
52
52
|
|
53
|
-
@sass_script_parser =
|
54
|
-
@sass_script_parser.send(:include, ScriptParser)
|
53
|
+
@sass_script_parser = Sass::Script::CssParser
|
55
54
|
end
|
56
55
|
end
|
57
56
|
end
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -28,6 +28,8 @@ module Sass
|
|
28
28
|
@line = line
|
29
29
|
@offset = offset
|
30
30
|
@strs = []
|
31
|
+
@expected = nil
|
32
|
+
@throw_error = false
|
31
33
|
end
|
32
34
|
|
33
35
|
# Parses an SCSS document.
|
@@ -509,7 +511,7 @@ module Sass
|
|
509
511
|
|
510
512
|
def moz_document_function
|
511
513
|
val = interp_uri || _interp_string(:url_prefix) ||
|
512
|
-
_interp_string(:domain) || function(
|
514
|
+
_interp_string(:domain) || function(false) || interpolation
|
513
515
|
return unless val
|
514
516
|
ss
|
515
517
|
val
|
@@ -638,7 +640,7 @@ module Sass
|
|
638
640
|
# are disallowed by the CSS spec,
|
639
641
|
# but they're included here for compatibility
|
640
642
|
# with some proprietary MS properties
|
641
|
-
str {ss if tok(
|
643
|
+
str {ss if tok(%r{[/,:.=]})}
|
642
644
|
end
|
643
645
|
|
644
646
|
def ruleset
|
@@ -764,7 +766,7 @@ module Sass
|
|
764
766
|
value_start_pos = source_position
|
765
767
|
value = nil
|
766
768
|
error = catch_error do
|
767
|
-
value = value!
|
769
|
+
value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
|
768
770
|
if tok?(/\{/)
|
769
771
|
# Properties that are ambiguous with selectors can't have additional
|
770
772
|
# properties nested beneath them.
|
@@ -857,7 +859,7 @@ module Sass
|
|
857
859
|
tok!(/:/)
|
858
860
|
ss
|
859
861
|
value_start_pos = source_position
|
860
|
-
value = value!
|
862
|
+
value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
|
861
863
|
value_end_pos = source_position
|
862
864
|
ss
|
863
865
|
require_block = tok?(/\{/)
|
@@ -871,7 +873,7 @@ module Sass
|
|
871
873
|
nested_properties! node
|
872
874
|
end
|
873
875
|
|
874
|
-
def value!
|
876
|
+
def value!(css_variable = false)
|
875
877
|
if tok?(/\{/)
|
876
878
|
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
|
877
879
|
str.line = source_position.line
|
@@ -891,10 +893,19 @@ module Sass
|
|
891
893
|
str.source_range = range(start_pos)
|
892
894
|
return str
|
893
895
|
end
|
894
|
-
|
896
|
+
|
897
|
+
sass_script(:parse, css_variable)
|
895
898
|
end
|
896
899
|
|
897
900
|
def nested_properties!(node)
|
901
|
+
if node.name.first.is_a?(String) && node.name.first.start_with?("--")
|
902
|
+
Sass::Util.sass_warn(<<WARNING)
|
903
|
+
DEPRECATION WARNING on line #{@line}#{" of #{@filename}" if @filename}:
|
904
|
+
Sass 3.6 will change the way CSS variables are parsed. Instead of being parsed as
|
905
|
+
normal properties, they will not allow any Sass-specific behavior other than \#{}.
|
906
|
+
WARNING
|
907
|
+
end
|
908
|
+
|
898
909
|
@expected = 'expression (e.g. 1px, bold) or "{"'
|
899
910
|
block(node, :property)
|
900
911
|
end
|
@@ -1027,8 +1038,7 @@ module Sass
|
|
1027
1038
|
node
|
1028
1039
|
end
|
1029
1040
|
|
1030
|
-
@sass_script_parser =
|
1031
|
-
@sass_script_parser.send(:include, ScriptParser)
|
1041
|
+
@sass_script_parser = Sass::Script::Parser
|
1032
1042
|
|
1033
1043
|
class << self
|
1034
1044
|
# @private
|
@@ -1037,7 +1047,7 @@ module Sass
|
|
1037
1047
|
|
1038
1048
|
def sass_script(*args)
|
1039
1049
|
parser = self.class.sass_script_parser.new(@scanner, @line, @offset,
|
1040
|
-
|
1050
|
+
:filename => @filename, :importer => @importer, :allow_extra_text => true)
|
1041
1051
|
result = parser.send(*args)
|
1042
1052
|
unless @strs.empty?
|
1043
1053
|
# Convert to CSS manually so that comments are ignored.
|
@@ -1102,7 +1112,7 @@ module Sass
|
|
1102
1112
|
|
1103
1113
|
unless name
|
1104
1114
|
# Display basic regexps as plain old strings
|
1105
|
-
source = rx.source.gsub(
|
1115
|
+
source = rx.source.gsub(%r{\\/}, '/')
|
1106
1116
|
string = rx.source.gsub(/\\(.)/, '\1')
|
1107
1117
|
name = source == Regexp.escape(string) ? string.inspect : rx.inspect
|
1108
1118
|
end
|
data/lib/sass/scss/rx.rb
CHANGED
@@ -32,7 +32,7 @@ module Sass
|
|
32
32
|
# @return [String] The escaped character
|
33
33
|
# @private
|
34
34
|
def self.escape_char(c)
|
35
|
-
return "\\%06x" % (Sass::Util.ord(c)) unless c =~
|
35
|
+
return "\\%06x" % (Sass::Util.ord(c)) unless c =~ %r{[ -/:-~]}
|
36
36
|
"\\#{c}"
|
37
37
|
end
|
38
38
|
|
@@ -133,7 +133,7 @@ module Sass
|
|
133
133
|
STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
|
134
134
|
|
135
135
|
STATIC_COMPONENT = /#{IDENT}|#{STRING_NOINTERP}|#{HEXCOLOR}|[+-]?#{NUMBER}|\!important/i
|
136
|
-
STATIC_VALUE =
|
136
|
+
STATIC_VALUE = %r(#{STATIC_COMPONENT}(\s*[\s,/]\s*#{STATIC_COMPONENT})*([;}]))i
|
137
137
|
STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){1,50}([{])/i
|
138
138
|
end
|
139
139
|
end
|
@@ -62,7 +62,7 @@ module Sass
|
|
62
62
|
private
|
63
63
|
|
64
64
|
def moz_document_function
|
65
|
-
val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function(
|
65
|
+
val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function(false)
|
66
66
|
return unless val
|
67
67
|
ss
|
68
68
|
[val]
|
@@ -78,7 +78,7 @@ module Sass
|
|
78
78
|
def use_css_import?; true; end
|
79
79
|
|
80
80
|
def special_directive(name, start_pos)
|
81
|
-
return unless %w
|
81
|
+
return unless %w(media import charset -moz-document).include?(name)
|
82
82
|
super
|
83
83
|
end
|
84
84
|
|
@@ -89,13 +89,12 @@ module Sass
|
|
89
89
|
ws = ''
|
90
90
|
while tok(/,/)
|
91
91
|
ws << str {ss}
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
ws = ''
|
92
|
+
next unless (sel = selector)
|
93
|
+
selectors << sel
|
94
|
+
if ws.include?("\n")
|
95
|
+
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
|
98
96
|
end
|
97
|
+
ws = ''
|
99
98
|
end
|
100
99
|
Selector::CommaSequence.new(selectors)
|
101
100
|
end
|
@@ -140,11 +139,11 @@ MESSAGE
|
|
140
139
|
end
|
141
140
|
|
142
141
|
def reference_combinator
|
143
|
-
return unless tok(
|
142
|
+
return unless tok(%r{/})
|
144
143
|
res = '/'
|
145
144
|
ns, name = expr!(:qualified_name)
|
146
145
|
res << ns << '|' if ns
|
147
|
-
res << name << tok!(
|
146
|
+
res << name << tok!(%r{/})
|
148
147
|
res
|
149
148
|
end
|
150
149
|
|
@@ -266,9 +265,9 @@ MESSAGE
|
|
266
265
|
return ns, name
|
267
266
|
end
|
268
267
|
|
269
|
-
SELECTOR_PSEUDO_CLASSES = %w
|
268
|
+
SELECTOR_PSEUDO_CLASSES = %w(not matches current any has host host-context).to_set
|
270
269
|
|
271
|
-
PREFIXED_SELECTOR_PSEUDO_CLASSES = %w
|
270
|
+
PREFIXED_SELECTOR_PSEUDO_CLASSES = %w(nth-child nth-last-child).to_set
|
272
271
|
|
273
272
|
def pseudo
|
274
273
|
s = tok(/::?/)
|
@@ -362,7 +361,6 @@ MESSAGE
|
|
362
361
|
end
|
363
362
|
|
364
363
|
@sass_script_parser = Class.new(Sass::Script::CssParser)
|
365
|
-
@sass_script_parser.send(:include, ScriptParser)
|
366
364
|
end
|
367
365
|
end
|
368
366
|
end
|
data/lib/sass/selector/pseudo.rb
CHANGED
@@ -9,7 +9,7 @@ module Sass
|
|
9
9
|
# selectors.
|
10
10
|
#
|
11
11
|
# @return [Set<String>]
|
12
|
-
ACTUALLY_ELEMENTS = %w
|
12
|
+
ACTUALLY_ELEMENTS = %w(after before first-line first-letter).to_set
|
13
13
|
|
14
14
|
# Like \{#type}, but returns the type of selector this looks like, rather
|
15
15
|
# than the type it is semantically. This only differs from type for
|
@@ -395,10 +395,10 @@ module Sass
|
|
395
395
|
([merged, '+'] if merged)
|
396
396
|
].compact
|
397
397
|
end
|
398
|
-
elsif op1 == '>' && %w
|
398
|
+
elsif op1 == '>' && %w(~ +).include?(op2)
|
399
399
|
res.unshift sel2, op2
|
400
400
|
seq1.push sel1, op1
|
401
|
-
elsif op2 == '>' && %w
|
401
|
+
elsif op2 == '>' && %w(~ +).include?(op1)
|
402
402
|
res.unshift sel1, op1
|
403
403
|
seq2.push sel2, op2
|
404
404
|
elsif op1 == op2
|
@@ -88,7 +88,7 @@ module Sass
|
|
88
88
|
def resolve_parent_refs(super_cseq)
|
89
89
|
resolved_members = @members.map do |sel|
|
90
90
|
next sel unless sel.is_a?(Pseudo) && sel.selector
|
91
|
-
sel.with_selector(sel.selector.resolve_parent_refs(super_cseq,
|
91
|
+
sel.with_selector(sel.selector.resolve_parent_refs(super_cseq, false))
|
92
92
|
end.flatten
|
93
93
|
|
94
94
|
# Parent selector only appears as the first selector in the sequence
|
@@ -161,7 +161,7 @@ module Sass
|
|
161
161
|
members = self.members.map do |sel|
|
162
162
|
next sel unless sel.is_a?(Pseudo) && sel.selector
|
163
163
|
next sel if seen.include?([sel])
|
164
|
-
extended = sel.selector.do_extend(extends, parent_directives, replace, seen,
|
164
|
+
extended = sel.selector.do_extend(extends, parent_directives, replace, seen, false)
|
165
165
|
next sel if extended == sel.selector
|
166
166
|
extended.members.reject! {|seq| seq.has_placeholder?}
|
167
167
|
|
@@ -204,7 +204,7 @@ module Sass
|
|
204
204
|
groups.map! do |sels, seq|
|
205
205
|
next [] if seen.include?(sels)
|
206
206
|
seq.do_extend(
|
207
|
-
extends, parent_directives,
|
207
|
+
extends, parent_directives, false, seen_with_pseudo_selectors + [sels], false)
|
208
208
|
end
|
209
209
|
groups.flatten!
|
210
210
|
|
@@ -260,7 +260,7 @@ module Sass
|
|
260
260
|
|
261
261
|
# Some psuedo-selectors can be subselectors of non-pseudo selectors.
|
262
262
|
# Pull those out here so we can efficiently check against them below.
|
263
|
-
their_subselector_pseudos = %w
|
263
|
+
their_subselector_pseudos = %w(matches any nth-child nth-last-child).
|
264
264
|
map {|name| their_spcs[name] || []}.flatten
|
265
265
|
|
266
266
|
# If `self`'s non-pseudo simple selectors aren't a subset of `their_sseq`'s,
|
data/lib/sass/stack.rb
CHANGED
@@ -101,8 +101,8 @@ module Sass
|
|
101
101
|
def to_s
|
102
102
|
Sass::Util.enum_with_index(Sass::Util.enum_cons(frames.reverse + [nil], 2)).
|
103
103
|
map do |(frame, caller), i|
|
104
|
-
"#{i == 0 ?
|
105
|
-
" of #{frame.filename ||
|
104
|
+
"#{i == 0 ? 'on' : 'from'} line #{frame.line}" +
|
105
|
+
" of #{frame.filename || 'an unknown file'}" +
|
106
106
|
(caller && caller.name ? ", in `#{caller.name}'" : "")
|
107
107
|
end.join("\n")
|
108
108
|
end
|