sass 3.3.0.alpha.256 → 3.3.0.alpha.353

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/REVISION +1 -1
  2. data/Rakefile +21 -1
  3. data/VERSION +1 -1
  4. data/VERSION_DATE +1 -1
  5. data/lib/sass.rb +6 -3
  6. data/lib/sass/cache_stores/base.rb +1 -1
  7. data/lib/sass/cache_stores/chain.rb +2 -1
  8. data/lib/sass/cache_stores/filesystem.rb +2 -6
  9. data/lib/sass/cache_stores/memory.rb +1 -1
  10. data/lib/sass/cache_stores/null.rb +2 -2
  11. data/lib/sass/callbacks.rb +1 -0
  12. data/lib/sass/css.rb +6 -6
  13. data/lib/sass/engine.rb +60 -34
  14. data/lib/sass/environment.rb +3 -1
  15. data/lib/sass/error.rb +5 -5
  16. data/lib/sass/exec.rb +52 -25
  17. data/lib/sass/features.rb +0 -2
  18. data/lib/sass/importers/deprecated_path.rb +1 -1
  19. data/lib/sass/importers/filesystem.rb +8 -6
  20. data/lib/sass/logger/base.rb +3 -3
  21. data/lib/sass/logger/log_level.rb +4 -6
  22. data/lib/sass/media.rb +2 -2
  23. data/lib/sass/plugin.rb +4 -2
  24. data/lib/sass/plugin/compiler.rb +28 -15
  25. data/lib/sass/plugin/configuration.rb +15 -7
  26. data/lib/sass/plugin/merb.rb +1 -1
  27. data/lib/sass/plugin/staleness_checker.rb +24 -8
  28. data/lib/sass/repl.rb +3 -3
  29. data/lib/sass/script.rb +2 -1
  30. data/lib/sass/script/css_lexer.rb +8 -3
  31. data/lib/sass/script/css_parser.rb +6 -2
  32. data/lib/sass/script/functions.rb +164 -109
  33. data/lib/sass/script/lexer.rb +30 -20
  34. data/lib/sass/script/parser.rb +66 -37
  35. data/lib/sass/script/tree/funcall.rb +23 -14
  36. data/lib/sass/script/tree/interpolation.rb +5 -1
  37. data/lib/sass/script/tree/list_literal.rb +5 -4
  38. data/lib/sass/script/tree/map_literal.rb +1 -1
  39. data/lib/sass/script/tree/node.rb +2 -2
  40. data/lib/sass/script/tree/operation.rb +2 -1
  41. data/lib/sass/script/tree/selector.rb +3 -2
  42. data/lib/sass/script/tree/string_interpolation.rb +2 -1
  43. data/lib/sass/script/tree/variable.rb +4 -3
  44. data/lib/sass/script/value/base.rb +12 -14
  45. data/lib/sass/script/value/color.rb +35 -16
  46. data/lib/sass/script/value/helpers.rb +146 -0
  47. data/lib/sass/script/value/list.rb +24 -5
  48. data/lib/sass/script/value/map.rb +1 -1
  49. data/lib/sass/script/value/null.rb +13 -3
  50. data/lib/sass/script/value/number.rb +44 -35
  51. data/lib/sass/script/value/string.rb +2 -2
  52. data/lib/sass/scss/css_parser.rb +2 -1
  53. data/lib/sass/scss/parser.rb +143 -93
  54. data/lib/sass/scss/rx.rb +4 -4
  55. data/lib/sass/scss/script_lexer.rb +1 -0
  56. data/lib/sass/scss/script_parser.rb +1 -0
  57. data/lib/sass/scss/static_parser.rb +5 -5
  58. data/lib/sass/selector.rb +5 -2
  59. data/lib/sass/selector/abstract_sequence.rb +1 -1
  60. data/lib/sass/selector/comma_sequence.rb +16 -14
  61. data/lib/sass/selector/sequence.rb +38 -24
  62. data/lib/sass/selector/simple.rb +4 -4
  63. data/lib/sass/selector/simple_sequence.rb +21 -11
  64. data/lib/sass/source/map.rb +7 -2
  65. data/lib/sass/source/range.rb +1 -1
  66. data/lib/sass/supports.rb +3 -3
  67. data/lib/sass/tree/debug_node.rb +1 -1
  68. data/lib/sass/tree/function_node.rb +2 -1
  69. data/lib/sass/tree/if_node.rb +1 -1
  70. data/lib/sass/tree/import_node.rb +3 -4
  71. data/lib/sass/tree/prop_node.rb +4 -2
  72. data/lib/sass/tree/rule_node.rb +5 -2
  73. data/lib/sass/tree/visitors/base.rb +6 -6
  74. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  75. data/lib/sass/tree/visitors/convert.rb +34 -28
  76. data/lib/sass/tree/visitors/cssize.rb +4 -3
  77. data/lib/sass/tree/visitors/deep_copy.rb +1 -0
  78. data/lib/sass/tree/visitors/perform.rb +31 -16
  79. data/lib/sass/tree/visitors/to_css.rb +34 -16
  80. data/lib/sass/util.rb +88 -37
  81. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  82. data/lib/sass/util/ordered_hash.rb +20 -18
  83. data/lib/sass/util/subset_map.rb +3 -2
  84. data/lib/sass/util/test.rb +0 -1
  85. data/lib/sass/version.rb +9 -5
  86. data/test/rubocop_extensions.rb +70 -0
  87. data/test/sass/functions_test.rb +20 -1
  88. data/test/sass/importer_test.rb +2 -1
  89. data/test/sass/script_test.rb +4 -0
  90. data/test/sass/source_map_test.rb +1 -1
  91. data/test/sass/util_test.rb +49 -0
  92. data/test/sass/value_helpers_test.rb +181 -0
  93. metadata +13 -9
@@ -33,10 +33,10 @@ module Sass::Plugin
33
33
 
34
34
  # Creates a new compiler.
35
35
  #
36
- # @param options [{Symbol => Object}]
36
+ # @param opts [{Symbol => Object}]
37
37
  # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
38
- def initialize(options = {})
39
- self.options.merge!(options)
38
+ def initialize(opts = {})
39
+ options.merge!(opts)
40
40
  end
41
41
 
42
42
  # Register a callback to be run after stylesheets are mass-updated.
@@ -159,7 +159,8 @@ module Sass::Plugin
159
159
 
160
160
  # Updates out-of-date stylesheets.
161
161
  #
162
- # Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
162
+ # Checks each Sass/SCSS file in
163
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location`}
163
164
  # to see if it's been modified more recently than the corresponding CSS file
164
165
  # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
165
166
  # If it has, it updates the CSS file.
@@ -230,7 +231,8 @@ module Sass::Plugin
230
231
 
231
232
  # TODO: Keep better track of what depends on what
232
233
  # so we don't have to run a global update every time anything changes.
233
- listener = create_listener(*(directories + [{:relative_paths => false}])) do |modified, added, removed|
234
+ listener_args = directories + [{:relative_paths => false}]
235
+ listener = create_listener(*listener_args) do |modified, added, removed|
234
236
  recompile_required = false
235
237
 
236
238
  modified.uniq.each do |f|
@@ -246,7 +248,7 @@ module Sass::Plugin
246
248
  end
247
249
 
248
250
  removed.uniq.each do |f|
249
- if files = individual_files.find {|(source,_,_)| File.expand_path(source) == f}
251
+ if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
250
252
  recompile_required = true
251
253
  # This was a file we were watching explicitly and compiling to a particular location.
252
254
  # Delete the corresponding file.
@@ -269,21 +271,24 @@ module Sass::Plugin
269
271
  end
270
272
 
271
273
  if recompile_required
272
- # In case a file we're watching is removed and then recreated we prune out the non-existant files here.
274
+ # In case a file we're watching is removed and then recreated we
275
+ # prune out the non-existant files here.
273
276
  watched_files_remaining = individual_files.select {|(source, _, _)| File.exists?(source)}
274
277
  update_stylesheets(watched_files_remaining)
275
278
  end
276
279
  end
277
280
 
278
- # The native windows listener is much slower than the polling
279
- # option, according to https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e#commitcomment-1295118
281
+ # The native windows listener is much slower than the polling option, according to
282
+ # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
280
283
  listener.force_polling(true) if @options[:poll] || Sass::Util.windows?
281
284
 
285
+ # rubocop:disable RescueException
282
286
  begin
283
287
  listener.start!
284
288
  rescue Exception => e
285
289
  raise e unless e.is_a?(Interrupt)
286
290
  end
291
+ # rubocop:enable RescueException
287
292
  end
288
293
 
289
294
  # Non-destructively modifies \{#options} so that default values are properly set,
@@ -313,9 +318,13 @@ module Sass::Plugin
313
318
  dedupped = []
314
319
  directories.each do |new_directory|
315
320
  # no need to add a directory that is already watched.
316
- next if dedupped.any? {|existing_directory| child_of_directory?(existing_directory, new_directory)}
321
+ next if dedupped.any? do |existing_directory|
322
+ child_of_directory?(existing_directory, new_directory)
323
+ end
317
324
  # get rid of any sub directories of this new directory
318
- dedupped.reject! {|existing_directory| child_of_directory?(new_directory, existing_directory)}
325
+ dedupped.reject! do |existing_directory|
326
+ child_of_directory?(new_directory, existing_directory)
327
+ end
319
328
  dedupped << new_directory
320
329
  end
321
330
  dedupped
@@ -338,14 +347,16 @@ module Sass::Plugin
338
347
  else
339
348
  rendered = engine.render
340
349
  end
341
- rescue Exception => e
350
+ rescue StandardError => e
342
351
  compilation_error_occured = true
343
352
  run_compilation_error e, filename, css, sourcemap
344
353
  rendered = Sass::SyntaxError.exception_to_css(e, options)
345
354
  end
346
355
 
347
356
  write_file(css, rendered)
348
- write_file(sourcemap, mapping.to_json(:css_path => css, :sourcemap_path => sourcemap)) if mapping
357
+ if mapping
358
+ write_file(sourcemap, mapping.to_json(:css_path => css, :sourcemap_path => sourcemap))
359
+ end
349
360
  run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occured
350
361
  end
351
362
 
@@ -379,7 +390,8 @@ module Sass::Plugin
379
390
  end
380
391
 
381
392
  def normalized_load_paths
382
- @normalized_load_paths ||= Sass::Engine.normalize_options(:load_paths=> load_paths)[:load_paths]
393
+ @normalized_load_paths ||=
394
+ Sass::Engine.normalize_options(:load_paths => load_paths)[:load_paths]
383
395
  end
384
396
 
385
397
  def load_paths(opts = options)
@@ -395,7 +407,8 @@ module Sass::Plugin
395
407
  end
396
408
 
397
409
  def css_filename(name, path)
398
- "#{path}#{File::SEPARATOR unless path.end_with?(File::SEPARATOR)}#{name}".gsub(/\.s[ac]ss$/, '.css')
410
+ "#{path}#{File::SEPARATOR unless path.end_with?(File::SEPARATOR)}#{name}".
411
+ gsub(/\.s[ac]ss$/, '.css')
399
412
  end
400
413
 
401
414
  def relative_to_pwd(f)
@@ -18,7 +18,8 @@ module Sass
18
18
  }.freeze
19
19
  end
20
20
 
21
- # Resets the options and {Sass::Callbacks::InstanceMethods#clear_callbacks! clears all callbacks}.
21
+ # Resets the options and
22
+ # {Sass::Callbacks::InstanceMethods#clear_callbacks! clears all callbacks}.
22
23
  def reset!
23
24
  @options = nil
24
25
  clear_callbacks!
@@ -36,7 +37,8 @@ module Sass
36
37
  # This means that Sass/SCSS files in `template_location`
37
38
  # will be compiled to CSS files in `css_location`.
38
39
  #
39
- # This is preferred over manually manipulating the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
40
+ # This is preferred over manually manipulating the
41
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
40
42
  # since the option can be in multiple formats.
41
43
  #
42
44
  # Note that this method will change `options[:template_location]`
@@ -56,7 +58,8 @@ module Sass
56
58
  # This means that Sass/SCSS files in `template_location`
57
59
  # will no longer be compiled to CSS files in `css_location`.
58
60
  #
59
- # This is preferred over manually manipulating the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
61
+ # This is preferred over manually manipulating the
62
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
60
63
  # since the option can be in multiple formats.
61
64
  #
62
65
  # Note that this method will change `options[:template_location]`
@@ -100,10 +103,15 @@ module Sass
100
103
  options[:template_location] =
101
104
  case options[:template_location]
102
105
  when nil
103
- options[:css_location] ?
104
- [[File.join(options[:css_location], 'sass'), options[:css_location]]] : []
105
- when String; [[options[:template_location], options[:css_location]]]
106
- else; options[:template_location].to_a
106
+ if options[:css_location]
107
+ [[File.join(options[:css_location], 'sass'), options[:css_location]]]
108
+ else
109
+ []
110
+ end
111
+ when String
112
+ [[options[:template_location], options[:css_location]]]
113
+ else
114
+ options[:template_location].to_a
107
115
  end
108
116
  end
109
117
  end
@@ -5,7 +5,7 @@ unless defined?(Sass::MERB_LOADED)
5
5
  # Different default options in a m envirionment.
6
6
  def default_options
7
7
  @default_options ||= begin
8
- version = Merb::VERSION.split('.').map { |n| n.to_i }
8
+ version = Merb::VERSION.split('.').map {|n| n.to_i}
9
9
  if version[0] <= 0 && version[1] < 5
10
10
  root = MERB_ROOT
11
11
  env = MERB_ENV
@@ -1,3 +1,5 @@
1
+ require 'thread'
2
+
1
3
  module Sass
2
4
  module Plugin
3
5
  # The class handles `.s[ca]ss` file staleness checks via their mtime timestamps.
@@ -24,11 +26,13 @@ module Sass
24
26
  # as its instance-level caches are never explicitly expired.
25
27
  class StalenessChecker
26
28
  @dependencies_cache = {}
29
+ @dependency_cache_mutex = Mutex.new
27
30
 
28
31
  class << self
29
32
  # TODO: attach this to a compiler instance.
30
33
  # @private
31
34
  attr_accessor :dependencies_cache
35
+ attr_reader :dependency_cache_mutex
32
36
  end
33
37
 
34
38
  # Creates a new StalenessChecker
@@ -37,15 +41,13 @@ module Sass
37
41
  # @param options [{Symbol => Object}]
38
42
  # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
39
43
  def initialize(options)
40
- @dependencies = self.class.dependencies_cache
41
-
42
44
  # URIs that are being actively checked for staleness. Protects against
43
45
  # import loops.
44
46
  @actively_checking = Set.new
45
47
 
46
48
  # Entries in the following instance-level caches are never explicitly expired.
47
- # Instead they are supposed to automaticaly go out of scope when a series of staleness checks
48
- # (this instance of StalenessChecker was created for) is finished.
49
+ # Instead they are supposed to automaticaly go out of scope when a series of staleness
50
+ # checks (this instance of StalenessChecker was created for) is finished.
49
51
  @mtimes, @dependencies_stale, @parse_trees = {}, {}, {}
50
52
  @options = Sass::Engine.normalize_options(options)
51
53
  end
@@ -131,7 +133,7 @@ module Sass
131
133
  begin
132
134
  mtime = importer.mtime(uri, @options)
133
135
  if mtime.nil?
134
- @dependencies.delete([uri, importer])
136
+ with_dependency_cache {|cache| cache.delete([uri, importer])}
135
137
  nil
136
138
  else
137
139
  mtime
@@ -140,18 +142,21 @@ module Sass
140
142
  end
141
143
 
142
144
  def dependencies(uri, importer)
143
- stored_mtime, dependencies = Sass::Util.destructure(@dependencies[[uri, importer]])
145
+ stored_mtime, dependencies =
146
+ with_dependency_cache {|cache| Sass::Util.destructure(cache[[uri, importer]])}
144
147
 
145
148
  if !stored_mtime || stored_mtime < mtime(uri, importer)
146
149
  dependencies = compute_dependencies(uri, importer)
147
- @dependencies[[uri, importer]] = [mtime(uri, importer), dependencies]
150
+ with_dependency_cache do |cache|
151
+ cache[[uri, importer]] = [mtime(uri, importer), dependencies]
152
+ end
148
153
  end
149
154
 
150
155
  dependencies
151
156
  end
152
157
 
153
158
  def dependency_updated?(css_mtime)
154
- Proc.new do |uri, importer|
159
+ proc do |uri, importer|
155
160
  next true if @actively_checking.include?(uri)
156
161
  begin
157
162
  @actively_checking << uri
@@ -178,6 +183,17 @@ module Sass
178
183
  def tree(uri, importer)
179
184
  @parse_trees[[uri, importer]] ||= importer.find(uri, @options).to_tree
180
185
  end
186
+
187
+ # Get access to the global dependency cache in a threadsafe manner.
188
+ # Inside the block, no other thread can access the dependency cache.
189
+ #
190
+ # @yieldparam cache [Hash] The hash that is the global dependency cache
191
+ # @return The value returned by the block to which this method yields
192
+ def with_dependency_cache
193
+ StalenessChecker.dependency_cache_mutex.synchronize do
194
+ yield StalenessChecker.dependencies_cache
195
+ end
196
+ end
181
197
  end
182
198
  end
183
199
  end
@@ -18,7 +18,7 @@ module Sass
18
18
  @line = 0
19
19
  loop do
20
20
  @line += 1
21
- unless text = Readline.readline('>> ')
21
+ unless (text = Readline.readline('>> '))
22
22
  puts
23
23
  return
24
24
  end
@@ -48,8 +48,8 @@ module Sass
48
48
  rescue Sass::SyntaxError => e
49
49
  puts "SyntaxError: #{e.message}"
50
50
  if @options[:trace]
51
- e.backtrace.each do |e|
52
- puts "\tfrom #{e}"
51
+ e.backtrace.each do |line|
52
+ puts "\tfrom #{line}"
53
53
  end
54
54
  end
55
55
  end
@@ -56,7 +56,8 @@ module Sass
56
56
 
57
57
  # @private
58
58
  def self.const_missing(name)
59
- super unless klass = CONST_RENAMES[name]
59
+ klass = CONST_RENAMES[name]
60
+ super unless klass
60
61
  CONST_RENAMES.each {|n, k| const_set(n, k)}
61
62
  klass
62
63
  end
@@ -4,6 +4,7 @@ module Sass
4
4
  #
5
5
  # @see Sass::SCSS::CssParser
6
6
  class CssLexer < Lexer
7
+
7
8
  private
8
9
 
9
10
  def token
@@ -12,16 +13,20 @@ module Sass
12
13
 
13
14
  def string(re, *args)
14
15
  if re == :uri
15
- return unless uri = scan(URI)
16
+ uri = scan(URI)
17
+ return unless uri
16
18
  return [:string, Script::Value::String.new(uri)]
17
19
  end
18
20
 
19
21
  return unless scan(STRING)
20
- [:string, Script::Value::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
22
+ string_value = (@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1')
23
+ value = Script::Value::String.new(string_value, :string)
24
+ [:string, value]
21
25
  end
22
26
 
23
27
  def important
24
- return unless s = scan(IMPORTANT)
28
+ s = scan(IMPORTANT)
29
+ return unless s
25
30
  [:raw, s]
26
31
  end
27
32
  end
@@ -7,6 +7,7 @@ module Sass
7
7
  #
8
8
  # @see Sass::SCSS::CssParser
9
9
  class CssParser < Parser
10
+
10
11
  private
11
12
 
12
13
  # @private
@@ -17,8 +18,11 @@ module Sass
17
18
  production :div, :unary_plus, :div
18
19
 
19
20
  def string
20
- return number unless tok = try_tok(:string)
21
- return literal_node(tok.value, tok.source_range) unless @lexer.peek && @lexer.peek.type == :begin_interpolation
21
+ tok = try_tok(:string)
22
+ return number unless tok
23
+ unless @lexer.peek && @lexer.peek.type == :begin_interpolation
24
+ return literal_node(tok.value, tok.source_range)
25
+ end
22
26
  end
23
27
 
24
28
  # Short-circuit all the SassScript-only productions
@@ -1,3 +1,5 @@
1
+ require 'sass/script/value/helpers'
2
+
1
3
  module Sass::Script
2
4
  # Methods in this module are accessible from the SassScript context.
3
5
  # For example, you can write
@@ -91,13 +93,16 @@ module Sass::Script
91
93
  #
92
94
  # ## Other Color Functions
93
95
  #
94
- # \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
96
+ # \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\],
97
+ # \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
95
98
  # : Increases or decreases one or more components of a color.
96
99
  #
97
- # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$saturation\], \[$lightness\], \[$alpha\])}
100
+ # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\],
101
+ # \[$saturation\], \[$lightness\], \[$alpha\])}
98
102
  # : Fluidly scales one or more properties of a color.
99
103
  #
100
- # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
104
+ # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\],
105
+ # \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
101
106
  # : Changes one or more properties of a color.
102
107
  #
103
108
  # \{#ie_hex_str ie-hex-str($color)}
@@ -342,22 +347,23 @@ module Sass::Script
342
347
  def self.signature(method_name, arg_arity, kwarg_arity)
343
348
  return unless @signatures[method_name]
344
349
  @signatures[method_name].each do |signature|
345
- return signature if signature.args.size == arg_arity + kwarg_arity
346
- next unless signature.args.size < arg_arity + kwarg_arity
350
+ sig_arity = signature.args.size
351
+ return signature if sig_arity == arg_arity + kwarg_arity
352
+ next unless sig_arity < arg_arity + kwarg_arity
347
353
 
348
354
  # We have enough args.
349
355
  # Now we need to figure out which args are varargs
350
356
  # and if the signature allows them.
351
357
  t_arg_arity, t_kwarg_arity = arg_arity, kwarg_arity
352
- if signature.args.size > t_arg_arity
358
+ if sig_arity > t_arg_arity
353
359
  # we transfer some kwargs arity to args arity
354
360
  # if it does not have enough args -- assuming the names will work out.
355
- t_kwarg_arity -= (signature.args.size - t_arg_arity)
356
- t_arg_arity = signature.args.size
361
+ t_kwarg_arity -= (sig_arity - t_arg_arity)
362
+ t_arg_arity = sig_arity
357
363
  end
358
364
 
359
- if ( t_arg_arity == signature.args.size || t_arg_arity > signature.args.size && signature.var_args ) &&
360
- (t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
365
+ if (t_arg_arity == sig_arity || t_arg_arity > sig_arity && signature.var_args) &&
366
+ (t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
361
367
  return signature
362
368
  end
363
369
  end
@@ -369,6 +375,7 @@ module Sass::Script
369
375
  # are available to use in functions.
370
376
  class EvaluationContext
371
377
  include Functions
378
+ include Value::Helpers
372
379
 
373
380
  # The human-readable names for [Sass::Script::Value::Base]. The default is
374
381
  # just the downcased name of the type. The default is the downcased type
@@ -440,8 +447,10 @@ module Sass::Script
440
447
  #
441
448
  # @example
442
449
  # assert_integer 2px
443
- # assert_integer 2.5px => SyntaxError: "Expected 2.5px to be an integer"
444
- # assert_integer 2.5px, "width" => SyntaxError: "Expected width to be an integer but got 2.5px"
450
+ # assert_integer 2.5px
451
+ # => SyntaxError: "Expected 2.5px to be an integer"
452
+ # assert_integer 2.5px, "width"
453
+ # => SyntaxError: "Expected width to be an integer but got 2.5px"
445
454
  # @param number [Sass::Script::Value::Base] The value to be validated.
446
455
  # @param name [::String] The name of the parameter being validated.
447
456
  # @raise [ArgumentError] if number is not an integer or is not a number.
@@ -464,6 +473,7 @@ module Sass::Script
464
473
  alias_method :callable?, :public_method_defined?
465
474
 
466
475
  private
476
+
467
477
  def include(*args)
468
478
  r = super
469
479
  # We have to re-include ourselves into EvaluationContext to work around
@@ -494,17 +504,17 @@ module Sass::Script
494
504
  assert_type green, :Number, :green
495
505
  assert_type blue, :Number, :blue
496
506
 
497
- Sass::Script::Value::Color.new([[red, :red], [green, :green], [blue, :blue]].map do |(c, name)|
498
- v = c.value
499
- if c.is_unit?("%")
500
- v = Sass::Util.check_range("$#{name}: Color value", 0..100, c, '%')
501
- v * 255 / 100.0
502
- elsif c.unitless?
503
- Sass::Util.check_range("$#{name}: Color value", 0..255, c)
504
- else
505
- raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
506
- end
507
- end)
507
+ color_attrs = [[red, :red], [green, :green], [blue, :blue]].map do |(c, name)|
508
+ if c.is_unit?("%")
509
+ v = Sass::Util.check_range("$#{name}: Color value", 0..100, c, '%')
510
+ v * 255 / 100.0
511
+ elsif c.unitless?
512
+ Sass::Util.check_range("$#{name}: Color value", 0..255, c)
513
+ else
514
+ raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
515
+ end
516
+ end
517
+ Sass::Script::Value::Color.new(color_attrs)
508
518
  end
509
519
  declare :rgb, [:red, :green, :blue]
510
520
 
@@ -576,7 +586,7 @@ module Sass::Script
576
586
  # @raise [ArgumentError] if `$saturation` or `$lightness` are out of bounds
577
587
  # or any parameter is the wrong type
578
588
  def hsl(hue, saturation, lightness)
579
- hsla(hue, saturation, lightness, Sass::Script::Value::Number.new(1))
589
+ hsla(hue, saturation, lightness, number(1))
580
590
  end
581
591
  declare :hsl, [:hue, :saturation, :lightness]
582
592
 
@@ -611,7 +621,8 @@ module Sass::Script
611
621
  s = Sass::Util.check_range('Saturation', 0..100, saturation, '%')
612
622
  l = Sass::Util.check_range('Lightness', 0..100, lightness, '%')
613
623
 
614
- Sass::Script::Value::Color.new(:hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
624
+ Sass::Script::Value::Color.new(
625
+ :hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
615
626
  end
616
627
  declare :hsla, [:hue, :saturation, :lightness, :alpha]
617
628
 
@@ -627,7 +638,7 @@ module Sass::Script
627
638
  # @raise [ArgumentError] if `$color` isn't a color
628
639
  def red(color)
629
640
  assert_type color, :Color, :color
630
- Sass::Script::Value::Number.new(color.red)
641
+ number(color.red)
631
642
  end
632
643
  declare :red, [:color]
633
644
 
@@ -643,7 +654,7 @@ module Sass::Script
643
654
  # @raise [ArgumentError] if `$color` isn't a color
644
655
  def green(color)
645
656
  assert_type color, :Color, :color
646
- Sass::Script::Value::Number.new(color.green)
657
+ number(color.green)
647
658
  end
648
659
  declare :green, [:color]
649
660
 
@@ -659,7 +670,7 @@ module Sass::Script
659
670
  # @raise [ArgumentError] if `$color` isn't a color
660
671
  def blue(color)
661
672
  assert_type color, :Color, :color
662
- Sass::Script::Value::Number.new(color.blue)
673
+ number(color.blue)
663
674
  end
664
675
  declare :blue, [:color]
665
676
 
@@ -677,7 +688,8 @@ module Sass::Script
677
688
  # @raise [ArgumentError] if `$color` isn't a color
678
689
  def hue(color)
679
690
  assert_type color, :Color, :color
680
- Sass::Script::Value::Number.new(color.hue, ["deg"])
691
+ assert_type color, :Color
692
+ number(color.hue, "deg")
681
693
  end
682
694
  declare :hue, [:color]
683
695
 
@@ -695,7 +707,7 @@ module Sass::Script
695
707
  # @raise [ArgumentError] if `$color` isn't a color
696
708
  def saturation(color)
697
709
  assert_type color, :Color, :color
698
- Sass::Script::Value::Number.new(color.saturation, ["%"])
710
+ number(color.saturation, "%")
699
711
  end
700
712
  declare :saturation, [:color]
701
713
 
@@ -713,7 +725,7 @@ module Sass::Script
713
725
  # @raise [ArgumentError] if `$color` isn't a color
714
726
  def lightness(color)
715
727
  assert_type color, :Color, :color
716
- Sass::Script::Value::Number.new(color.lightness, ["%"])
728
+ number(color.lightness, "%")
717
729
  end
718
730
  declare :lightness, [:color]
719
731
 
@@ -729,17 +741,17 @@ module Sass::Script
729
741
  # @raise [ArgumentError] if `$color` isn't a color
730
742
  def alpha(*args)
731
743
  if args.all? do |a|
732
- a.is_a?(Sass::Script::Value::String) && a.type == :identifier &&
733
- a.value =~ /^[a-zA-Z]+\s*=/
734
- end
744
+ a.is_a?(Sass::Script::Value::String) && a.type == :identifier &&
745
+ a.value =~ /^[a-zA-Z]+\s*=/
746
+ end
735
747
  # Support the proprietary MS alpha() function
736
- return Sass::Script::Value::String.new("alpha(#{args.map {|a| a.to_s}.join(", ")})")
748
+ return identifier("alpha(#{args.map {|a| a.to_s}.join(", ")})")
737
749
  end
738
750
 
739
751
  raise ArgumentError.new("wrong number of arguments (#{args.size} for 1)") if args.size != 1
740
752
 
741
753
  assert_type args.first, :Color, :color
742
- Sass::Script::Value::Number.new(args.first.alpha)
754
+ number(args.first.alpha)
743
755
  end
744
756
  declare :alpha, [:color]
745
757
 
@@ -752,10 +764,10 @@ module Sass::Script
752
764
  # @raise [ArgumentError] if `$color` isn't a color
753
765
  def opacity(color)
754
766
  if color.is_a?(Sass::Script::Value::Number)
755
- return Sass::Script::Value::String.new("opacity(#{color})")
767
+ return identifier("opacity(#{color})")
756
768
  end
757
769
  assert_type color, :Color, :color
758
- Sass::Script::Value::Number.new(color.alpha)
770
+ number(color.alpha)
759
771
  end
760
772
  declare :opacity, [:color]
761
773
 
@@ -858,7 +870,7 @@ module Sass::Script
858
870
  def saturate(color, amount = nil)
859
871
  # Support the filter effects definition of saturate.
860
872
  # https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
861
- return Sass::Script::Value::String.new("saturate(#{color})") if amount.nil?
873
+ return identifier("saturate(#{color})") if amount.nil?
862
874
  _adjust(color, amount, :saturation, 0..100, :+, "%")
863
875
  end
864
876
  declare :saturate, [:color, :amount]
@@ -918,7 +930,7 @@ module Sass::Script
918
930
  def ie_hex_str(color)
919
931
  assert_type color, :Color, :color
920
932
  alpha = (color.alpha * 255).round.to_s(16).rjust(2, '0')
921
- Sass::Script::Value::String.new("##{alpha}#{color.send(:hex_str)[1..-1]}".upcase)
933
+ identifier("##{alpha}#{color.send(:hex_str)[1..-1]}".upcase)
922
934
  end
923
935
  declare :ie_hex_str, [:color]
924
936
 
@@ -935,7 +947,8 @@ module Sass::Script
935
947
  # adjust-color(#102030, $blue: 5) => #102035
936
948
  # adjust-color(#102030, $red: -5, $blue: 5) => #0b2035
937
949
  # adjust-color(hsl(25, 100%, 80%), $lightness: -30%, $alpha: -0.4) => hsla(25, 100%, 50%, 0.6)
938
- # @overload adjust_color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])
950
+ # @overload adjust_color($color, [$red], [$green], [$blue],
951
+ # [$hue], [$saturation], [$lightness], [$alpha])
939
952
  # @param $color [Sass::Script::Value::Color]
940
953
  # @param $red [Sass::Script::Value::Number] The adjustment to make on the
941
954
  # red component, between -255 and 255 inclusive
@@ -967,7 +980,8 @@ module Sass::Script
967
980
  "alpha" => [-1..1, ""]
968
981
  }) do |name, (range, units)|
969
982
 
970
- next unless val = kwargs.delete(name)
983
+ val = kwargs.delete(name)
984
+ next unless val
971
985
  assert_type val, :Number, name
972
986
  Sass::Util.check_range("$#{name}: Amount", range, val, units) if range
973
987
  adjusted = color.send(name) + val.value
@@ -1011,7 +1025,8 @@ module Sass::Script
1011
1025
  # scale-color(hsl(120, 70%, 80%), $lightness: 50%) => hsl(120, 70%, 90%)
1012
1026
  # scale-color(rgb(200, 150%, 170%), $green: -40%, $blue: 70%) => rgb(200, 90, 229)
1013
1027
  # scale-color(hsl(200, 70%, 80%), $saturation: -90%, $alpha: -30%) => hsla(200, 7%, 80%, 0.7)
1014
- # @overload scale_color($color, [$red], [$green], [$blue], [$saturation], [$lightness], [$alpha])
1028
+ # @overload scale_color($color, [$red], [$green], [$blue],
1029
+ # [$saturation], [$lightness], [$alpha])
1015
1030
  # @param $color [Sass::Script::Value::Color]
1016
1031
  # @param $red [Sass::Script::Value::Number]
1017
1032
  # @param $green [Sass::Script::Value::Number]
@@ -1034,15 +1049,16 @@ module Sass::Script
1034
1049
  "alpha" => 1
1035
1050
  }) do |name, max|
1036
1051
 
1037
- next unless val = kwargs.delete(name)
1052
+ val = kwargs.delete(name)
1053
+ next unless val
1038
1054
  assert_type val, :Number, name
1039
1055
  assert_unit val, '%', name
1040
1056
  Sass::Util.check_range("$#{name}: Amount", -100..100, val, '%')
1041
1057
 
1042
1058
  current = color.send(name)
1043
- scale = val.value/100.0
1059
+ scale = val.value / 100.0
1044
1060
  diff = scale > 0 ? max - current : current
1045
- [name.to_sym, current + diff*scale]
1061
+ [name.to_sym, current + diff * scale]
1046
1062
  end
1047
1063
 
1048
1064
  unless kwargs.empty?
@@ -1067,7 +1083,8 @@ module Sass::Script
1067
1083
  # change-color(#102030, $blue: 5) => #102005
1068
1084
  # change-color(#102030, $red: 120, $blue: 5) => #782005
1069
1085
  # change-color(hsl(25, 100%, 80%), $lightness: 40%, $alpha: 0.8) => hsla(25, 100%, 40%, 0.8)
1070
- # @overload change_color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])
1086
+ # @overload change_color($color, [$red], [$green], [$blue], [$hue],
1087
+ # [$saturation], [$lightness], [$alpha])
1071
1088
  # @param $color [Sass::Script::Value::Color]
1072
1089
  # @param $red [Sass::Script::Value::Number] The new red component for the
1073
1090
  # color, within 0 and 255 inclusive
@@ -1090,7 +1107,8 @@ module Sass::Script
1090
1107
  def change_color(color, kwargs)
1091
1108
  assert_type color, :Color, :color
1092
1109
  with = Sass::Util.map_hash(%w[red green blue hue saturation lightness alpha]) do |name, max|
1093
- next unless val = kwargs.delete(name)
1110
+ val = kwargs.delete(name)
1111
+ next unless val
1094
1112
  assert_type val, :Number, name
1095
1113
  [name.to_sym, val.value]
1096
1114
  end
@@ -1126,7 +1144,7 @@ module Sass::Script
1126
1144
  # @return [Sass::Script::Value::Color]
1127
1145
  # @raise [ArgumentError] if `$weight` is out of bounds or any parameter is
1128
1146
  # the wrong type
1129
- def mix(color_1, color_2, weight = Sass::Script::Value::Number.new(50))
1147
+ def mix(color_1, color_2, weight = number(50))
1130
1148
  assert_type color_1, :Color, :color_1
1131
1149
  assert_type color_2, :Color, :color_2
1132
1150
  assert_type weight, :Number, :weight
@@ -1152,16 +1170,16 @@ module Sass::Script
1152
1170
  #
1153
1171
  # Finally, the weight of color_1 is renormalized to be within [0, 1]
1154
1172
  # and the weight of color_2 is given by 1 minus the weight of color_1.
1155
- p = (weight.value/100.0).to_f
1156
- w = p*2 - 1
1173
+ p = (weight.value / 100.0).to_f
1174
+ w = p * 2 - 1
1157
1175
  a = color_1.alpha - color_2.alpha
1158
1176
 
1159
- w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0
1177
+ w1 = ((w * a == -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0
1160
1178
  w2 = 1 - w1
1161
1179
 
1162
- rgb = color_1.rgb.zip(color_2.rgb).map {|v1, v2| v1*w1 + v2*w2}
1163
- alpha = color_1.alpha*p + color_2.alpha*(1-p)
1164
- Sass::Script::Value::Color.new(rgb + [alpha])
1180
+ rgba = color_1.rgb.zip(color_2.rgb).map {|v1, v2| v1 * w1 + v2 * w2}
1181
+ rgba << color_1.alpha * p + color_2.alpha * (1 - p)
1182
+ rgb_color(*rgba)
1165
1183
  end
1166
1184
  declare :mix, [:color_1, :color_2]
1167
1185
  declare :mix, [:color_1, :color_2, :weight]
@@ -1176,9 +1194,9 @@ module Sass::Script
1176
1194
  # @raise [ArgumentError] if `$color` isn't a color
1177
1195
  def grayscale(color)
1178
1196
  if color.is_a?(Sass::Script::Value::Number)
1179
- return Sass::Script::Value::String.new("grayscale(#{color})")
1197
+ return identifier("grayscale(#{color})")
1180
1198
  end
1181
- desaturate color, Sass::Script::Value::Number.new(100)
1199
+ desaturate color, number(100)
1182
1200
  end
1183
1201
  declare :grayscale, [:color]
1184
1202
 
@@ -1191,7 +1209,7 @@ module Sass::Script
1191
1209
  # @return [Sass::Script::Value::Color]
1192
1210
  # @raise [ArgumentError] if `$color` isn't a color
1193
1211
  def complement(color)
1194
- adjust_hue color, Sass::Script::Value::Number.new(180)
1212
+ adjust_hue color, number(180)
1195
1213
  end
1196
1214
  declare :complement, [:color]
1197
1215
 
@@ -1204,7 +1222,7 @@ module Sass::Script
1204
1222
  # @raise [ArgumentError] if `$color` isn't a color
1205
1223
  def invert(color)
1206
1224
  if color.is_a?(Sass::Script::Value::Number)
1207
- return Sass::Script::Value::String.new("invert(#{color})")
1225
+ return identifier("invert(#{color})")
1208
1226
  end
1209
1227
 
1210
1228
  assert_type color, :Color, :color
@@ -1227,8 +1245,8 @@ module Sass::Script
1227
1245
  # @return [Sass::Script::Value::String]
1228
1246
  # @raise [ArgumentError] if `$string` isn't a string
1229
1247
  def unquote(string)
1230
- if string.is_a?(Sass::Script::Value::String)
1231
- Sass::Script::Value::String.new(string.value, :identifier)
1248
+ if string.is_a?(Sass::Script::Value::String) && string.type != :identifier
1249
+ identifier(string.value)
1232
1250
  else
1233
1251
  string
1234
1252
  end
@@ -1248,7 +1266,11 @@ module Sass::Script
1248
1266
  # @raise [ArgumentError] if `$string` isn't a string
1249
1267
  def quote(string)
1250
1268
  assert_type string, :String, :string
1251
- Sass::Script::Value::String.new(string.value, :string)
1269
+ if string.type != :string
1270
+ quoted_string(string.value)
1271
+ else
1272
+ string
1273
+ end
1252
1274
  end
1253
1275
  declare :quote, [:string]
1254
1276
 
@@ -1262,7 +1284,7 @@ module Sass::Script
1262
1284
  # @raise [ArgumentError] if `$string` isn't a string
1263
1285
  def str_length(string)
1264
1286
  assert_type string, :String, :string
1265
- Sass::Script::Value::Number.new(string.value.size)
1287
+ number(string.value.size)
1266
1288
  end
1267
1289
  declare :str_length, [:string]
1268
1290
 
@@ -1291,8 +1313,13 @@ module Sass::Script
1291
1313
  assert_type insert, :String, :insert
1292
1314
  assert_integer index, :index
1293
1315
  assert_unit index, nil, :index
1294
- insertion_point = index.value > 0 ? [index.value - 1, original.value.size].min : [index.value, -original.value.size - 1].max
1295
- Sass::Script::Value::String.new(original.value.dup.insert(insertion_point, insert.value), original.type)
1316
+ insertion_point = if index.value > 0
1317
+ [index.value - 1, original.value.size].min
1318
+ else
1319
+ [index.value, -original.value.size - 1].max
1320
+ end
1321
+ result = original.value.dup.insert(insertion_point, insert.value)
1322
+ Sass::Script::Value::String.new(result, original.type)
1296
1323
  end
1297
1324
  declare :str_insert, [:string, :insert, :index]
1298
1325
 
@@ -1317,11 +1344,10 @@ module Sass::Script
1317
1344
  assert_type string, :String, :string
1318
1345
  assert_type substring, :String, :substring
1319
1346
  index = string.value.index(substring.value) || -1
1320
- Sass::Script::Value::Number.new(index + 1)
1347
+ number(index + 1)
1321
1348
  end
1322
1349
  declare :str_index, [:string, :substring]
1323
1350
 
1324
-
1325
1351
  # Extracts a substring from `$string`. The substring will begin at index
1326
1352
  # `$start-at` and ends at index `$end-at`.
1327
1353
  #
@@ -1348,7 +1374,7 @@ module Sass::Script
1348
1374
  assert_type string, :String, :string
1349
1375
  assert_unit start_at, nil, "start-at"
1350
1376
 
1351
- end_at = Sass::Script::Value::Number.new(-1) if end_at.nil?
1377
+ end_at = number(-1) if end_at.nil?
1352
1378
  assert_unit end_at, nil, "end-at"
1353
1379
 
1354
1380
  s = start_at.value > 0 ? start_at.value - 1 : start_at.value
@@ -1407,7 +1433,7 @@ module Sass::Script
1407
1433
  # @return [Sass::Script::Value::String] The unquoted string name of the
1408
1434
  # value's type
1409
1435
  def type_of(value)
1410
- Sass::Script::Value::String.new(value.class.name.gsub(/Sass::Script::Value::/,'').downcase)
1436
+ identifier(value.class.name.gsub(/Sass::Script::Value::/, '').downcase)
1411
1437
  end
1412
1438
  declare :type_of, [:value]
1413
1439
 
@@ -1423,7 +1449,7 @@ module Sass::Script
1423
1449
  # @raise [ArgumentError] if `$feature` isn't a string
1424
1450
  def feature_exists(feature)
1425
1451
  assert_type feature, :String, :feature
1426
- Sass::Script::Value::Bool.new(Sass.has_feature?(feature.value))
1452
+ bool(Sass.has_feature?(feature.value))
1427
1453
  end
1428
1454
  declare :feature_exists, [:feature]
1429
1455
 
@@ -1443,7 +1469,7 @@ module Sass::Script
1443
1469
  # @raise [ArgumentError] if `$number` isn't a number
1444
1470
  def unit(number)
1445
1471
  assert_type number, :Number, :number
1446
- Sass::Script::Value::String.new(number.unit_str, :string)
1472
+ quoted_string(number.unit_str)
1447
1473
  end
1448
1474
  declare :unit, [:number]
1449
1475
 
@@ -1458,7 +1484,7 @@ module Sass::Script
1458
1484
  # @raise [ArgumentError] if `$number` isn't a number
1459
1485
  def unitless(number)
1460
1486
  assert_type number, :Number, :number
1461
- Sass::Script::Value::Bool.new(number.unitless?)
1487
+ bool(number.unitless?)
1462
1488
  end
1463
1489
  declare :unitless, [:number]
1464
1490
 
@@ -1476,7 +1502,7 @@ module Sass::Script
1476
1502
  def comparable(number_1, number_2)
1477
1503
  assert_type number_1, :Number, :number_1
1478
1504
  assert_type number_2, :Number, :number_2
1479
- Sass::Script::Value::Bool.new(number_1.comparable_to?(number_2))
1505
+ bool(number_1.comparable_to?(number_2))
1480
1506
  end
1481
1507
  declare :comparable, [:number_1, :number_2]
1482
1508
 
@@ -1493,7 +1519,7 @@ module Sass::Script
1493
1519
  unless value.is_a?(Sass::Script::Value::Number) && value.unitless?
1494
1520
  raise ArgumentError.new("$value: #{value.inspect} is not a unitless number")
1495
1521
  end
1496
- Sass::Script::Value::Number.new(value.value * 100, ['%'])
1522
+ number(value.value * 100, '%')
1497
1523
  end
1498
1524
  declare :percentage, [:value]
1499
1525
 
@@ -1599,10 +1625,40 @@ module Sass::Script
1599
1625
  # @param $list [Sass::Script::Value::Base]
1600
1626
  # @return [Sass::Script::Value::Number]
1601
1627
  def length(list)
1602
- Sass::Script::Value::Number.new(list.to_a.size)
1628
+ number(list.to_a.size)
1603
1629
  end
1604
1630
  declare :length, [:list]
1605
1631
 
1632
+ # Return a new list, based on the list provided, but with the nth
1633
+ # element changed to the value given.
1634
+ #
1635
+ # Note that unlike some languages, the first item in a Sass list is number
1636
+ # 1, the second number 2, and so forth.
1637
+ #
1638
+ # Negative index values address elements in reverse order, starting with the last element
1639
+ # in the list.
1640
+ #
1641
+ # @example
1642
+ # set-nth($list: 10px 20px 30px, $n: 2, $value: -20px) => 10px -20px 30px
1643
+ # @overload nth($list, $n, $value)
1644
+ # @param $list [Sass::Script::Value::Base] The list that will be copied, having the element
1645
+ # at index `$n` changed.
1646
+ # @param $n [Sass::Script::Value::Number] The index of the item to set.
1647
+ # Negative indices count from the end of the list.
1648
+ # @param $value [Sass::Script::Value::Base] The new value at index `$n`.
1649
+ # @return [Sass::Script::Value::List]
1650
+ # @raise [ArgumentError] if `$n` isn't an integer between 1 and the length
1651
+ # of `$list`
1652
+ def set_nth(list, n, value)
1653
+ assert_type n, :Number, :n
1654
+ Sass::Script::Value::List.assert_valid_index(list, n)
1655
+ index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1656
+ new_list = list.to_a.dup
1657
+ new_list[index] = value
1658
+ Sass::Script::Value::List.new(new_list, list.separator)
1659
+ end
1660
+ declare :set_nth, [:list, :n, :value]
1661
+
1606
1662
  # Gets the nth item in a list.
1607
1663
  #
1608
1664
  # Note that unlike some languages, the first item in a Sass list is number
@@ -1610,6 +1666,9 @@ module Sass::Script
1610
1666
  #
1611
1667
  # This can return the nth pair in a map as well.
1612
1668
  #
1669
+ # Negative index values address elements in reverse order, starting with the last element in
1670
+ # the list.
1671
+ #
1613
1672
  # @example
1614
1673
  # nth(10px 20px 30px, 1) => 10px
1615
1674
  # nth((Helvetica, Arial, sans-serif), 3) => sans-serif
@@ -1623,13 +1682,7 @@ module Sass::Script
1623
1682
  # of `$list`
1624
1683
  def nth(list, n)
1625
1684
  assert_type n, :Number, :n
1626
- if !n.int? || n.to_i == 0
1627
- raise ArgumentError.new("List index #{n} must be a non-zero integer")
1628
- elsif list.to_a.size == 0
1629
- raise ArgumentError.new("List index is #{n} but list has no items")
1630
- elsif n.to_i.abs > (size = list.to_a.size)
1631
- raise ArgumentError.new("List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
1632
- end
1685
+ Sass::Script::Value::List.assert_valid_index(list, n)
1633
1686
 
1634
1687
  index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1635
1688
  list.to_a[index]
@@ -1656,18 +1709,17 @@ module Sass::Script
1656
1709
  # If this is `comma` or `space`, that separator will be used. If this is
1657
1710
  # `auto` (the default), the separator is determined as explained above.
1658
1711
  # @return [Sass::Script::Value::List]
1659
- def join(list1, list2, separator = Sass::Script::Value::String.new("auto"))
1712
+ def join(list1, list2, separator = identifier("auto"))
1660
1713
  assert_type separator, :String, :separator
1661
1714
  unless %w[auto space comma].include?(separator.value)
1662
1715
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1663
1716
  end
1664
- Sass::Script::Value::List.new(
1665
- list1.to_a + list2.to_a,
1666
- if separator.value == 'auto'
1667
- list1.separator || list2.separator || :space
1668
- else
1669
- separator.value.to_sym
1670
- end)
1717
+ sep = if separator.value == 'auto'
1718
+ list1.separator || list2.separator || :space
1719
+ else
1720
+ separator.value.to_sym
1721
+ end
1722
+ list(list1.to_a + list2.to_a, sep)
1671
1723
  end
1672
1724
  declare :join, [:list1, :list2]
1673
1725
  declare :join, [:list1, :list2, :separator]
@@ -1690,18 +1742,17 @@ module Sass::Script
1690
1742
  # If this is `comma` or `space`, that separator will be used. If this is
1691
1743
  # `auto` (the default), the separator is determined as explained above.
1692
1744
  # @return [Sass::Script::Value::List]
1693
- def append(list, val, separator = Sass::Script::String.new("auto"))
1745
+ def append(list, val, separator = identifier("auto"))
1694
1746
  assert_type separator, :String, :separator
1695
1747
  unless %w[auto space comma].include?(separator.value)
1696
1748
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1697
1749
  end
1698
- Sass::Script::Value::List.new(
1699
- list.to_a + [val],
1700
- if separator.value == 'auto'
1701
- list.separator || :space
1702
- else
1703
- separator.value.to_sym
1704
- end)
1750
+ sep = if separator.value == 'auto'
1751
+ list.separator || :space
1752
+ else
1753
+ separator.value.to_sym
1754
+ end
1755
+ list(list.to_a + [val], sep)
1705
1756
  end
1706
1757
  declare :append, [:list, :val]
1707
1758
  declare :append, [:list, :val, :separator]
@@ -1731,11 +1782,10 @@ module Sass::Script
1731
1782
  value.slice!(length)
1732
1783
  end
1733
1784
  new_list_value = values.first.zip(*values[1..-1])
1734
- Sass::Script::Value::List.new(new_list_value.map{|list| Sass::Script::Value::List.new(list, :space)}, :comma)
1785
+ list(new_list_value.map {|list| list(list, :space)}, :comma)
1735
1786
  end
1736
1787
  declare :zip, [], :var_args => true
1737
1788
 
1738
-
1739
1789
  # Returns the position of a value within a list. If the value isn't found,
1740
1790
  # returns false instead.
1741
1791
  #
@@ -1754,11 +1804,11 @@ module Sass::Script
1754
1804
  # @return [Sass::Script::Value::Number, Sass::Script::Value::Bool] The
1755
1805
  # 1-based index of `$value` in `$list`, or `false`
1756
1806
  def index(list, value)
1757
- index = list.to_a.index {|e| e.eq(value).to_bool }
1807
+ index = list.to_a.index {|e| e.eq(value).to_bool}
1758
1808
  if index
1759
- Sass::Script::Value::Number.new(index + 1)
1809
+ number(index + 1)
1760
1810
  else
1761
- Sass::Script::Value::Bool::FALSE
1811
+ bool(false)
1762
1812
  end
1763
1813
  end
1764
1814
  declare :index, [:list, :value]
@@ -1774,7 +1824,7 @@ module Sass::Script
1774
1824
  # @param $list [Sass::Script::Value::Base]
1775
1825
  # @return [Sass::Script::Value::String] `comma` or `space`
1776
1826
  def list_separator(list)
1777
- Sass::Script::Value::String.new((list.separator || :space).to_s)
1827
+ identifier((list.separator || :space).to_s)
1778
1828
  end
1779
1829
  declare :separator, [:list]
1780
1830
 
@@ -1883,7 +1933,8 @@ module Sass::Script
1883
1933
  # @raise [ArgumentError] if `$args` isn't a variable argument list
1884
1934
  def keywords(args)
1885
1935
  assert_type args, :ArgList
1886
- Sass::Script::Value::Map.new(Sass::Util.map_keys(args.keywords) {|k| Sass::Script::String.new(k)})
1936
+ Sass::Script::Value::Map.new(
1937
+ Sass::Util.map_keys(args.keywords) {|k| Sass::Script::String.new(k)})
1887
1938
  end
1888
1939
  declare :keywords, [:args]
1889
1940
 
@@ -1920,7 +1971,7 @@ module Sass::Script
1920
1971
  # avoid the temptation of trying to guess the next unique value.
1921
1972
  value = (Thread.current[:sass_last_unique_id] += (rand(10) + 1))
1922
1973
  # the u makes this a legal identifier if it would otherwise start with a number.
1923
- Sass::Script::String.new("u" + value.to_s(36).rjust(8, '0'))
1974
+ identifier("u" + value.to_s(36).rjust(8, '0'))
1924
1975
  end
1925
1976
  declare :unique_id, []
1926
1977
 
@@ -1963,7 +2014,7 @@ module Sass::Script
1963
2014
  # @overload counter($args...)
1964
2015
  # @return [String]
1965
2016
  def counter(*args)
1966
- Sass::Script::Value::String.new("counter(#{args.map {|a| a.to_s(options)}.join(',')})")
2017
+ identifier("counter(#{args.map {|a| a.to_s(options)}.join(',')})")
1967
2018
  end
1968
2019
  declare :counter, [], :var_args => true
1969
2020
 
@@ -1989,10 +2040,14 @@ module Sass::Script
1989
2040
  # It yields a number to a block to perform the operation and return a number
1990
2041
  def numeric_transformation(value)
1991
2042
  assert_type value, :Number, :value
1992
- Sass::Script::Value::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
2043
+ Sass::Script::Value::Number.new(
2044
+ yield(value.value), value.numerator_units, value.denominator_units)
1993
2045
  end
1994
2046
 
2047
+ # @comment
2048
+ # rubocop:disable ParameterLists
1995
2049
  def _adjust(color, amount, attr, range, op, units = "")
2050
+ # rubocop:enable ParameterLists
1996
2051
  assert_type color, :Color, :color
1997
2052
  assert_type amount, :Number, :amount
1998
2053
  Sass::Util.check_range('Amount', range, amount, units)
@@ -2013,7 +2068,7 @@ DEPRECATION WARNING: Passing lists of pairs to #{fn_name} is deprecated and will
2013
2068
  be removed in future versions of Sass. Use Sass maps instead. For details, see
2014
2069
  http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#maps.
2015
2070
  WARNING
2016
- return obj.to_h
2071
+ obj.to_h
2017
2072
  end
2018
2073
  end
2019
2074
  end