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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MWUyNTcwMjI2MDViMjQ1MjdkMTUzYjAyMzAwNDM4OGMyODUwNmQ3ZA==
4
+ ZmQ4ODgyYzJhYWQzYjhkN2YwM2MxNTUxNDU1ZmUwZmRjMTdjNzc0ZQ==
5
5
  data.tar.gz: !binary |-
6
- NDU0NjNmOTk2MzU2ODllMGRjMGMwYTU5OTNiOTk2YzYwNGRmYjg2MQ==
6
+ YjBlMTNlNzkwYjBjMzQ5MzUxNGM1OTliZTdhNGE1ZTMzMTI3NmM1Yg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODVlN2I2NjI5NGUyNjMyZjIyNGE4YzkwNmUwYzZhOThlMWE3OWM4N2U3MDMw
10
- OGNiODViMmRiMGZlZTA1MGZjNTUzYTg1MzY2YWRhOGM3MTdhZDNlMmVmODk5
11
- MTJmOGJhYWQ4YWMxNzRkN2JiNjJlNGYwMWJlZWFiNWY5MjIxYTY=
9
+ YTQ5ZWU3MWJkYTU3OTk3YTgzNjVlMzQzZDljYzg0NTVlM2U1ZGE0YWI1YzQ0
10
+ NmE3YjE4OWIzOTRlMDdkY2Q2OWY5ZTRjYjdmODc1OGU4NGRiMmUzZjNiMTIx
11
+ MWUxMDYyOGY2OWE0MmJhMThmNGRkNzNjZDJjMmM0ODk1NjA2YTY=
12
12
  data.tar.gz: !binary |-
13
- ZWY5NDhjODk1YThkMGE5OTcyMmQ5MTY3NWZlNDEzNWFjMDI0YWMxZDlmMWU4
14
- YjcwZWJiNDdkM2RlYmI5Njc1MTVmYzM0YzA5OGQ5N2UxNzA4YjUzMGQ5Nzk2
15
- YzNkYzVjYjI1NTBjMDdlODdkZjcwNmMzNWI0NzBkZTY4MjgyODU=
13
+ Nzg1YTgxNTdmOWI3MjQxOGYwYWQzZTM4OTE3Mjk4NjAzNzRiYTYxNmRmOTIx
14
+ NDgwODRiNzQ4ZGIyZDFjMTZmNTUwMzM5NTNjMmZmN2Y5OGYzNTcyOTRhOTU1
15
+ NmE1NjY1NTVlYzZhMzUyMjE0ZTFkNTQ4MTNiODUwM2E3YzUzNDk=
data/.yardopts CHANGED
@@ -9,3 +9,5 @@
9
9
  --protected
10
10
  --no-private
11
11
  --no-highlight
12
+ --tag comment
13
+ --hide-tag comment
data/Rakefile CHANGED
@@ -23,7 +23,14 @@ end
23
23
 
24
24
  # ----- Code Style Enforcement -----
25
25
 
26
- if RUBY_VERSION !~ /^(1\.8)/ && (ENV.has_key?("RUBOCOP") && ENV["RUBOCOP"] == "true" || !(ENV.has_key?("RUBOCOP") || ENV.has_key?("TEST")))
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.19
1
+ 3.4.20
data/VERSION_DATE CHANGED
@@ -1 +1 @@
1
- 10 October 2015 01:18:29 UTC
1
+ 09 December 2015 23:08:32 UTC
@@ -7,7 +7,7 @@ enable :lock
7
7
  Dir.chdir(File.dirname(__FILE__) + "/..")
8
8
 
9
9
  post "/" do
10
- puts "Recieved payload!"
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
@@ -31,7 +31,7 @@ module Sass
31
31
  template = template.read
32
32
  end
33
33
 
34
- @options = options.dup
34
+ @options = options.merge(:_convert => true)
35
35
  # Backwards compatibility
36
36
  @options[:old] = true if @options[:alternate] == false
37
37
  @template = template
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 specificed
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
- media_parser = Sass::SCSS::Parser.new(scanner,
1028
+ supports_parser = Sass::SCSS::Parser.new(scanner,
1017
1029
  @options[:filename], @options[:importer],
1018
1030
  @line, str.source_range.end_pos.offset)
1019
- media = media_parser.parse_media_query_list
1020
- end_pos = Sass::Source::Position.new(@line, media_parser.offset + 1)
1021
- node = Tree::CssImportNode.new(str, media.to_a)
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
- # Add 1 to emulate to_parser_offset.
1184
- res << Script::Parser.new(
1185
- scan, line, offset + scan.pos - scan.matched_size + 1, options).
1186
- parse_interpolated
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
@@ -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 esapes aren't supported on this platform,
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.
@@ -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 => Objet}] Options for the Sass file
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
- # so that we can load it independently in Rails 3,
5
- # where the full plugin stuff is lazy-loaded.
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
- old_template_location = options[:template_location]
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
- def normalize_template_location!
101
- return if options[:template_location].is_a?(Array)
102
- options[:template_location] =
103
- case options[:template_location]
104
- when nil
105
- if options[:css_location]
106
- [[File.join(options[:css_location], 'sass'), options[:css_location]]]
107
- else
108
- []
109
- end
110
- when String
111
- [[options[:template_location], options[:css_location]]]
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
- options[:template_location].to_a
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
@@ -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 envirionment.
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}
@@ -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 envirionment.
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 occurance of `$substring` in `$string`.
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
- return if value.is_a?(klass)
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]
@@ -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
@@ -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, !:originally_text, warn_for_color)
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 {|a| a.options = @options}
106
- keywords.each {|k, v| v.options = @options}
107
- splat.options = @options if splat
108
- kwarg_splat.options = @options if kwarg_splat
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
- v.options = @options if v
146
+
147
+ if v
148
+ check_for_interpolation v
149
+ v.options = @options
150
+ end
127
151
  end
128
- splat.options = @options if splat
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
- v.options = @options if v
176
+
177
+ if v
178
+ check_for_interpolation v
179
+ v.options = @options
180
+ end
147
181
  end
148
- splat.options = @options if splat
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(prev, str, nil, wb, !:wa, :originally_text),
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(prev, str, assert_expr(name), !:wb, wa, :originally_text),
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, space, wb, wa),
355
- (e || mid).source_range.start_pos)
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(Tree::Interpolation.new(str, mid, last, false, false),
506
- first.source_range.start_pos)
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