sass 3.3.14 → 3.4.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
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