sass 3.3.14 → 3.4.0.rc.1

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.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +5 -5
  4. data/VERSION +1 -1
  5. data/VERSION_DATE +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/sass +1 -1
  8. data/bin/scss +1 -1
  9. data/lib/sass.rb +0 -5
  10. data/lib/sass/css.rb +1 -3
  11. data/lib/sass/engine.rb +28 -39
  12. data/lib/sass/environment.rb +13 -17
  13. data/lib/sass/error.rb +6 -9
  14. data/lib/sass/exec.rb +5 -771
  15. data/lib/sass/exec/base.rb +187 -0
  16. data/lib/sass/exec/sass_convert.rb +264 -0
  17. data/lib/sass/exec/sass_scss.rb +419 -0
  18. data/lib/sass/features.rb +6 -0
  19. data/lib/sass/importers.rb +0 -1
  20. data/lib/sass/importers/base.rb +5 -1
  21. data/lib/sass/importers/filesystem.rb +4 -21
  22. data/lib/sass/media.rb +1 -4
  23. data/lib/sass/plugin/compiler.rb +32 -136
  24. data/lib/sass/script/css_lexer.rb +1 -1
  25. data/lib/sass/script/functions.rb +363 -39
  26. data/lib/sass/script/lexer.rb +68 -50
  27. data/lib/sass/script/parser.rb +29 -14
  28. data/lib/sass/script/tree.rb +1 -0
  29. data/lib/sass/script/tree/funcall.rb +1 -1
  30. data/lib/sass/script/tree/interpolation.rb +19 -1
  31. data/lib/sass/script/tree/selector.rb +26 -0
  32. data/lib/sass/script/value.rb +0 -1
  33. data/lib/sass/script/value/bool.rb +0 -5
  34. data/lib/sass/script/value/color.rb +32 -12
  35. data/lib/sass/script/value/helpers.rb +107 -0
  36. data/lib/sass/script/value/list.rb +0 -15
  37. data/lib/sass/script/value/null.rb +0 -5
  38. data/lib/sass/script/value/number.rb +60 -14
  39. data/lib/sass/script/value/string.rb +53 -9
  40. data/lib/sass/scss/css_parser.rb +8 -2
  41. data/lib/sass/scss/parser.rb +175 -319
  42. data/lib/sass/scss/rx.rb +14 -5
  43. data/lib/sass/scss/static_parser.rb +298 -1
  44. data/lib/sass/selector.rb +56 -193
  45. data/lib/sass/selector/abstract_sequence.rb +28 -13
  46. data/lib/sass/selector/comma_sequence.rb +91 -12
  47. data/lib/sass/selector/pseudo.rb +256 -0
  48. data/lib/sass/selector/sequence.rb +99 -31
  49. data/lib/sass/selector/simple.rb +14 -25
  50. data/lib/sass/selector/simple_sequence.rb +101 -37
  51. data/lib/sass/shared.rb +1 -1
  52. data/lib/sass/source/map.rb +23 -9
  53. data/lib/sass/stack.rb +0 -6
  54. data/lib/sass/supports.rb +1 -1
  55. data/lib/sass/tree/at_root_node.rb +1 -0
  56. data/lib/sass/tree/directive_node.rb +7 -1
  57. data/lib/sass/tree/error_node.rb +18 -0
  58. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  59. data/lib/sass/tree/prop_node.rb +1 -1
  60. data/lib/sass/tree/rule_node.rb +11 -6
  61. data/lib/sass/tree/visitors/check_nesting.rb +3 -4
  62. data/lib/sass/tree/visitors/convert.rb +8 -17
  63. data/lib/sass/tree/visitors/cssize.rb +12 -24
  64. data/lib/sass/tree/visitors/deep_copy.rb +5 -0
  65. data/lib/sass/tree/visitors/perform.rb +43 -28
  66. data/lib/sass/tree/visitors/set_options.rb +5 -0
  67. data/lib/sass/tree/visitors/to_css.rb +14 -13
  68. data/lib/sass/util.rb +94 -90
  69. data/test/sass/cache_test.rb +1 -1
  70. data/test/sass/callbacks_test.rb +1 -1
  71. data/test/sass/compiler_test.rb +5 -14
  72. data/test/sass/conversion_test.rb +47 -1
  73. data/test/sass/css2sass_test.rb +3 -3
  74. data/test/sass/encoding_test.rb +219 -0
  75. data/test/sass/engine_test.rb +128 -191
  76. data/test/sass/exec_test.rb +2 -2
  77. data/test/sass/extend_test.rb +234 -17
  78. data/test/sass/functions_test.rb +268 -213
  79. data/test/sass/importer_test.rb +31 -21
  80. data/test/sass/logger_test.rb +1 -1
  81. data/test/sass/more_results/more_import.css +1 -1
  82. data/test/sass/plugin_test.rb +12 -11
  83. data/test/sass/results/compact.css +1 -1
  84. data/test/sass/results/complex.css +4 -4
  85. data/test/sass/results/expanded.css +1 -1
  86. data/test/sass/results/import.css +1 -1
  87. data/test/sass/results/import_charset_ibm866.css +2 -2
  88. data/test/sass/results/mixins.css +17 -17
  89. data/test/sass/results/nested.css +1 -1
  90. data/test/sass/results/parent_ref.css +2 -2
  91. data/test/sass/results/script.css +3 -3
  92. data/test/sass/results/scss_import.css +1 -1
  93. data/test/sass/script_conversion_test.rb +7 -4
  94. data/test/sass/script_test.rb +202 -79
  95. data/test/sass/scss/css_test.rb +95 -25
  96. data/test/sass/scss/rx_test.rb +4 -4
  97. data/test/sass/scss/scss_test.rb +363 -19
  98. data/test/sass/source_map_test.rb +48 -41
  99. data/test/sass/superselector_test.rb +191 -0
  100. data/test/sass/templates/scss_import.scss +2 -1
  101. data/test/sass/test_helper.rb +1 -1
  102. data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
  103. data/test/sass/util/normalized_map_test.rb +1 -1
  104. data/test/sass/util/subset_map_test.rb +2 -2
  105. data/test/sass/util_test.rb +1 -1
  106. data/test/sass/value_helpers_test.rb +3 -3
  107. data/test/test_helper.rb +2 -2
  108. metadata +30 -7
  109. data/lib/sass/importers/deprecated_path.rb +0 -51
  110. data/lib/sass/script/value/deprecated_false.rb +0 -55
@@ -64,17 +64,16 @@ module Sass
64
64
  filename.start_with?(root + File::SEPARATOR)
65
65
  end
66
66
 
67
- def public_url(name, sourcemap_directory = nil)
67
+ def public_url(name, sourcemap_directory)
68
+ file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
68
69
  if sourcemap_directory.nil?
69
- warn_about_public_url(name)
70
+ Sass::Util.file_uri_from_path(file_pathname)
70
71
  else
71
- file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
72
72
  sourcemap_pathname = Sass::Util.cleanpath(sourcemap_directory)
73
73
  begin
74
74
  Sass::Util.file_uri_from_path(file_pathname.relative_path_from(sourcemap_pathname))
75
75
  rescue ArgumentError # when a relative path cannot be constructed
76
- warn_about_public_url(name)
77
- nil
76
+ Sass::Util.file_uri_from_path(file_pathname)
78
77
  end
79
78
  end
80
79
  end
@@ -196,22 +195,6 @@ WARNING
196
195
  [dirname, basename, extension]
197
196
  end
198
197
 
199
- # Issues a warning about being unable to determine a public url.
200
- #
201
- # @param uri [String] A URI known to be valid for this importer.
202
- # @return [NilClass] nil
203
- def warn_about_public_url(uri)
204
- @warnings_issued ||= Set.new
205
- unless @warnings_issued.include?(uri)
206
- Sass::Util.sass_warn <<WARNING
207
- WARNING: Couldn't determine public URL for "#{uri}" while generating sourcemap.
208
- Without a public URL, there's nothing for the source map to link to.
209
- WARNING
210
- @warnings_issued << uri
211
- end
212
- nil
213
- end
214
-
215
198
  private
216
199
 
217
200
  def _find(dir, name, options)
@@ -205,9 +205,6 @@ module Sass::Media
205
205
  # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
206
206
  # @return [String]
207
207
  def self._interp_to_src(interp, options)
208
- interp.map do |r|
209
- next r if r.is_a?(String)
210
- "\#{#{r.to_sass(options)}}"
211
- end.join
208
+ interp.map {|r| r.is_a?(String) ? r : r.to_sass(options)}.join
212
209
  end
213
210
  end
@@ -36,30 +36,19 @@ module Sass::Plugin
36
36
  options.merge!(opts)
37
37
  end
38
38
 
39
- # Register a callback to be run before stylesheets are mass-updated.
39
+ # Register a callback to be run after stylesheets are mass-updated.
40
40
  # This is run whenever \{#update\_stylesheets} is called,
41
41
  # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
42
42
  # is enabled.
43
43
  #
44
- # @yield [files]
45
- # @yieldparam files [<(String, String, String)>]
46
- # Individual files to be updated. Files in directories specified are included in this list.
44
+ # @yield [individual_files]
45
+ # @yieldparam individual_files [<(String, String)>]
46
+ # Individual files to be updated, in addition to the directories
47
+ # specified in the options.
47
48
  # The first element of each pair is the source file,
48
- # the second is the target CSS file,
49
- # the third is the target sourcemap file.
49
+ # the second is the target CSS file.
50
50
  define_callback :updating_stylesheets
51
51
 
52
- # Register a callback to be run after stylesheets are mass-updated.
53
- # This is run whenever \{#update\_stylesheets} is called,
54
- # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
55
- # is enabled.
56
- #
57
- # @yield [updated_files]
58
- # @yieldparam updated_files [<(String, String)>]
59
- # Individual files that were updated.
60
- # The first element of each pair is the source file, the second is the target CSS file.
61
- define_callback :updated_stylesheets
62
-
63
52
  # Register a callback to be run after a single stylesheet is updated.
64
53
  # The callback is only run if the stylesheet is really updated;
65
54
  # if the CSS file is fresh, this won't be run.
@@ -78,21 +67,6 @@ module Sass::Plugin
78
67
  # The location of the sourcemap being generated, if any.
79
68
  define_callback :updated_stylesheet
80
69
 
81
- # Register a callback to be run when compilation starts.
82
- #
83
- # In combination with on_updated_stylesheet, this could be used
84
- # to collect compilation statistics like timing or to take a
85
- # diff of the changes to the output file.
86
- #
87
- # @yield [template, css, sourcemap]
88
- # @yieldparam template [String]
89
- # The location of the Sass/SCSS file being updated.
90
- # @yieldparam css [String]
91
- # The location of the CSS file being generated.
92
- # @yieldparam sourcemap [String]
93
- # The location of the sourcemap being generated, if any.
94
- define_callback :compilation_starting
95
-
96
70
  # Register a callback to be run when Sass decides not to update a stylesheet.
97
71
  # In particular, the callback is run when Sass finds that
98
72
  # the template file and none of its dependencies
@@ -165,8 +139,7 @@ module Sass::Plugin
165
139
  define_callback :template_deleted
166
140
 
167
141
  # Register a callback to be run when Sass deletes a CSS file.
168
- # This happens when the corresponding Sass/SCSS file has been deleted
169
- # and when the compiler cleans the output files.
142
+ # This happens when the corresponding Sass/SCSS file has been deleted.
170
143
  #
171
144
  # @yield [filename]
172
145
  # @yieldparam filename [String]
@@ -174,8 +147,7 @@ module Sass::Plugin
174
147
  define_callback :deleting_css
175
148
 
176
149
  # Register a callback to be run when Sass deletes a sourcemap file.
177
- # This happens when the corresponding Sass/SCSS file has been deleted
178
- # and when the compiler cleans the output files.
150
+ # This happens when the corresponding Sass/SCSS file has been deleted.
179
151
  #
180
152
  # @yield [filename]
181
153
  # @yieldparam filename [String]
@@ -190,72 +162,35 @@ module Sass::Plugin
190
162
  # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
191
163
  # If it has, it updates the CSS file.
192
164
  #
193
- # @param individual_files [Array<(String, String[, String])>]
165
+ # @param individual_files [Array<(String, String)>]
194
166
  # A list of files to check for updates
195
167
  # **in addition to those specified by the
196
168
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
197
169
  # The first string in each pair is the location of the Sass/SCSS file,
198
170
  # the second is the location of the CSS file that it should be compiled to.
199
- # The third string, if provided, is the location of the Sourcemap file.
200
171
  def update_stylesheets(individual_files = [])
172
+ individual_files = individual_files.dup
201
173
  Sass::Plugin.checked_for_updates = true
202
174
  staleness_checker = StalenessChecker.new(engine_options)
203
175
 
204
- files = file_list(individual_files)
205
- run_updating_stylesheets(files)
176
+ template_location_array.each do |template_location, css_location|
177
+ Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
178
+ # Get the relative path to the file
179
+ name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
180
+ css = css_filename(name, css_location)
181
+ sourcemap = Sass::Util.sourcemap_name(css) if engine_options[:sourcemap] != :none
182
+ individual_files << [file, css, sourcemap]
183
+ end
184
+ end
206
185
 
207
- updated_stylesheets = []
208
- files.each do |file, css, sourcemap|
186
+ individual_files.each do |file, css, sourcemap|
209
187
  # TODO: Does staleness_checker need to check the sourcemap file as well?
210
188
  if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
211
- # XXX For consistency, this should return the sourcemap too, but it would
212
- # XXX be an API change.
213
- updated_stylesheets << [file, css]
214
189
  update_stylesheet(file, css, sourcemap)
215
190
  else
216
191
  run_not_updating_stylesheet(file, css, sourcemap)
217
192
  end
218
193
  end
219
- run_updated_stylesheets(updated_stylesheets)
220
- end
221
-
222
- # Construct a list of files that might need to be compiled
223
- # from the provided individual_files and the template_locations.
224
- #
225
- # Note: this method does not cache the results as they can change
226
- # across invocations when sass files are added or removed.
227
- #
228
- # @param individual_files [Array<(String, String[, String])>]
229
- # A list of files to check for updates
230
- # **in addition to those specified by the
231
- # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
232
- # The first string in each pair is the location of the Sass/SCSS file,
233
- # the second is the location of the CSS file that it should be compiled to.
234
- # The third string, if provided, is the location of the Sourcemap file.
235
- # @return [Array<(String, String, String)>]
236
- # A list of [sass_file, css_file, sourcemap_file] tuples similar
237
- # to what was passed in, but expanded to include the current state
238
- # of the directories being updated.
239
- def file_list(individual_files = [])
240
- files = individual_files.map do |tuple|
241
- if tuple.size < 3
242
- [tuple[0], tuple[1], Sass::Util.sourcemap_name(tuple[1])]
243
- else
244
- tuple
245
- end
246
- end
247
-
248
- template_location_array.each do |template_location, css_location|
249
- Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
250
- # Get the relative path to the file
251
- name = Sass::Util.pathname(file).relative_path_from(
252
- Sass::Util.pathname(template_location.to_s)).to_s
253
- css = css_filename(name, css_location)
254
- sourcemap = Sass::Util.sourcemap_name(css) if engine_options[:sourcemap]
255
- files << [file, css, sourcemap]
256
- end
257
- end
258
- files
259
194
  end
260
195
 
261
196
  # Watches the template directory (or directories)
@@ -276,19 +211,14 @@ module Sass::Plugin
276
211
  # The version of Listen distributed with Sass is loaded by default,
277
212
  # but if another version has already been loaded that will be used instead.
278
213
  #
279
- # @param individual_files [Array<(String, String[, String])>]
280
- # A list of files to check for updates
214
+ # @param individual_files [Array<(String, String)>]
215
+ # A list of files to watch for updates
281
216
  # **in addition to those specified by the
282
217
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
283
218
  # The first string in each pair is the location of the Sass/SCSS file,
284
219
  # the second is the location of the CSS file that it should be compiled to.
285
- # The third string, if provided, is the location of the Sourcemap file.
286
- # @param options [Hash] The options that control how watching works.
287
- # @option options [Boolean] :skip_initial_update
288
- # Don't do an initial update when starting the watcher when true
289
- def watch(individual_files = [], options = {})
290
- options, individual_files = individual_files, [] if individual_files.is_a?(Hash)
291
- update_stylesheets(individual_files) unless options[:skip_initial_update]
220
+ def watch(individual_files = [])
221
+ update_stylesheets(individual_files)
292
222
 
293
223
  directories = watched_paths
294
224
  individual_files.each do |(source, _, _)|
@@ -305,11 +235,7 @@ module Sass::Plugin
305
235
 
306
236
  # TODO: Keep better track of what depends on what
307
237
  # so we don't have to run a global update every time anything changes.
308
- # XXX The :additional_watch_paths option exists for Compass to use until
309
- # a deprecated feature is removed. It may be removed without warning.
310
- listener_args = directories +
311
- Array(options[:additional_watch_paths]) +
312
- [{:relative_paths => false}]
238
+ listener_args = directories + [{:relative_paths => false}]
313
239
 
314
240
  # The native windows listener is much slower than the polling option, according to
315
241
  # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
@@ -322,7 +248,6 @@ module Sass::Plugin
322
248
 
323
249
  listener = create_listener(*listener_args) do |modified, added, removed|
324
250
  on_file_changed(individual_files, modified, added, removed)
325
- yield(modified, added, removed) if block_given?
326
251
  end
327
252
 
328
253
  if poll && !Sass::Util.listen_geq_2?
@@ -350,42 +275,12 @@ module Sass::Plugin
350
275
  StalenessChecker.stylesheet_needs_update?(css_file, template_file)
351
276
  end
352
277
 
353
- # Remove all output files that would be created by calling update_stylesheets, if they exist.
354
- #
355
- # This method runs the deleting_css and deleting_sourcemap callbacks for
356
- # the files that are deleted.
357
- #
358
- # @param individual_files [Array<(String, String[, String])>]
359
- # A list of files to check for updates
360
- # **in addition to those specified by the
361
- # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
362
- # The first string in each pair is the location of the Sass/SCSS file,
363
- # the second is the location of the CSS file that it should be compiled to.
364
- # The third string, if provided, is the location of the Sourcemap file.
365
- def clean(individual_files = [])
366
- file_list(individual_files).each do |(_, css_file, sourcemap_file)|
367
- if File.exist?(css_file)
368
- run_deleting_css css_file
369
- File.delete(css_file)
370
- end
371
- if sourcemap_file && File.exist?(sourcemap_file)
372
- run_deleting_sourcemap sourcemap_file
373
- File.delete(sourcemap_file)
374
- end
375
- end
376
- nil
377
- end
378
-
379
278
  private
380
279
 
381
280
  def create_listener(*args, &block)
382
281
  Sass::Util.load_listen!
383
282
  if Sass::Util.listen_geq_2?
384
- # Work around guard/listen#243.
385
- options = args.pop if args.last.is_a?(Hash)
386
- args.map do |dir|
387
- Listen.to(dir, options, &block)
388
- end
283
+ Listen.to(*args, &block)
389
284
  else
390
285
  Listen::Listener.new(*args, &block)
391
286
  end
@@ -393,7 +288,7 @@ module Sass::Plugin
393
288
 
394
289
  def listen_to(listener)
395
290
  if Sass::Util.listen_geq_2?
396
- listener.map {|l| l.start}.each {|thread| thread.join}
291
+ listener.start.join
397
292
  else
398
293
  listener.start!
399
294
  end
@@ -433,7 +328,6 @@ module Sass::Plugin
433
328
  end
434
329
 
435
330
  removed.uniq.each do |f|
436
- run_template_deleted(relative_to_pwd(f))
437
331
  if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
438
332
  recompile_required = true
439
333
  # This was a file we were watching explicitly and compiling to a particular location.
@@ -453,6 +347,7 @@ module Sass::Plugin
453
347
  end
454
348
  end
455
349
  end
350
+ run_template_deleted(relative_to_pwd(f))
456
351
  end
457
352
 
458
353
  if recompile_required
@@ -476,7 +371,6 @@ module Sass::Plugin
476
371
  :filename => filename,
477
372
  :sourcemap_filename => sourcemap)
478
373
  mapping = nil
479
- run_compilation_starting(filename, css, sourcemap)
480
374
  engine = Sass::Engine.for_file(filename, engine_opts)
481
375
  if sourcemap
482
376
  rendered, mapping = engine.render_with_sourcemap(File.basename(sourcemap))
@@ -486,12 +380,14 @@ module Sass::Plugin
486
380
  rescue StandardError => e
487
381
  compilation_error_occured = true
488
382
  run_compilation_error e, filename, css, sourcemap
489
- rendered = Sass::SyntaxError.exception_to_css(e, options)
383
+ raise e unless options[:full_exception]
384
+ rendered = Sass::SyntaxError.exception_to_css(e, options[:line] || 1)
490
385
  end
491
386
 
492
387
  write_file(css, rendered)
493
388
  if mapping
494
- write_file(sourcemap, mapping.to_json(:css_path => css, :sourcemap_path => sourcemap))
389
+ write_file(sourcemap, mapping.to_json(
390
+ :css_path => css, :sourcemap_path => sourcemap, :type => options[:sourcemap]))
495
391
  end
496
392
  run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occured
497
393
  end
@@ -18,7 +18,7 @@ module Sass
18
18
  end
19
19
 
20
20
  return unless scan(STRING)
21
- string_value = (@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1')
21
+ string_value = Sass::Script::Value::String.value(@scanner[1] || @scanner[2])
22
22
  value = Script::Value::String.new(string_value, :string)
23
23
  [:string, value]
24
24
  end
@@ -193,8 +193,8 @@ module Sass::Script
193
193
  # \{#map_merge map-merge($map1, $map2)}
194
194
  # : Merges two maps together into a new map.
195
195
  #
196
- # \{#map_remove map-remove($map, $key)}
197
- # : Returns a new map with a key removed.
196
+ # \{#map_remove map-remove($map, $keys...)}
197
+ # : Returns a new map with keys removed.
198
198
  #
199
199
  # \{#map_keys map-keys($map)}
200
200
  # : Returns a list of all keys in a map.
@@ -208,6 +208,48 @@ module Sass::Script
208
208
  # \{#keywords keywords($args)}
209
209
  # : Returns the keywords passed to a function that takes variable arguments.
210
210
  #
211
+ # ## Selector Functions
212
+ #
213
+ # Selector functions are very liberal in the formats they support
214
+ # for selector arguments. They can take a plain string, a list of
215
+ # lists as returned by `&` or anything in between:
216
+ #
217
+ # * A plain sring, such as `".foo .bar, .baz .bang"`.
218
+ # * A space-separated list of strings such as `(".foo" ".bar")`.
219
+ # * A comma-separated list of strings such as `(".foo .bar", ".baz .bang")`.
220
+ # * A comma-separated list of space-separated lists of strings such
221
+ # as `((".foo" ".bar"), (".baz" ".bang"))`.
222
+ #
223
+ # In general, selector functions allow placeholder selectors
224
+ # (`%foo`) but disallow parent-reference selectors (`&`).
225
+ #
226
+ # \{#selector_nest selector-nest($selectors...)}
227
+ # : Nests selector beneath one another like they would be nested in the
228
+ # stylesheet.
229
+ #
230
+ # \{#selector_append selector-append($selectors...)}
231
+ # : Appends selectors to one another without spaces in between.
232
+ #
233
+ # \{#selector_extend selector-extend($selector, $extendee, $extender)}
234
+ # : Extends `$extendee` with `$extender` within `$selector`.
235
+ #
236
+ # \{#selector_replace selector-replace($selector, $original, $replacement)}
237
+ # : Replaces `$original` with `$replacement` within `$selector`.
238
+ #
239
+ # \{#selector_unify selector-unify($selector1, $selector2)}
240
+ # : Unifies two selectors to produce a selector that matches
241
+ # elements matched by both.
242
+ #
243
+ # \{#is_superselector is-superselector($super, $sub)}
244
+ # : Returns whether `$super` matches all the elements `$sub` does, and
245
+ # possibly more.
246
+ #
247
+ # \{#simple_selectors simple-selectors($selector)}
248
+ # : Returns the simple selectors that comprise a compound selector.
249
+ #
250
+ # \{#selector_parse selector-parse($selector)}
251
+ # : Parses a selector into the format returned by `&`.
252
+ #
211
253
  # ## Introspection Functions
212
254
  #
213
255
  # \{#feature_exists feature-exists($feature)}
@@ -477,7 +519,7 @@ module Sass::Script
477
519
  def assert_type(value, type, name = nil)
478
520
  klass = Sass::Script::Value.const_get(type)
479
521
  return if value.is_a?(klass)
480
- return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.is_pseudo_map?
522
+ return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.value.empty?
481
523
  err = "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
482
524
  err = "$#{name.to_s.gsub('_', '-')}: " + err if name
483
525
  raise ArgumentError.new(err)
@@ -594,6 +636,10 @@ module Sass::Script
594
636
  raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
595
637
  end
596
638
  end
639
+
640
+ # Don't store the string representation for function-created colors, both
641
+ # because it's not very useful and because some functions aren't supported
642
+ # on older browsers.
597
643
  Sass::Script::Value::Color.new(color_attrs)
598
644
  end
599
645
  declare :rgb, [:red, :green, :blue]
@@ -701,6 +747,9 @@ module Sass::Script
701
747
  s = Sass::Util.check_range('Saturation', 0..100, saturation, '%')
702
748
  l = Sass::Util.check_range('Lightness', 0..100, lightness, '%')
703
749
 
750
+ # Don't store the string representation for function-created colors, both
751
+ # because it's not very useful and because some functions aren't supported
752
+ # on older browsers.
704
753
  Sass::Script::Value::Color.new(
705
754
  :hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
706
755
  end
@@ -1269,8 +1318,8 @@ module Sass::Script
1269
1318
  rgba << color1.alpha * p + color2.alpha * (1 - p)
1270
1319
  rgb_color(*rgba)
1271
1320
  end
1272
- declare :mix, [:color1, :color2], :deprecated => [:color_1, :color_2]
1273
- declare :mix, [:color1, :color2, :weight], :deprecated => [:color_1, :color_2, :weight]
1321
+ declare :mix, [:color1, :color2]
1322
+ declare :mix, [:color1, :color2, :weight]
1274
1323
 
1275
1324
  # Converts a color to grayscale. This is identical to `desaturate(color,
1276
1325
  # 100%)`.
@@ -1465,7 +1514,6 @@ module Sass::Script
1465
1514
  end_at = number(-1) if end_at.nil?
1466
1515
  assert_unit end_at, nil, "end-at"
1467
1516
 
1468
- return Sass::Script::Value::String.new("", string.type) if end_at.value == 0
1469
1517
  s = start_at.value > 0 ? start_at.value - 1 : start_at.value
1470
1518
  e = end_at.value > 0 ? end_at.value - 1 : end_at.value
1471
1519
  s = string.value.length + s if s < 0
@@ -1528,6 +1576,19 @@ module Sass::Script
1528
1576
 
1529
1577
  # Returns whether a feature exists in the current Sass runtime.
1530
1578
  #
1579
+ # The following features are supported:
1580
+ #
1581
+ # * `global-variable-exists` indicates that a local variable will shadow a
1582
+ # global variable unless `!global` is used.
1583
+ #
1584
+ # * `extend-selector-pseudoclass` indicates that `@extend` will reach into
1585
+ # selector pseudoclasses like `:not`.
1586
+ #
1587
+ # * `units-level-3` indicates full support for unit arithmetic using units
1588
+ # defined in the [Values and Units Level 3][] spec.
1589
+ #
1590
+ # [Values and Units Level 3]: http://www.w3.org/TR/css3-values/
1591
+ #
1531
1592
  # @example
1532
1593
  # feature-exists(some-feature-that-exists) => true
1533
1594
  # feature-exists(what-is-this-i-dont-know) => false
@@ -1593,7 +1654,7 @@ module Sass::Script
1593
1654
  assert_type number2, :Number, :number2
1594
1655
  bool(number1.comparable_to?(number2))
1595
1656
  end
1596
- declare :comparable, [:number1, :number2], :deprecated => [:number_1, :number_2]
1657
+ declare :comparable, [:number1, :number2]
1597
1658
 
1598
1659
  # Converts a unitless number to a percentage.
1599
1660
  #
@@ -1610,7 +1671,7 @@ module Sass::Script
1610
1671
  end
1611
1672
  number(number.value * 100, '%')
1612
1673
  end
1613
- declare :percentage, [:number], :deprecated => [:value]
1674
+ declare :percentage, [:number]
1614
1675
 
1615
1676
  # Rounds a number to the nearest whole number.
1616
1677
  #
@@ -1624,7 +1685,7 @@ module Sass::Script
1624
1685
  def round(number)
1625
1686
  numeric_transformation(number) {|n| n.round}
1626
1687
  end
1627
- declare :round, [:number], :deprecated => [:value]
1688
+ declare :round, [:number]
1628
1689
 
1629
1690
  # Rounds a number up to the next whole number.
1630
1691
  #
@@ -1638,7 +1699,7 @@ module Sass::Script
1638
1699
  def ceil(number)
1639
1700
  numeric_transformation(number) {|n| n.ceil}
1640
1701
  end
1641
- declare :ceil, [:number], :deprecated => [:value]
1702
+ declare :ceil, [:number]
1642
1703
 
1643
1704
  # Rounds a number down to the previous whole number.
1644
1705
  #
@@ -1652,7 +1713,7 @@ module Sass::Script
1652
1713
  def floor(number)
1653
1714
  numeric_transformation(number) {|n| n.floor}
1654
1715
  end
1655
- declare :floor, [:number], :deprecated => [:value]
1716
+ declare :floor, [:number]
1656
1717
 
1657
1718
  # Returns the absolute value of a number.
1658
1719
  #
@@ -1666,7 +1727,7 @@ module Sass::Script
1666
1727
  def abs(number)
1667
1728
  numeric_transformation(number) {|n| n.abs}
1668
1729
  end
1669
- declare :abs, [:number], :deprecated => [:value]
1730
+ declare :abs, [:number]
1670
1731
 
1671
1732
  # Finds the minimum of several numbers. This function takes any number of
1672
1733
  # arguments.
@@ -1894,8 +1955,7 @@ module Sass::Script
1894
1955
  # 1-based index of `$value` in `$list`, or `null`
1895
1956
  def index(list, value)
1896
1957
  index = list.to_a.index {|e| e.eq(value).to_bool}
1897
- return number(index + 1) if index
1898
- Sass::Script::Value::DeprecatedFalse.new(environment)
1958
+ index ? number(index + 1) : null
1899
1959
  end
1900
1960
  declare :index, [:list, :value]
1901
1961
 
@@ -1929,7 +1989,7 @@ module Sass::Script
1929
1989
  # @raise [ArgumentError] if `$map` is not a map
1930
1990
  def map_get(map, key)
1931
1991
  assert_type map, :Map, :map
1932
- to_h(map)[key] || null
1992
+ map.to_h[key] || null
1933
1993
  end
1934
1994
  declare :map_get, [:map, :key]
1935
1995
 
@@ -1953,27 +2013,28 @@ module Sass::Script
1953
2013
  def map_merge(map1, map2)
1954
2014
  assert_type map1, :Map, :map1
1955
2015
  assert_type map2, :Map, :map2
1956
- map(to_h(map1).merge(to_h(map2)))
2016
+ map(map1.to_h.merge(map2.to_h))
1957
2017
  end
1958
2018
  declare :map_merge, [:map1, :map2]
1959
2019
 
1960
- # Returns a new map with a key removed.
2020
+ # Returns a new map with keys removed.
1961
2021
  #
1962
2022
  # @example
1963
2023
  # map-remove(("foo": 1, "bar": 2), "bar") => ("foo": 1)
2024
+ # map-remove(("foo": 1, "bar": 2, "baz": 3), "bar", "baz") => ("foo": 1)
1964
2025
  # map-remove(("foo": 1, "bar": 2), "baz") => ("foo": 1, "bar": 2)
1965
- # @overload map_remove($map, $key)
1966
- # @param $map [Sass::Script::Value::Map]
1967
- # @param $key [Sass::Script::Value::Base]
2026
+ # @overload map_remove($map, $keys...)
2027
+ # @param $map [Sass::Script::Value::Map]
2028
+ # @param $keys [[Sass::Script::Value::Base]]
1968
2029
  # @return [Sass::Script::Value::Map]
1969
2030
  # @raise [ArgumentError] if `$map` is not a map
1970
- def map_remove(map, key)
2031
+ def map_remove(map, *keys)
1971
2032
  assert_type map, :Map, :map
1972
- hash = to_h(map).dup
1973
- hash.delete key
2033
+ hash = map.to_h.dup
2034
+ hash.delete_if {|key, _| keys.include?(key)}
1974
2035
  map(hash)
1975
2036
  end
1976
- declare :map_remove, [:map, :key]
2037
+ declare :map_remove, [:map, :key], :var_args => true
1977
2038
 
1978
2039
  # Returns a list of all keys in a map.
1979
2040
  #
@@ -1985,7 +2046,7 @@ module Sass::Script
1985
2046
  # @raise [ArgumentError] if `$map` is not a map
1986
2047
  def map_keys(map)
1987
2048
  assert_type map, :Map, :map
1988
- list(to_h(map).keys, :comma)
2049
+ list(map.to_h.keys, :comma)
1989
2050
  end
1990
2051
  declare :map_keys, [:map]
1991
2052
 
@@ -2001,7 +2062,7 @@ module Sass::Script
2001
2062
  # @raise [ArgumentError] if `$map` is not a map
2002
2063
  def map_values(map)
2003
2064
  assert_type map, :Map, :map
2004
- list(to_h(map).values, :comma)
2065
+ list(map.to_h.values, :comma)
2005
2066
  end
2006
2067
  declare :map_values, [:map]
2007
2068
 
@@ -2017,7 +2078,7 @@ module Sass::Script
2017
2078
  # @raise [ArgumentError] if `$map` is not a map
2018
2079
  def map_has_key(map, key)
2019
2080
  assert_type map, :Map, :map
2020
- bool(to_h(map).has_key?(key))
2081
+ bool(map.to_h.has_key?(key))
2021
2082
  end
2022
2083
  declare :map_has_key, [:map, :key]
2023
2084
 
@@ -2251,6 +2312,281 @@ module Sass::Script
2251
2312
  declare :random, []
2252
2313
  declare :random, [:limit]
2253
2314
 
2315
+ # Parses a user-provided selector into a list of lists of strings
2316
+ # as returned by `&`.
2317
+ #
2318
+ # @example
2319
+ # selector-parse(".foo .bar, .baz .bang") => ('.foo' '.bar', '.baz' '.bang')
2320
+ #
2321
+ # @overload selector_parse($selector)
2322
+ # @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
2323
+ # The selector to parse. This can be either a string, a list of
2324
+ # strings, or a list of lists of strings as returned by `&`.
2325
+ # @return [Sass::Script::Value::List]
2326
+ # A list of lists of strings representing `$selector`. This is
2327
+ # in the same format as a selector returned by `&`.
2328
+ def selector_parse(selector)
2329
+ parse_selector(selector, :selector).to_sass_script
2330
+ end
2331
+ declare :selector_parse, [:selector]
2332
+
2333
+ # Return a new selector with all selectors in `$selectors` nested beneath
2334
+ # one another as though they had been nested in the stylesheet as
2335
+ # `$selector1 { $selector2 { ... } }`.
2336
+ #
2337
+ # Unlike most selector functions, `selector-nest` allows the
2338
+ # parent selector `&` to be used in any selector but the first.
2339
+ #
2340
+ # @example
2341
+ # selector-nest(".foo", ".bar", ".baz") => .foo .bar .baz
2342
+ # selector-nest(".a .foo", ".b .bar") => .a .foo .b .bar
2343
+ # selector-nest(".foo", "&.bar") => .foo.bar
2344
+ #
2345
+ # @overload selector_nest($selectors...)
2346
+ # @param $selectors [[Sass::Script::Value::String, Sass::Script::Value::List]]
2347
+ # The selectors to nest. At least one selector must be passed. Each of
2348
+ # these can be either a string, a list of strings, or a list of lists of
2349
+ # strings as returned by `&`.
2350
+ # @return [Sass::Script::Value::List]
2351
+ # A list of lists of strings representing the result of nesting
2352
+ # `$selectors`. This is in the same format as a selector returned by
2353
+ # `&`.
2354
+ def selector_nest(*selectors)
2355
+ if selectors.empty?
2356
+ raise ArgumentError.new("$selectors: At least one selector must be passed")
2357
+ end
2358
+
2359
+ parsed = [parse_selector(selectors.first, :selectors)]
2360
+ parsed += selectors[1..-1].map {|sel| parse_selector(sel, :selectors, !!:parse_parent_ref)}
2361
+ parsed.inject {|result, child| child.resolve_parent_refs(result)}.to_sass_script
2362
+ end
2363
+ declare :selector_nest, [], :var_args => true
2364
+
2365
+ # Return a new selector with all selectors in `$selectors` appended one
2366
+ # another as though they had been nested in the stylesheet as `$selector1 {
2367
+ # &$selector2 { ... } }`.
2368
+ #
2369
+ # @example
2370
+ # selector-append(".foo", ".bar", ".baz") => .foo.bar.baz
2371
+ # selector-append(".a .foo", ".b .bar") => "a .foo.b .bar"
2372
+ # selector-append(".foo", "-suffix") => ".foo-suffix"
2373
+ #
2374
+ # @overload selector_append($selectors...)
2375
+ # @param $selectors [[Sass::Script::Value::String, Sass::Script::Value::List]]
2376
+ # The selectors to append. At least one selector must be passed. Each of
2377
+ # these can be either a string, a list of strings, or a list of lists of
2378
+ # strings as returned by `&`.
2379
+ # @return [Sass::Script::Value::List]
2380
+ # A list of lists of strings representing the result of appending
2381
+ # `$selectors`. This is in the same format as a selector returned by
2382
+ # `&`.
2383
+ # @raise [ArgumentError] if a selector could not be appended.
2384
+ def selector_append(*selectors)
2385
+ if selectors.empty?
2386
+ raise ArgumentError.new("$selectors: At least one selector must be passed")
2387
+ end
2388
+
2389
+ selectors.map {|sel| parse_selector(sel, :selectors)}.inject do |parent, child|
2390
+ child.members.each do |seq|
2391
+ sseq = seq.members.first
2392
+ unless sseq.is_a?(Sass::Selector::SimpleSequence)
2393
+ raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
2394
+ end
2395
+
2396
+ base = sseq.base
2397
+ case base
2398
+ when Sass::Selector::Universal
2399
+ raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
2400
+ when Sass::Selector::Element
2401
+ unless base.namespace.nil?
2402
+ raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
2403
+ end
2404
+ sseq.members[0] = Sass::Selector::Parent.new(base.name)
2405
+ else
2406
+ sseq.members.unshift Sass::Selector::Parent.new
2407
+ end
2408
+ end
2409
+ child.resolve_parent_refs(parent)
2410
+ end.to_sass_script
2411
+ end
2412
+ declare :selector_append, [], :var_args => true
2413
+
2414
+ # Returns a new version of `$selector` with `$extendee` extended
2415
+ # with `$extender`. This works just like the result of
2416
+ #
2417
+ # $selector { ... }
2418
+ # $extender { @extend $extendee }
2419
+ #
2420
+ # @example
2421
+ # selector-extend(".a .b", ".b", ".foo .bar") => .a .b, .a .foo .bar, .foo .a .bar
2422
+ #
2423
+ # @overload selector_extend($selector, $extendee, $extender)
2424
+ # @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
2425
+ # The selector within which `$extendee` is extended with
2426
+ # `$extender`. This can be either a string, a list of strings,
2427
+ # or a list of lists of strings as returned by `&`.
2428
+ # @param $extendee [Sass::Script::Value::String, Sass::Script::Value::List]
2429
+ # The selector being extended. This can be either a string, a
2430
+ # list of strings, or a list of lists of strings as returned
2431
+ # by `&`.
2432
+ # @param $extender [Sass::Script::Value::String, Sass::Script::Value::List]
2433
+ # The selector being injected into `$selector`. This can be
2434
+ # either a string, a list of strings, or a list of lists of
2435
+ # strings as returned by `&`.
2436
+ # @return [Sass::Script::Value::List]
2437
+ # A list of lists of strings representing the result of the
2438
+ # extension. This is in the same format as a selector returned
2439
+ # by `&`.
2440
+ # @raise [ArgumentError] if the extension fails
2441
+ def selector_extend(selector, extendee, extender)
2442
+ selector = parse_selector(selector, :selector)
2443
+ extendee = parse_selector(extendee, :extendee)
2444
+ extender = parse_selector(extender, :extender)
2445
+
2446
+ extends = Sass::Util::SubsetMap.new
2447
+ begin
2448
+ extender.populate_extends(extends, extendee)
2449
+ selector.do_extend(extends).to_sass_script
2450
+ rescue Sass::SyntaxError => e
2451
+ raise ArgumentError.new(e.to_s)
2452
+ end
2453
+ end
2454
+ declare :selector_extend, [:selector, :extendee, :extender]
2455
+
2456
+ # Replaces all instances of `$original` with `$replacement` in `$selector`
2457
+ #
2458
+ # This works by using `@extend` and throwing away the original
2459
+ # selector. This means that it can be used to do very advanced
2460
+ # replacements; see the examples below.
2461
+ #
2462
+ # @example
2463
+ # selector-replace(".foo .bar", ".bar", ".baz") => ".foo .baz"
2464
+ # selector-replace(".foo.bar.baz", ".foo.baz", ".qux") => ".foo.qux"
2465
+ #
2466
+ # @overload selector_replace($selector, $original, $replacement)
2467
+ # @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
2468
+ # The selector within which `$original` is replaced with
2469
+ # `$replacement`. This can be either a string, a list of
2470
+ # strings, or a list of lists of strings as returned by `&`.
2471
+ # @param $original [Sass::Script::Value::String, Sass::Script::Value::List]
2472
+ # The selector being replaced. This can be either a string, a
2473
+ # list of strings, or a list of lists of strings as returned
2474
+ # by `&`.
2475
+ # @param $replacement [Sass::Script::Value::String, Sass::Script::Value::List]
2476
+ # The selector that `$original` is being replaced with. This
2477
+ # can be either a string, a list of strings, or a list of
2478
+ # lists of strings as returned by `&`.
2479
+ # @return [Sass::Script::Value::List]
2480
+ # A list of lists of strings representing the result of the
2481
+ # extension. This is in the same format as a selector returned
2482
+ # by `&`.
2483
+ # @raise [ArgumentError] if the replacement fails
2484
+ def selector_replace(selector, original, replacement)
2485
+ selector = parse_selector(selector, :selector)
2486
+ original = parse_selector(original, :original)
2487
+ replacement = parse_selector(replacement, :replacement)
2488
+
2489
+ extends = Sass::Util::SubsetMap.new
2490
+ begin
2491
+ replacement.populate_extends(extends, original)
2492
+ selector.do_extend(extends, [], !!:replace).to_sass_script
2493
+ rescue Sass::SyntaxError => e
2494
+ raise ArgumentError.new(e.to_s)
2495
+ end
2496
+ end
2497
+ declare :selector_replace, [:selector, :original, :replacement]
2498
+
2499
+ # Unifies two selectors into a single selector that matches only
2500
+ # elements matched by both input selectors. Returns `null` if
2501
+ # there is no such selector.
2502
+ #
2503
+ # Like the selector unification done for `@extend`, this doesn't
2504
+ # guarantee that the output selector will match *all* elements
2505
+ # matched by both input selectors. For example, if `.a .b` is
2506
+ # unified with `.x .y`, `.a .x .b.y, .x .a .b.y` will be returned,
2507
+ # but `.a.x .b.y` will not. This avoids exponential output size
2508
+ # while matching all elements that are likely to exist in
2509
+ # practice.
2510
+ #
2511
+ # @example
2512
+ # selector-unify(".a", ".b") => .a.b
2513
+ # selector-unify(".a .b", ".x .y") => .a .x .b.y, .x .a .b.y
2514
+ # selector-unify(".a.b", ".b.c") => .a.b.c
2515
+ # selector-unify("#a", "#b") => null
2516
+ #
2517
+ # @overload selector_unify($selector1, $selector2)
2518
+ # @param $selector1 [Sass::Script::Value::String, Sass::Script::Value::List]
2519
+ # The first selector to be unified. This can be either a
2520
+ # string, a list of strings, or a list of lists of strings as
2521
+ # returned by `&`.
2522
+ # @param $selector2 [Sass::Script::Value::String, Sass::Script::Value::List]
2523
+ # The second selector to be unified. This can be either a
2524
+ # string, a list of strings, or a list of lists of strings as
2525
+ # returned by `&`.
2526
+ # @return [Sass::Script::Value::List, Sass::Script::Value::Null]
2527
+ # A list of lists of strings representing the result of the
2528
+ # unification, or null if no unification exists. This is in
2529
+ # the same format as a selector returned by `&`.
2530
+ def selector_unify(selector1, selector2)
2531
+ selector1 = parse_selector(selector1, :selector1)
2532
+ selector2 = parse_selector(selector2, :selector2)
2533
+ return null unless (unified = selector1.unify(selector2))
2534
+ unified.to_sass_script
2535
+ end
2536
+ declare :selector_unify, [:selector1, :selector2]
2537
+
2538
+ # Returns the [simple
2539
+ # selectors](http://dev.w3.org/csswg/selectors4/#simple) that
2540
+ # comprise the compound selector `$selector`.
2541
+ #
2542
+ # Note that `$selector` **must be** a [compound
2543
+ # selector](http://dev.w3.org/csswg/selectors4/#compound). That
2544
+ # means it cannot contain commas or spaces. It also means that
2545
+ # unlike other selector functions, this takes only strings, not
2546
+ # lists.
2547
+ #
2548
+ # @example
2549
+ # simple-selectors(".foo.bar") => ".foo", ".bar"
2550
+ # simple-selectors(".foo.bar.baz") => ".foo", ".bar", ".baz"
2551
+ #
2552
+ # @overload simple_selectors($selector)
2553
+ # @param $selector [Sass::Script::Value::String]
2554
+ # The compound selector whose simple selectors will be extracted.
2555
+ # @return [Sass::Script::Value::List]
2556
+ # A list of simple selectors in the compound selector.
2557
+ def simple_selectors(selector)
2558
+ selector = parse_compound_selector(selector, :selector)
2559
+ list(selector.members.map {|simple| unquoted_string(simple.to_s)}, :comma)
2560
+ end
2561
+ declare :simple_selectors, [:selector]
2562
+
2563
+ # Returns whether `$super` is a superselector of `$sub`. This means that
2564
+ # `$super` matches all the elements that `$sub` matches, as well as possibly
2565
+ # additional elements. In general, simpler selectors tend to be
2566
+ # superselectors of more complex oned.
2567
+ #
2568
+ # @example
2569
+ # is-superselector(".foo", ".foo.bar") => true
2570
+ # is-superselector(".foo.bar", ".foo") => false
2571
+ # is-superselector(".bar", ".foo .bar") => true
2572
+ # is-superselector(".foo .bar", ".bar") => true
2573
+ #
2574
+ # @overload is_superselector($super, $sub)
2575
+ # @param $super [Sass::Script::Value::String, Sass::Script::Value::List]
2576
+ # The potential superselector. This can be either a string, a list of
2577
+ # strings, or a list of lists of strings as returned by `&`.
2578
+ # @param $sub [Sass::Script::Value::String, Sass::Script::Value::List]
2579
+ # The potential subselector. This can be either a string, a list of
2580
+ # strings, or a list of lists of strings as returned by `&`.
2581
+ # @return [Sass::Script::Value::Bool]
2582
+ # Whether `$selector1` is a superselector of `$selector2`.
2583
+ def is_superselector(sup, sub)
2584
+ sup = parse_selector(sup, :super)
2585
+ sub = parse_selector(sub, :sub)
2586
+ bool(sup.superselector?(sub))
2587
+ end
2588
+ declare :is_superselector, [:super, :sub]
2589
+
2254
2590
  private
2255
2591
 
2256
2592
  # This method implements the pattern of transforming a numeric value into
@@ -2276,17 +2612,5 @@ module Sass::Script
2276
2612
  color.with(attr => Sass::Util.restrict(
2277
2613
  color.send(attr).send(op, amount.value), range))
2278
2614
  end
2279
-
2280
- def to_h(obj)
2281
- return obj.to_h unless obj.is_a?(Sass::Script::Value::List) && obj.needs_map_warning?
2282
-
2283
- fn_name = Sass::Util.caller_info.last.gsub('_', '-')
2284
- Sass::Util.sass_warn <<WARNING + environment.stack.to_s.gsub(/^/, ' ')
2285
- DEPRECATION WARNING: Passing lists of pairs to #{fn_name} is deprecated and will
2286
- be removed in future versions of Sass. Use Sass maps instead. For details, see
2287
- http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#maps.
2288
- WARNING
2289
- obj.to_h
2290
- end
2291
2615
  end
2292
2616
  end