sass 3.4.19 → 3.4.20
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.
- 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
|