sass 3.4.19 → 3.4.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.yardopts +2 -0
- data/Rakefile +10 -3
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/extra/update_watch.rb +1 -1
- data/lib/sass/css.rb +1 -1
- data/lib/sass/engine.rb +36 -10
- data/lib/sass/exec/base.rb +1 -1
- data/lib/sass/importers/base.rb +1 -1
- data/lib/sass/plugin/configuration.rb +37 -21
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/rails.rb +1 -1
- data/lib/sass/script/functions.rb +9 -2
- data/lib/sass/script/lexer.rb +7 -0
- data/lib/sass/script/parser.rb +154 -17
- data/lib/sass/script/tree/interpolation.rb +109 -4
- data/lib/sass/script/tree/string_interpolation.rb +6 -0
- data/lib/sass/script/value/number.rb +4 -0
- data/lib/sass/script/value/string.rb +37 -1
- data/lib/sass/scss/parser.rb +33 -15
- data/lib/sass/selector/simple_sequence.rb +1 -1
- data/lib/sass/tree/css_import_node.rb +9 -1
- data/lib/sass/tree/visitors/convert.rb +1 -0
- data/lib/sass/tree/visitors/perform.rb +3 -0
- data/lib/sass/tree/visitors/to_css.rb +3 -2
- data/lib/sass/util.rb +4 -3
- data/test/sass/conversion_test.rb +8 -0
- data/test/sass/engine_test.rb +28 -0
- data/test/sass/functions_test.rb +3 -0
- data/test/sass/script_conversion_test.rb +27 -26
- data/test/sass/script_test.rb +197 -37
- data/test/sass/scss/scss_test.rb +19 -0
- data/test/sass/source_map_test.rb +130 -64
- data/test/sass/util_test.rb +11 -0
- data/test/test_helper.rb +4 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZmQ4ODgyYzJhYWQzYjhkN2YwM2MxNTUxNDU1ZmUwZmRjMTdjNzc0ZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjBlMTNlNzkwYjBjMzQ5MzUxNGM1OTliZTdhNGE1ZTMzMTI3NmM1Yg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YTQ5ZWU3MWJkYTU3OTk3YTgzNjVlMzQzZDljYzg0NTVlM2U1ZGE0YWI1YzQ0
|
10
|
+
NmE3YjE4OWIzOTRlMDdkY2Q2OWY5ZTRjYjdmODc1OGU4NGRiMmUzZjNiMTIx
|
11
|
+
MWUxMDYyOGY2OWE0MmJhMThmNGRkNzNjZDJjMmM0ODk1NjA2YTY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Nzg1YTgxNTdmOWI3MjQxOGYwYWQzZTM4OTE3Mjk4NjAzNzRiYTYxNmRmOTIx
|
14
|
+
NDgwODRiNzQ4ZGIyZDFjMTZmNTUwMzM5NTNjMmZmN2Y5OGYzNTcyOTRhOTU1
|
15
|
+
NmE1NjY1NTVlYzZhMzUyMjE0ZTFkNTQ4MTNiODUwM2E3YzUzNDk=
|
data/.yardopts
CHANGED
data/Rakefile
CHANGED
@@ -23,7 +23,14 @@ end
|
|
23
23
|
|
24
24
|
# ----- Code Style Enforcement -----
|
25
25
|
|
26
|
-
|
26
|
+
version = RUBY_VERSION.split(".").map {|n| n.to_i}
|
27
|
+
|
28
|
+
# TODO: Run Rubocop on Ruby 2.2+ when it's supported. See
|
29
|
+
# https://github.com/sass/sass/pull/1805.
|
30
|
+
if (version[0] > 1 || (version[0] == 1 && version[1] > 8)) &&
|
31
|
+
(version[0] < 2 || (version[0] == 2 && version[1] < 2)) &&
|
32
|
+
(ENV.has_key?("RUBOCOP") && ENV["RUBOCOP"] == "true" ||
|
33
|
+
!(ENV.has_key?("RUBOCOP") || ENV.has_key?("TEST")))
|
27
34
|
require 'rubocop/rake_task'
|
28
35
|
Rubocop::RakeTask.new do |t|
|
29
36
|
t.patterns = FileList["lib/**/*"]
|
@@ -98,7 +105,7 @@ end
|
|
98
105
|
|
99
106
|
desc "Install Sass as a gem. Use SUDO=1 to install with sudo."
|
100
107
|
task :install => [:package] do
|
101
|
-
gem = RUBY_PLATFORM =~ /java/ ? 'jgem' : 'gem'
|
108
|
+
gem = RUBY_PLATFORM =~ /java/ ? 'jgem' : 'gem'
|
102
109
|
sh %{#{'sudo ' if ENV["SUDO"]}#{gem} install --no-ri pkg/sass-#{get_version}}
|
103
110
|
end
|
104
111
|
|
@@ -310,7 +317,7 @@ END
|
|
310
317
|
file = File.read(scope("test/sass/templates/#{file || 'complex'}.sass"))
|
311
318
|
result = RubyProf.profile { times.times { Sass::Engine.new(file).render } }
|
312
319
|
|
313
|
-
RubyProf.const_get("#{(ENV['OUTPUT'] || 'Flat').capitalize}Printer").new(result).print
|
320
|
+
RubyProf.const_get("#{(ENV['OUTPUT'] || 'Flat').capitalize}Printer").new(result).print
|
314
321
|
end
|
315
322
|
rescue LoadError; end
|
316
323
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.4.
|
1
|
+
3.4.20
|
data/VERSION_DATE
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
09 December 2015 23:08:32 UTC
|
data/extra/update_watch.rb
CHANGED
@@ -7,7 +7,7 @@ enable :lock
|
|
7
7
|
Dir.chdir(File.dirname(__FILE__) + "/..")
|
8
8
|
|
9
9
|
post "/" do
|
10
|
-
puts "
|
10
|
+
puts "Received payload!"
|
11
11
|
puts "Rev: #{`git name-rev HEAD`.strip}"
|
12
12
|
system %{rake handle_update --trace REF=#{JSON.parse(params["payload"])["ref"].inspect}}
|
13
13
|
end
|
data/lib/sass/css.rb
CHANGED
data/lib/sass/engine.rb
CHANGED
@@ -229,7 +229,7 @@ module Sass
|
|
229
229
|
had_syntax = options[:syntax]
|
230
230
|
|
231
231
|
if had_syntax
|
232
|
-
# Use what was explicitly
|
232
|
+
# Use what was explicitly specified
|
233
233
|
elsif filename =~ /\.scss$/
|
234
234
|
options.merge!(:syntax => :scss)
|
235
235
|
elsif filename =~ /\.sass$/
|
@@ -776,7 +776,19 @@ WARNING
|
|
776
776
|
else
|
777
777
|
:normal
|
778
778
|
end
|
779
|
-
Tree::CommentNode.new(value, type)
|
779
|
+
comment = Tree::CommentNode.new(value, type)
|
780
|
+
comment.line = line.index
|
781
|
+
text = line.text.rstrip
|
782
|
+
if text.include?("\n")
|
783
|
+
end_offset = text.length - text.rindex("\n")
|
784
|
+
else
|
785
|
+
end_offset = to_parser_offset(line.offset + text.length)
|
786
|
+
end
|
787
|
+
comment.source_range = Sass::Source::Range.new(
|
788
|
+
Sass::Source::Position.new(@line, to_parser_offset(line.offset)),
|
789
|
+
Sass::Source::Position.new(@line + text.count("\n"), end_offset),
|
790
|
+
@options[:filename])
|
791
|
+
comment
|
780
792
|
else
|
781
793
|
Tree::RuleNode.new(parse_interp(line.text), full_line_range(line))
|
782
794
|
end
|
@@ -1013,12 +1025,21 @@ WARNING
|
|
1013
1025
|
end_pos = str.source_range.end_pos
|
1014
1026
|
node = Tree::CssImportNode.new(str)
|
1015
1027
|
else
|
1016
|
-
|
1028
|
+
supports_parser = Sass::SCSS::Parser.new(scanner,
|
1017
1029
|
@options[:filename], @options[:importer],
|
1018
1030
|
@line, str.source_range.end_pos.offset)
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1031
|
+
supports_condition = supports_parser.parse_supports_clause
|
1032
|
+
|
1033
|
+
if scanner.eos?
|
1034
|
+
node = Tree::CssImportNode.new(str, [], supports_condition)
|
1035
|
+
else
|
1036
|
+
media_parser = Sass::SCSS::Parser.new(scanner,
|
1037
|
+
@options[:filename], @options[:importer],
|
1038
|
+
@line, str.source_range.end_pos.offset)
|
1039
|
+
media = media_parser.parse_media_query_list
|
1040
|
+
end_pos = Sass::Source::Position.new(@line, media_parser.offset + 1)
|
1041
|
+
node = Tree::CssImportNode.new(str, media.to_a, supports_condition)
|
1042
|
+
end
|
1022
1043
|
end
|
1023
1044
|
|
1024
1045
|
node.source_range = Sass::Source::Range.new(
|
@@ -1180,10 +1201,15 @@ WARNING
|
|
1180
1201
|
res << "\\" * (escapes - 1) << '#{'
|
1181
1202
|
else
|
1182
1203
|
res << "\\" * [0, escapes - 1].max
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1204
|
+
if scan[1].include?("\n")
|
1205
|
+
line = line + scan[1].count("\n")
|
1206
|
+
offset = scan.matched_size - scan[1].rindex("\n")
|
1207
|
+
else
|
1208
|
+
offset += scan.matched_size
|
1209
|
+
end
|
1210
|
+
node = Script::Parser.new(scan, line, offset, options).parse_interpolated
|
1211
|
+
offset = node.source_range.end_pos.offset
|
1212
|
+
res << node
|
1187
1213
|
end
|
1188
1214
|
end
|
1189
1215
|
res << rest
|
data/lib/sass/exec/base.rb
CHANGED
@@ -149,7 +149,7 @@ module Sass::Exec
|
|
149
149
|
|
150
150
|
# Wraps the given string in terminal escapes
|
151
151
|
# causing it to have the given color.
|
152
|
-
# If terminal
|
152
|
+
# If terminal escapes aren't supported on this platform,
|
153
153
|
# just returns the string instead.
|
154
154
|
#
|
155
155
|
# @param color [Symbol] The name of the color to use.
|
data/lib/sass/importers/base.rb
CHANGED
@@ -90,7 +90,7 @@ module Sass
|
|
90
90
|
#
|
91
91
|
# @param uri [String] The URI of the file to check.
|
92
92
|
# Comes from a `:filename` option set on an engine returned by this importer.
|
93
|
-
# @param options [{Symbol =>
|
93
|
+
# @param options [{Symbol => Object}] Options for the Sass file
|
94
94
|
# containing the `@import` currently being checked.
|
95
95
|
# @return [Time, nil]
|
96
96
|
def mtime(uri, options)
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module Sass
|
2
2
|
module Plugin
|
3
|
-
# We keep configuration in its own self-contained file
|
4
|
-
#
|
5
|
-
#
|
3
|
+
# We keep configuration in its own self-contained file so that we can load
|
4
|
+
# it independently in Rails 3, where the full plugin stuff is lazy-loaded.
|
5
|
+
#
|
6
|
+
# Note that this is not guaranteed to be thread-safe. For guaranteed thread
|
7
|
+
# safety, use a separate {Sass::Plugin} for each thread.
|
6
8
|
module Configuration
|
7
9
|
# Returns the default options for a {Sass::Plugin::Compiler}.
|
8
10
|
#
|
@@ -85,33 +87,47 @@ module Sass
|
|
85
87
|
# See the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
|
86
88
|
# for details.
|
87
89
|
#
|
90
|
+
# Modifications to the returned array may not be persistent. Use {#add_template_location}
|
91
|
+
# and {#remove_template_location} instead.
|
92
|
+
#
|
88
93
|
# @return [Array<(String, String)>]
|
89
94
|
# An array of `[template_location, css_location]` pairs.
|
90
95
|
def template_location_array
|
91
|
-
|
92
|
-
normalize_template_location!
|
93
|
-
options[:template_location]
|
94
|
-
ensure
|
95
|
-
options[:template_location] = old_template_location
|
96
|
+
convert_template_location(options[:template_location], options[:css_location])
|
96
97
|
end
|
97
98
|
|
98
99
|
private
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
101
|
+
# Returns the given template location, as an array. If it's already an array,
|
102
|
+
# it is returned unmodified. Otherwise, a new array is created and returned.
|
103
|
+
#
|
104
|
+
# @param template_location [String, Array<(String, String)>]
|
105
|
+
# A single template location, or a pre-normalized array of template
|
106
|
+
# locations and CSS locations.
|
107
|
+
# @param css_location [String?]
|
108
|
+
# The location for compiled CSS files.
|
109
|
+
# @return [Array<(String, String)>]
|
110
|
+
# An array of `[template_location, css_location]` pairs.
|
111
|
+
def convert_template_location(template_location, css_location)
|
112
|
+
return template_location if template_location.is_a?(Array)
|
113
|
+
|
114
|
+
case template_location
|
115
|
+
when nil
|
116
|
+
if css_location
|
117
|
+
[[File.join(css_location, 'sass'), css_location]]
|
112
118
|
else
|
113
|
-
|
119
|
+
[]
|
114
120
|
end
|
121
|
+
when String
|
122
|
+
[[template_location, css_location]]
|
123
|
+
else
|
124
|
+
template_location.to_a
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def normalize_template_location!
|
129
|
+
options[:template_location] = convert_template_location(
|
130
|
+
options[:template_location], options[:css_location])
|
115
131
|
end
|
116
132
|
end
|
117
133
|
end
|
data/lib/sass/plugin/merb.rb
CHANGED
@@ -2,7 +2,7 @@ unless defined?(Sass::MERB_LOADED)
|
|
2
2
|
Sass::MERB_LOADED = true
|
3
3
|
|
4
4
|
module Sass::Plugin::Configuration
|
5
|
-
# Different default options in a m
|
5
|
+
# Different default options in a m environment.
|
6
6
|
def default_options
|
7
7
|
@default_options ||= begin
|
8
8
|
version = Merb::VERSION.split('.').map {|n| n.to_i}
|
data/lib/sass/plugin/rails.rb
CHANGED
@@ -2,7 +2,7 @@ unless defined?(Sass::RAILS_LOADED)
|
|
2
2
|
Sass::RAILS_LOADED = true
|
3
3
|
|
4
4
|
module Sass::Plugin::Configuration
|
5
|
-
# Different default options in a rails
|
5
|
+
# Different default options in a rails environment.
|
6
6
|
def default_options
|
7
7
|
return @default_options if @default_options
|
8
8
|
opts = {
|
@@ -123,7 +123,7 @@ module Sass::Script
|
|
123
123
|
# : Inserts `$insert` into `$string` at `$index`.
|
124
124
|
#
|
125
125
|
# \{#str_index str-index($string, $substring)}
|
126
|
-
# : Returns the index of the first
|
126
|
+
# : Returns the index of the first occurrence of `$substring` in `$string`.
|
127
127
|
#
|
128
128
|
# \{#str_slice str-slice($string, $start-at, [$end-at])}
|
129
129
|
# : Extracts a substring from `$string`.
|
@@ -526,7 +526,11 @@ module Sass::Script
|
|
526
526
|
# @raise [ArgumentError] if value is not of the correct type.
|
527
527
|
def assert_type(value, type, name = nil)
|
528
528
|
klass = Sass::Script::Value.const_get(type)
|
529
|
-
|
529
|
+
if value.is_a?(klass)
|
530
|
+
value.check_deprecated_interp if type == :String
|
531
|
+
return
|
532
|
+
end
|
533
|
+
|
530
534
|
return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.value.empty?
|
531
535
|
err = "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
|
532
536
|
err = "$#{name.to_s.gsub('_', '-')}: " + err if name
|
@@ -1438,6 +1442,7 @@ MESSAGE
|
|
1438
1442
|
return string
|
1439
1443
|
end
|
1440
1444
|
|
1445
|
+
string.check_deprecated_interp
|
1441
1446
|
return string if string.type == :identifier
|
1442
1447
|
identifier(string.value)
|
1443
1448
|
end
|
@@ -1624,6 +1629,7 @@ MESSAGE
|
|
1624
1629
|
# @return [Sass::Script::Value::String] The unquoted string name of the
|
1625
1630
|
# value's type
|
1626
1631
|
def type_of(value)
|
1632
|
+
value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String)
|
1627
1633
|
identifier(value.class.name.gsub(/Sass::Script::Value::/, '').downcase)
|
1628
1634
|
end
|
1629
1635
|
declare :type_of, [:value]
|
@@ -2352,6 +2358,7 @@ MESSAGE
|
|
2352
2358
|
# @return [Sass::Script::Value::String] A representation of the value as
|
2353
2359
|
# it would be written in Sass.
|
2354
2360
|
def inspect(value)
|
2361
|
+
value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String)
|
2355
2362
|
unquoted_string(value.to_sass)
|
2356
2363
|
end
|
2357
2364
|
declare :inspect, [:value]
|
data/lib/sass/script/lexer.rb
CHANGED
@@ -179,6 +179,13 @@ module Sass
|
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
182
|
+
# Returns the given character.
|
183
|
+
#
|
184
|
+
# @return [String]
|
185
|
+
def char(pos = @scanner.pos)
|
186
|
+
@scanner.string[pos, 1]
|
187
|
+
end
|
188
|
+
|
182
189
|
# Returns the next token without moving the lexer forward.
|
183
190
|
#
|
184
191
|
# @return [Token] The next token
|
data/lib/sass/script/parser.rb
CHANGED
@@ -46,7 +46,8 @@ module Sass
|
|
46
46
|
expr = assert_expr :expr
|
47
47
|
assert_tok :end_interpolation
|
48
48
|
expr = Sass::Script::Tree::Interpolation.new(
|
49
|
-
nil, expr, nil, !:wb, !:wa,
|
49
|
+
nil, expr, nil, !:wb, !:wa, :warn_for_color => warn_for_color)
|
50
|
+
check_for_interpolation expr
|
50
51
|
expr.options = @options
|
51
52
|
node(expr, start_pos)
|
52
53
|
rescue Sass::SyntaxError => e
|
@@ -62,6 +63,7 @@ module Sass
|
|
62
63
|
expr = assert_expr :expr
|
63
64
|
assert_done
|
64
65
|
expr.options = @options
|
66
|
+
check_for_interpolation expr
|
65
67
|
expr
|
66
68
|
rescue Sass::SyntaxError => e
|
67
69
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -79,6 +81,7 @@ module Sass
|
|
79
81
|
expr = assert_expr :expr
|
80
82
|
assert_done
|
81
83
|
expr.options = @options
|
84
|
+
check_for_interpolation expr
|
82
85
|
expr
|
83
86
|
rescue Sass::SyntaxError => e
|
84
87
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -102,10 +105,26 @@ module Sass
|
|
102
105
|
end
|
103
106
|
assert_done
|
104
107
|
|
105
|
-
args.each
|
106
|
-
|
107
|
-
|
108
|
-
|
108
|
+
args.each do |a|
|
109
|
+
check_for_interpolation a
|
110
|
+
a.options = @options
|
111
|
+
end
|
112
|
+
|
113
|
+
keywords.each do |k, v|
|
114
|
+
check_for_interpolation v
|
115
|
+
v.options = @options
|
116
|
+
end
|
117
|
+
|
118
|
+
if splat
|
119
|
+
check_for_interpolation splat
|
120
|
+
splat.options = @options
|
121
|
+
end
|
122
|
+
|
123
|
+
if kwarg_splat
|
124
|
+
check_for_interpolation kwarg_splat
|
125
|
+
kwarg_splat.options = @options
|
126
|
+
end
|
127
|
+
|
109
128
|
return args, keywords, splat, kwarg_splat
|
110
129
|
rescue Sass::SyntaxError => e
|
111
130
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -122,10 +141,20 @@ module Sass
|
|
122
141
|
assert_done
|
123
142
|
|
124
143
|
args.each do |k, v|
|
144
|
+
check_for_interpolation k
|
125
145
|
k.options = @options
|
126
|
-
|
146
|
+
|
147
|
+
if v
|
148
|
+
check_for_interpolation v
|
149
|
+
v.options = @options
|
150
|
+
end
|
127
151
|
end
|
128
|
-
|
152
|
+
|
153
|
+
if splat
|
154
|
+
check_for_interpolation splat
|
155
|
+
splat.options = @options
|
156
|
+
end
|
157
|
+
|
129
158
|
return args, splat
|
130
159
|
rescue Sass::SyntaxError => e
|
131
160
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -142,10 +171,20 @@ module Sass
|
|
142
171
|
assert_done
|
143
172
|
|
144
173
|
args.each do |k, v|
|
174
|
+
check_for_interpolation k
|
145
175
|
k.options = @options
|
146
|
-
|
176
|
+
|
177
|
+
if v
|
178
|
+
check_for_interpolation v
|
179
|
+
v.options = @options
|
180
|
+
end
|
147
181
|
end
|
148
|
-
|
182
|
+
|
183
|
+
if splat
|
184
|
+
check_for_interpolation splat
|
185
|
+
splat.options = @options
|
186
|
+
end
|
187
|
+
|
149
188
|
return args, splat
|
150
189
|
rescue Sass::SyntaxError => e
|
151
190
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -165,6 +204,7 @@ module Sass
|
|
165
204
|
end
|
166
205
|
|
167
206
|
expr = assert_expr :funcall
|
207
|
+
check_for_interpolation expr
|
168
208
|
expr.options = @options
|
169
209
|
@lexer.unpeek!
|
170
210
|
expr
|
@@ -315,13 +355,25 @@ RUBY
|
|
315
355
|
|
316
356
|
production :equals, :interpolation, :single_eq
|
317
357
|
|
318
|
-
def try_op_before_interp(op, prev = nil)
|
358
|
+
def try_op_before_interp(op, prev = nil, after_interp = false)
|
319
359
|
return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
|
360
|
+
unary = !prev && !after_interp
|
320
361
|
wb = @lexer.whitespace?(op)
|
321
362
|
str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]),
|
322
363
|
op.source_range)
|
364
|
+
|
365
|
+
deprecation =
|
366
|
+
case op.type
|
367
|
+
when :comma; :potential
|
368
|
+
when :div, :single_eq; :none
|
369
|
+
when :plus; unary ? :none : :immediate
|
370
|
+
when :minus; @lexer.whitespace?(@lexer.peek) ? :immediate : :none
|
371
|
+
else; :immediate
|
372
|
+
end
|
373
|
+
|
323
374
|
interp = node(
|
324
|
-
Script::Tree::Interpolation.new(
|
375
|
+
Script::Tree::Interpolation.new(
|
376
|
+
prev, str, nil, wb, !:wa, :originally_text => true, :deprecation => deprecation),
|
325
377
|
(prev || str).source_range.start_pos)
|
326
378
|
interpolation(interp)
|
327
379
|
end
|
@@ -330,15 +382,25 @@ RUBY
|
|
330
382
|
return unless @lexer.after_interpolation?
|
331
383
|
op = try_toks(*ops)
|
332
384
|
return unless op
|
333
|
-
interp = try_op_before_interp(op, prev)
|
385
|
+
interp = try_op_before_interp(op, prev, :after_interp)
|
334
386
|
return interp if interp
|
335
387
|
|
336
388
|
wa = @lexer.whitespace?
|
337
389
|
str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]),
|
338
390
|
op.source_range)
|
339
391
|
str.line = @lexer.line
|
392
|
+
|
393
|
+
deprecation =
|
394
|
+
case op.type
|
395
|
+
when :comma; :potential
|
396
|
+
when :div, :single_eq; :none
|
397
|
+
when :minus; @lexer.whitespace?(op) ? :immediate : :none
|
398
|
+
else; :immediate
|
399
|
+
end
|
340
400
|
interp = node(
|
341
|
-
Script::Tree::Interpolation.new(
|
401
|
+
Script::Tree::Interpolation.new(
|
402
|
+
prev, str, assert_expr(name), !:wb, wa,
|
403
|
+
:originally_text => true, :deprecation => deprecation),
|
342
404
|
(prev || str).source_range.start_pos)
|
343
405
|
interp
|
344
406
|
end
|
@@ -347,16 +409,54 @@ RUBY
|
|
347
409
|
e = first
|
348
410
|
while (interp = try_tok(:begin_interpolation))
|
349
411
|
wb = @lexer.whitespace?(interp)
|
412
|
+
char_before = @lexer.char(interp.pos - 1)
|
350
413
|
mid = assert_expr :expr
|
351
414
|
assert_tok :end_interpolation
|
352
415
|
wa = @lexer.whitespace?
|
416
|
+
char_after = @lexer.char
|
417
|
+
|
418
|
+
after = space
|
419
|
+
before_deprecation = e.is_a?(Script::Tree::Interpolation) ? e.deprecation : :none
|
420
|
+
after_deprecation = after.is_a?(Script::Tree::Interpolation) ? after.deprecation : :none
|
421
|
+
|
422
|
+
deprecation =
|
423
|
+
if before_deprecation == :immediate || after_deprecation == :immediate ||
|
424
|
+
# Warn for #{foo}$var and #{foo}(1) but not #{$foo}1.
|
425
|
+
(after && !wa && char_after =~ /[$(]/) ||
|
426
|
+
# Warn for $var#{foo} and (a)#{foo} but not a#{foo}.
|
427
|
+
(e && !wb && is_unsafe_before?(e, char_before))
|
428
|
+
:immediate
|
429
|
+
else
|
430
|
+
:potential
|
431
|
+
end
|
432
|
+
|
353
433
|
e = node(
|
354
|
-
Script::Tree::Interpolation.new(e, mid,
|
355
|
-
(e ||
|
434
|
+
Script::Tree::Interpolation.new(e, mid, after, wb, wa, :deprecation => deprecation),
|
435
|
+
(e || interp).source_range.start_pos)
|
356
436
|
end
|
357
437
|
e
|
358
438
|
end
|
359
439
|
|
440
|
+
# Returns whether `expr` is unsafe to include before an interpolation.
|
441
|
+
#
|
442
|
+
# @param expr [Node] The expression to check.
|
443
|
+
# @param char_before [String] The character immediately before the
|
444
|
+
# interpolation being checked (and presumably the last character of
|
445
|
+
# `expr`).
|
446
|
+
# @return [Boolean]
|
447
|
+
def is_unsafe_before?(expr, char_before)
|
448
|
+
# If the previous expression is an identifier or number, it's safe
|
449
|
+
# unless it was wrapped in parentheses.
|
450
|
+
if expr.is_a?(Script::Tree::Literal) &&
|
451
|
+
(expr.value.is_a?(Script::Value::Number) ||
|
452
|
+
(expr.value.is_a?(Script::Value::String) && expr.value.type == :identifier))
|
453
|
+
return char_before == ')'
|
454
|
+
end
|
455
|
+
|
456
|
+
# Otherwise, it's only safe if it was another interpolation.
|
457
|
+
!expr.is_a?(Script::Tree::Interpolation)
|
458
|
+
end
|
459
|
+
|
360
460
|
def space
|
361
461
|
start_pos = source_position
|
362
462
|
e = or_expr
|
@@ -502,8 +602,9 @@ RUBY
|
|
502
602
|
mid = assert_expr :expr
|
503
603
|
assert_tok :end_interpolation
|
504
604
|
last = assert_expr(:special_fun)
|
505
|
-
node(
|
506
|
-
|
605
|
+
node(
|
606
|
+
Tree::Interpolation.new(str, mid, last, !:wb, !:wa),
|
607
|
+
first.source_range.start_pos)
|
507
608
|
end
|
508
609
|
|
509
610
|
def paren
|
@@ -538,6 +639,7 @@ RUBY
|
|
538
639
|
tok = try_tok(:number)
|
539
640
|
return selector unless tok
|
540
641
|
num = tok.value
|
642
|
+
num.options = @options
|
541
643
|
num.original = num.to_s
|
542
644
|
literal_node(num, tok.source_range.start_pos)
|
543
645
|
end
|
@@ -631,6 +733,41 @@ RUBY
|
|
631
733
|
node.filename = @options[:filename]
|
632
734
|
node
|
633
735
|
end
|
736
|
+
|
737
|
+
# Checks a script node for any immediately-deprecated interpolations, and
|
738
|
+
# emits warnings for them.
|
739
|
+
#
|
740
|
+
# @param node [Sass::Script::Tree::Node]
|
741
|
+
def check_for_interpolation(node)
|
742
|
+
nodes = [node]
|
743
|
+
until nodes.empty?
|
744
|
+
node = nodes.pop
|
745
|
+
unless node.is_a?(Sass::Script::Tree::Interpolation) &&
|
746
|
+
node.deprecation == :immediate
|
747
|
+
nodes.concat node.children
|
748
|
+
next
|
749
|
+
end
|
750
|
+
|
751
|
+
interpolation_deprecation(node)
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
# Emits a deprecation warning for an interpolation node.
|
756
|
+
#
|
757
|
+
# @param node [Sass::Script::Tree::Node]
|
758
|
+
def interpolation_deprecation(interpolation)
|
759
|
+
return if @options[:_convert]
|
760
|
+
location = "on line #{interpolation.line}"
|
761
|
+
location << " of #{interpolation.filename}" if interpolation.filename
|
762
|
+
Sass::Util.sass_warn <<WARNING
|
763
|
+
DEPRECATION WARNING #{location}: \#{} interpolation near operators will be simplified
|
764
|
+
in a future version of Sass. To preserve the current behavior, use quotes:
|
765
|
+
|
766
|
+
#{interpolation.to_quoted_equivalent.to_sass}
|
767
|
+
|
768
|
+
You can use the sass-convert command to automatically fix most cases.
|
769
|
+
WARNING
|
770
|
+
end
|
634
771
|
end
|
635
772
|
end
|
636
773
|
end
|