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 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