sass 3.4.25 → 3.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +1 -1
  3. data/CONTRIBUTING.md +3 -3
  4. data/README.md +17 -9
  5. data/VERSION +1 -1
  6. data/VERSION_DATE +1 -1
  7. data/VERSION_NAME +1 -1
  8. data/extra/sass-spec-ref.sh +9 -1
  9. data/lib/sass.rb +0 -7
  10. data/lib/sass/cache_stores/filesystem.rb +1 -1
  11. data/lib/sass/css.rb +1 -2
  12. data/lib/sass/engine.rb +39 -29
  13. data/lib/sass/environment.rb +26 -5
  14. data/lib/sass/error.rb +2 -2
  15. data/lib/sass/exec/base.rb +2 -13
  16. data/lib/sass/exec/sass_scss.rb +1 -5
  17. data/lib/sass/features.rb +1 -0
  18. data/lib/sass/importers/filesystem.rb +6 -4
  19. data/lib/sass/logger/base.rb +11 -0
  20. data/lib/sass/plugin/compiler.rb +20 -50
  21. data/lib/sass/plugin/configuration.rb +2 -2
  22. data/lib/sass/railtie.rb +1 -1
  23. data/lib/sass/script/css_parser.rb +4 -1
  24. data/lib/sass/script/functions.rb +308 -81
  25. data/lib/sass/script/lexer.rb +63 -9
  26. data/lib/sass/script/parser.rb +287 -118
  27. data/lib/sass/script/tree/funcall.rb +35 -34
  28. data/lib/sass/script/tree/interpolation.rb +0 -3
  29. data/lib/sass/script/tree/list_literal.rb +23 -8
  30. data/lib/sass/script/tree/map_literal.rb +2 -2
  31. data/lib/sass/script/tree/node.rb +0 -8
  32. data/lib/sass/script/tree/operation.rb +1 -8
  33. data/lib/sass/script/value.rb +2 -0
  34. data/lib/sass/script/value/arg_list.rb +1 -1
  35. data/lib/sass/script/value/base.rb +17 -0
  36. data/lib/sass/script/value/callable.rb +25 -0
  37. data/lib/sass/script/value/color.rb +8 -2
  38. data/lib/sass/script/value/function.rb +19 -0
  39. data/lib/sass/script/value/helpers.rb +37 -11
  40. data/lib/sass/script/value/list.rb +35 -14
  41. data/lib/sass/script/value/map.rb +2 -2
  42. data/lib/sass/script/value/number.rb +3 -2
  43. data/lib/sass/scss/css_parser.rb +6 -1
  44. data/lib/sass/scss/parser.rb +145 -56
  45. data/lib/sass/scss/rx.rb +5 -11
  46. data/lib/sass/scss/static_parser.rb +20 -42
  47. data/lib/sass/selector.rb +4 -0
  48. data/lib/sass/selector/abstract_sequence.rb +7 -6
  49. data/lib/sass/selector/comma_sequence.rb +9 -5
  50. data/lib/sass/selector/pseudo.rb +20 -3
  51. data/lib/sass/selector/sequence.rb +35 -10
  52. data/lib/sass/selector/simple.rb +9 -2
  53. data/lib/sass/selector/simple_sequence.rb +8 -4
  54. data/lib/sass/source/map.rb +2 -6
  55. data/lib/sass/stack.rb +21 -1
  56. data/lib/sass/tree/charset_node.rb +1 -1
  57. data/lib/sass/tree/prop_node.rb +45 -53
  58. data/lib/sass/tree/rule_node.rb +6 -8
  59. data/lib/sass/tree/visitors/check_nesting.rb +1 -1
  60. data/lib/sass/tree/visitors/convert.rb +2 -3
  61. data/lib/sass/tree/visitors/cssize.rb +4 -15
  62. data/lib/sass/tree/visitors/deep_copy.rb +1 -1
  63. data/lib/sass/tree/visitors/extend.rb +2 -8
  64. data/lib/sass/tree/visitors/perform.rb +23 -15
  65. data/lib/sass/tree/visitors/set_options.rb +1 -1
  66. data/lib/sass/tree/visitors/to_css.rb +49 -22
  67. data/lib/sass/util.rb +72 -310
  68. data/lib/sass/util/multibyte_string_scanner.rb +127 -131
  69. data/lib/sass/util/normalized_map.rb +1 -8
  70. data/lib/sass/version.rb +0 -4
  71. metadata +55 -202
  72. data/Rakefile +0 -453
  73. data/lib/sass/script/css_variable_warning.rb +0 -52
  74. data/lib/sass/util/cross_platform_random.rb +0 -19
  75. data/lib/sass/util/ordered_hash.rb +0 -192
  76. data/test/sass-spec.yml +0 -3
  77. data/test/sass/cache_test.rb +0 -131
  78. data/test/sass/callbacks_test.rb +0 -61
  79. data/test/sass/compiler_test.rb +0 -236
  80. data/test/sass/conversion_test.rb +0 -2188
  81. data/test/sass/css2sass_test.rb +0 -526
  82. data/test/sass/css_variable_test.rb +0 -132
  83. data/test/sass/data/hsl-rgb.txt +0 -319
  84. data/test/sass/encoding_test.rb +0 -219
  85. data/test/sass/engine_test.rb +0 -3447
  86. data/test/sass/exec_test.rb +0 -96
  87. data/test/sass/extend_test.rb +0 -1733
  88. data/test/sass/fixtures/test_staleness_check_across_importers.css +0 -1
  89. data/test/sass/fixtures/test_staleness_check_across_importers.scss +0 -1
  90. data/test/sass/functions_test.rb +0 -1977
  91. data/test/sass/importer_test.rb +0 -421
  92. data/test/sass/logger_test.rb +0 -58
  93. data/test/sass/mock_importer.rb +0 -49
  94. data/test/sass/more_results/more1.css +0 -9
  95. data/test/sass/more_results/more1_with_line_comments.css +0 -26
  96. data/test/sass/more_results/more_import.css +0 -29
  97. data/test/sass/more_templates/_more_partial.sass +0 -2
  98. data/test/sass/more_templates/more1.sass +0 -23
  99. data/test/sass/more_templates/more_import.sass +0 -11
  100. data/test/sass/plugin_test.rb +0 -556
  101. data/test/sass/results/alt.css +0 -4
  102. data/test/sass/results/basic.css +0 -9
  103. data/test/sass/results/cached_import_option.css +0 -3
  104. data/test/sass/results/compact.css +0 -5
  105. data/test/sass/results/complex.css +0 -86
  106. data/test/sass/results/compressed.css +0 -1
  107. data/test/sass/results/expanded.css +0 -19
  108. data/test/sass/results/filename_fn.css +0 -3
  109. data/test/sass/results/if.css +0 -3
  110. data/test/sass/results/import.css +0 -31
  111. data/test/sass/results/import_charset.css +0 -5
  112. data/test/sass/results/import_charset_1_8.css +0 -5
  113. data/test/sass/results/import_charset_ibm866.css +0 -5
  114. data/test/sass/results/import_content.css +0 -1
  115. data/test/sass/results/line_numbers.css +0 -49
  116. data/test/sass/results/mixins.css +0 -95
  117. data/test/sass/results/multiline.css +0 -24
  118. data/test/sass/results/nested.css +0 -22
  119. data/test/sass/results/options.css +0 -1
  120. data/test/sass/results/parent_ref.css +0 -13
  121. data/test/sass/results/script.css +0 -16
  122. data/test/sass/results/scss_import.css +0 -31
  123. data/test/sass/results/scss_importee.css +0 -2
  124. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +0 -1
  125. data/test/sass/results/subdir/subdir.css +0 -3
  126. data/test/sass/results/units.css +0 -11
  127. data/test/sass/results/warn.css +0 -0
  128. data/test/sass/results/warn_imported.css +0 -0
  129. data/test/sass/script_conversion_test.rb +0 -357
  130. data/test/sass/script_test.rb +0 -1431
  131. data/test/sass/scss/css_test.rb +0 -1281
  132. data/test/sass/scss/rx_test.rb +0 -160
  133. data/test/sass/scss/scss_test.rb +0 -4205
  134. data/test/sass/scss/test_helper.rb +0 -37
  135. data/test/sass/source_map_test.rb +0 -1055
  136. data/test/sass/superselector_test.rb +0 -210
  137. data/test/sass/templates/_cached_import_option_partial.scss +0 -1
  138. data/test/sass/templates/_double_import_loop2.sass +0 -1
  139. data/test/sass/templates/_filename_fn_import.scss +0 -11
  140. data/test/sass/templates/_imported_charset_ibm866.sass +0 -4
  141. data/test/sass/templates/_imported_charset_utf8.sass +0 -4
  142. data/test/sass/templates/_imported_content.sass +0 -3
  143. data/test/sass/templates/_partial.sass +0 -2
  144. data/test/sass/templates/_same_name_different_partiality.scss +0 -1
  145. data/test/sass/templates/alt.sass +0 -16
  146. data/test/sass/templates/basic.sass +0 -23
  147. data/test/sass/templates/bork1.sass +0 -2
  148. data/test/sass/templates/bork2.sass +0 -2
  149. data/test/sass/templates/bork3.sass +0 -2
  150. data/test/sass/templates/bork4.sass +0 -2
  151. data/test/sass/templates/bork5.sass +0 -3
  152. data/test/sass/templates/cached_import_option.scss +0 -3
  153. data/test/sass/templates/compact.sass +0 -17
  154. data/test/sass/templates/complex.sass +0 -305
  155. data/test/sass/templates/compressed.sass +0 -15
  156. data/test/sass/templates/double_import_loop1.sass +0 -1
  157. data/test/sass/templates/expanded.sass +0 -17
  158. data/test/sass/templates/filename_fn.scss +0 -18
  159. data/test/sass/templates/if.sass +0 -11
  160. data/test/sass/templates/import.sass +0 -12
  161. data/test/sass/templates/import_charset.sass +0 -9
  162. data/test/sass/templates/import_charset_1_8.sass +0 -6
  163. data/test/sass/templates/import_charset_ibm866.sass +0 -11
  164. data/test/sass/templates/import_content.sass +0 -4
  165. data/test/sass/templates/importee.less +0 -2
  166. data/test/sass/templates/importee.sass +0 -19
  167. data/test/sass/templates/line_numbers.sass +0 -13
  168. data/test/sass/templates/mixin_bork.sass +0 -5
  169. data/test/sass/templates/mixins.sass +0 -76
  170. data/test/sass/templates/multiline.sass +0 -20
  171. data/test/sass/templates/nested.sass +0 -25
  172. data/test/sass/templates/nested_bork1.sass +0 -2
  173. data/test/sass/templates/nested_bork2.sass +0 -2
  174. data/test/sass/templates/nested_bork3.sass +0 -2
  175. data/test/sass/templates/nested_bork4.sass +0 -2
  176. data/test/sass/templates/nested_import.sass +0 -2
  177. data/test/sass/templates/nested_mixin_bork.sass +0 -6
  178. data/test/sass/templates/options.sass +0 -2
  179. data/test/sass/templates/parent_ref.sass +0 -25
  180. data/test/sass/templates/same_name_different_ext.sass +0 -2
  181. data/test/sass/templates/same_name_different_ext.scss +0 -1
  182. data/test/sass/templates/same_name_different_partiality.scss +0 -1
  183. data/test/sass/templates/script.sass +0 -101
  184. data/test/sass/templates/scss_import.scss +0 -12
  185. data/test/sass/templates/scss_importee.scss +0 -1
  186. data/test/sass/templates/single_import_loop.sass +0 -1
  187. data/test/sass/templates/subdir/import_up1.scss +0 -1
  188. data/test/sass/templates/subdir/import_up2.scss +0 -1
  189. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +0 -2
  190. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +0 -3
  191. data/test/sass/templates/subdir/subdir.sass +0 -6
  192. data/test/sass/templates/units.sass +0 -11
  193. data/test/sass/templates/warn.sass +0 -3
  194. data/test/sass/templates/warn_imported.sass +0 -4
  195. data/test/sass/test_helper.rb +0 -8
  196. data/test/sass/util/multibyte_string_scanner_test.rb +0 -155
  197. data/test/sass/util/normalized_map_test.rb +0 -51
  198. data/test/sass/util/subset_map_test.rb +0 -91
  199. data/test/sass/util_test.rb +0 -438
  200. data/test/sass/value_helpers_test.rb +0 -179
  201. data/test/test_helper.rb +0 -110
  202. data/vendor/listen/CHANGELOG.md +0 -1
  203. data/vendor/listen/CONTRIBUTING.md +0 -38
  204. data/vendor/listen/Gemfile +0 -20
  205. data/vendor/listen/Guardfile +0 -8
  206. data/vendor/listen/LICENSE +0 -20
  207. data/vendor/listen/README.md +0 -349
  208. data/vendor/listen/Rakefile +0 -5
  209. data/vendor/listen/Vagrantfile +0 -96
  210. data/vendor/listen/lib/listen.rb +0 -54
  211. data/vendor/listen/lib/listen/adapter.rb +0 -327
  212. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -75
  213. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -48
  214. data/vendor/listen/lib/listen/adapters/linux.rb +0 -81
  215. data/vendor/listen/lib/listen/adapters/polling.rb +0 -58
  216. data/vendor/listen/lib/listen/adapters/windows.rb +0 -91
  217. data/vendor/listen/lib/listen/directory_record.rb +0 -406
  218. data/vendor/listen/lib/listen/listener.rb +0 -323
  219. data/vendor/listen/lib/listen/turnstile.rb +0 -32
  220. data/vendor/listen/lib/listen/version.rb +0 -3
  221. data/vendor/listen/listen.gemspec +0 -28
  222. data/vendor/listen/spec/listen/adapter_spec.rb +0 -149
  223. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  224. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  225. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  226. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  227. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  228. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1250
  229. data/vendor/listen/spec/listen/listener_spec.rb +0 -258
  230. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  231. data/vendor/listen/spec/listen_spec.rb +0 -67
  232. data/vendor/listen/spec/spec_helper.rb +0 -25
  233. data/vendor/listen/spec/support/adapter_helper.rb +0 -666
  234. data/vendor/listen/spec/support/directory_record_helper.rb +0 -57
  235. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  236. data/vendor/listen/spec/support/listeners_helper.rb +0 -179
  237. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -12,6 +12,7 @@ module Sass
12
12
  extend-selector-pseudoclass
13
13
  units-level-3
14
14
  at-error
15
+ custom-property
15
16
  )]
16
17
 
17
18
  # Check if a feature exists by name. This is used to implement
@@ -67,7 +67,7 @@ module Sass
67
67
  end
68
68
 
69
69
  def public_url(name, sourcemap_directory)
70
- file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
70
+ file_pathname = Sass::Util.cleanpath(File.absolute_path(name, @root))
71
71
  return Sass::Util.file_uri_from_path(file_pathname) if sourcemap_directory.nil?
72
72
 
73
73
  sourcemap_pathname = Sass::Util.cleanpath(sourcemap_directory)
@@ -133,7 +133,7 @@ module Sass
133
133
 
134
134
  REDUNDANT_DIRECTORY = /#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}/
135
135
  # Given a base directory and an `@import`ed name,
136
- # finds an existant file that matches the name.
136
+ # finds an existent file that matches the name.
137
137
  #
138
138
  # @param dir [String] The directory relative to which to search.
139
139
  # @param name [String] The filename to search for.
@@ -154,7 +154,9 @@ module Sass
154
154
  [Sass::Util.cleanpath(full_path).to_s, s]
155
155
  end
156
156
  end.flatten(1)
157
- return if found.empty?
157
+ if found.empty? && split(name)[2].nil? && File.directory?("#{dir}/#{name}")
158
+ return find_real_file("#{dir}/#{name}", "index", options)
159
+ end
158
160
 
159
161
  if found.size > 1 && !@same_name_warnings.include?(found.first.first)
160
162
  found.each {|(f, _)| @same_name_warnings << f}
@@ -202,7 +204,7 @@ WARNING
202
204
 
203
205
  def _find(dir, name, options)
204
206
  full_filename, syntax = Sass::Util.destructure(find_real_file(dir, name, options))
205
- return unless full_filename && File.readable?(full_filename)
207
+ return unless full_filename && File.file?(full_filename) && File.readable?(full_filename)
206
208
 
207
209
  # TODO: this preserves historical behavior, but it's possible
208
210
  # :filename should be either normalized to the native format
@@ -22,6 +22,17 @@ class Sass::Logger::Base
22
22
  !disabled && self.class.log_level?(level, log_level)
23
23
  end
24
24
 
25
+ # Captures all logger messages emitted during a block and returns them as a
26
+ # string.
27
+ def capture
28
+ old_io = io
29
+ self.io = StringIO.new
30
+ yield
31
+ io.string
32
+ ensure
33
+ self.io = old_io
34
+ end
35
+
25
36
  def log(level, message)
26
37
  _log(level, message) if logging_level?(level)
27
38
  end
@@ -303,42 +303,30 @@ module Sass::Plugin
303
303
  directories += @inferred_directories
304
304
  directories = remove_redundant_directories(directories)
305
305
 
306
- # A Listen version prior to 2.0 will write a test file to a directory to
307
- # see if a watcher supports watching that directory. That breaks horribly
308
- # on read-only directories, so we filter those out.
309
- unless Sass::Util.listen_geq_2?
310
- directories = directories.select {|d| File.directory?(d) && File.writable?(d)}
311
- end
312
-
313
306
  # TODO: Keep better track of what depends on what
314
307
  # so we don't have to run a global update every time anything changes.
315
308
  # XXX The :additional_watch_paths option exists for Compass to use until
316
309
  # a deprecated feature is removed. It may be removed without warning.
317
- listener_args = directories +
318
- Array(options[:additional_watch_paths]) +
319
- [{:relative_paths => false}]
320
-
321
- # The native windows listener is much slower than the polling option, according to
322
- # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
323
- poll = @options[:poll] || Sass::Util.windows?
324
- if poll && Sass::Util.listen_geq_2?
325
- # In Listen 2.0.0 and on, :force_polling is an option. In earlier
326
- # versions, it's a method on the listener (called below).
327
- listener_args.last[:force_polling] = true
328
- end
310
+ directories += Array(options[:additional_watch_paths])
311
+
312
+ options = {
313
+ :relative_paths => false,
314
+ # The native windows listener is much slower than the polling option, according to
315
+ # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
316
+ :force_polling => @options[:poll] || Sass::Util.windows?
317
+ }
329
318
 
330
- listener = create_listener(*listener_args) do |modified, added, removed|
319
+ listener = create_listener(*directories, options) do |modified, added, removed|
331
320
  on_file_changed(individual_files, modified, added, removed)
332
321
  yield(modified, added, removed) if block_given?
333
322
  end
334
323
 
335
- if poll && !Sass::Util.listen_geq_2?
336
- # In Listen 2.0.0 and on, :force_polling is an option (set above). In
337
- # earlier versions, it's a method on the listener.
338
- listener.force_polling(true)
324
+ begin
325
+ listener.start
326
+ sleep
327
+ rescue Interrupt
328
+ # Squelch Interrupt for clean exit from Listen::Listener
339
329
  end
340
-
341
- listen_to(listener)
342
330
  end
343
331
 
344
332
  # Non-destructively modifies \{#options} so that default values are properly set,
@@ -387,28 +375,10 @@ module Sass::Plugin
387
375
 
388
376
  private
389
377
 
378
+ # This is mocked out in compiler_test.rb.
390
379
  def create_listener(*args, &block)
391
- Sass::Util.load_listen!
392
- if Sass::Util.listen_geq_2?
393
- # Work around guard/listen#243.
394
- options = args.pop if args.last.is_a?(Hash)
395
- args.map do |dir|
396
- Listen.to(dir, options, &block)
397
- end
398
- else
399
- Listen::Listener.new(*args, &block)
400
- end
401
- end
402
-
403
- def listen_to(listener)
404
- if Sass::Util.listen_geq_2?
405
- listener.map {|l| l.start}
406
- sleep
407
- else
408
- listener.start!
409
- end
410
- rescue Interrupt
411
- # Squelch Interrupt for clean exit from Listen::Listener
380
+ require 'sass-listen'
381
+ SassListen.to(*args, &block)
412
382
  end
413
383
 
414
384
  def remove_redundant_directories(directories)
@@ -494,7 +464,7 @@ module Sass::Plugin
494
464
  rendered = engine.render
495
465
  end
496
466
  rescue StandardError => e
497
- compilation_error_occured = true
467
+ compilation_error_occurred = true
498
468
  run_compilation_error e, filename, css, sourcemap
499
469
  raise e unless options[:full_exception]
500
470
  rendered = Sass::SyntaxError.exception_to_css(e, options[:line] || 1)
@@ -507,14 +477,14 @@ module Sass::Plugin
507
477
  mapping.to_json(
508
478
  :css_path => css, :sourcemap_path => sourcemap, :type => options[:sourcemap]))
509
479
  end
510
- run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occured
480
+ run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occurred
511
481
  end
512
482
 
513
483
  def write_file(fileName, content)
514
484
  flag = 'w'
515
485
  flag = 'wb' if Sass::Util.windows? && options[:unix_newlines]
516
486
  File.open(fileName, flag) do |file|
517
- file.set_encoding(content.encoding) unless Sass::Util.ruby1_8?
487
+ file.set_encoding(content.encoding)
518
488
  file.print(content)
519
489
  end
520
490
  end
@@ -26,8 +26,8 @@ module Sass
26
26
  clear_callbacks!
27
27
  end
28
28
 
29
- # An options hash.
30
- # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
29
+ # An options hash. See {file:SASS_REFERENCE.md#Options the Sass options
30
+ # documentation}.
31
31
  #
32
32
  # @return [{Symbol => Object}]
33
33
  def options
@@ -1,5 +1,5 @@
1
1
  # Rails 3.0.0.beta.2+, < 3.1
2
- if defined?(ActiveSupport) && Sass::Util.has?(:public_method, ActiveSupport, :on_load) &&
2
+ if defined?(ActiveSupport) && ActiveSupport.public_methods.include?(:on_load) &&
3
3
  !Sass::Util.ap_geq?('3.1.0.beta')
4
4
  require 'sass/plugin/configuration'
5
5
  ActiveSupport.on_load(:before_configuration) do
@@ -24,7 +24,10 @@ module Sass
24
24
  end
25
25
 
26
26
  # Short-circuit all the SassScript-only productions
27
- alias_method :interpolation, :space
27
+ def interpolation(first: nil, inner: :space)
28
+ first || send(inner)
29
+ end
30
+
28
31
  alias_method :or_expr, :div
29
32
  alias_method :unary_div, :ident
30
33
  alias_method :paren, :string
@@ -1,9 +1,7 @@
1
1
  require 'sass/script/value/helpers'
2
2
 
3
3
  module Sass::Script
4
- # @comment
5
4
  # YARD can't handle some multiline tags, and we need really long tags for function declarations.
6
- # rubocop:disable LineLength
7
5
  # Methods in this module are accessible from the SassScript context.
8
6
  # For example, you can write
9
7
  #
@@ -77,7 +75,7 @@ module Sass::Script
77
75
  # \{#complement complement($color)}
78
76
  # : Returns the complement of a color.
79
77
  #
80
- # \{#invert invert($color)}
78
+ # \{#invert invert($color, \[$weight\])}
81
79
  # : Returns the inverse of a color.
82
80
  #
83
81
  # ## Opacity Functions
@@ -176,7 +174,7 @@ module Sass::Script
176
174
  # \{#set-nth set-nth($list, $n, $value)}
177
175
  # : Replaces the nth item in a list.
178
176
  #
179
- # \{#join join($list1, $list2, \[$separator\])}
177
+ # \{#join join($list1, $list2, \[$separator, $bracketed\])}
180
178
  # : Joins together two lists into one.
181
179
  #
182
180
  # \{#append append($list1, $val, \[$separator\])}
@@ -191,6 +189,9 @@ module Sass::Script
191
189
  # \{#list_separator list-separator($list)}
192
190
  # : Returns the separator of a list.
193
191
  #
192
+ # \{#is_bracketed is-bracketed($list)}
193
+ # : Returns whether a list has square brackets.
194
+ #
194
195
  # ## Map Functions {#map-functions}
195
196
  #
196
197
  # Maps in Sass are immutable; all map functions return a new map rather than
@@ -276,6 +277,9 @@ module Sass::Script
276
277
  # \{#mixin_exists mixin-exists($name)}
277
278
  # : Returns whether a mixin with the given name exists.
278
279
  #
280
+ # \{#content_exists content-exists()}
281
+ # : Returns whether the current mixin was passed a content block.
282
+ #
279
283
  # \{#inspect inspect($value)}
280
284
  # : Returns the string representation of a value as it would be represented in Sass.
281
285
  #
@@ -291,8 +295,12 @@ module Sass::Script
291
295
  # \{#comparable comparable($number1, $number2)}
292
296
  # : Returns whether two numbers can be added, subtracted, or compared.
293
297
  #
294
- # \{#call call($name, $args...)}
295
- # : Dynamically calls a Sass function.
298
+ # \{#call call($function, $args...)}
299
+ # : Dynamically calls a Sass function reference returned by `get-function`.
300
+ #
301
+ # \{#get_function get-function($name, $css: false)}
302
+ # : Looks up a function with the given name in the current lexical scope
303
+ # and returns a reference to it.
296
304
  #
297
305
  # ## Miscellaneous Functions
298
306
  #
@@ -354,9 +362,6 @@ module Sass::Script
354
362
  # representation) on those objects without first setting {Tree::Node#options=
355
363
  # the #options attribute}.
356
364
  #
357
- # @comment
358
- # rubocop:enable LineLength
359
- # rubocop:disable ModuleLength
360
365
  module Functions
361
366
  @signatures = {}
362
367
 
@@ -471,14 +476,14 @@ module Sass::Script
471
476
  # @param seed [Integer]
472
477
  # @return [Integer] The same seed.
473
478
  def self.random_seed=(seed)
474
- @random_number_generator = Sass::Util::CrossPlatformRandom.new(seed)
479
+ @random_number_generator = Random.new(seed)
475
480
  end
476
481
 
477
482
  # Get Sass's internal random number generator.
478
483
  #
479
484
  # @return [Random]
480
485
  def self.random_number_generator
481
- @random_number_generator ||= Sass::Util::CrossPlatformRandom.new
486
+ @random_number_generator ||= Random.new
482
487
  end
483
488
 
484
489
  # The context in which methods in {Script::Functions} are evaluated.
@@ -522,18 +527,27 @@ module Sass::Script
522
527
  # assert_type value, :String
523
528
  # assert_type value, :Number
524
529
  # @param value [Sass::Script::Value::Base] A SassScript value
525
- # @param type [Symbol] The name of the type the value is expected to be
530
+ # @param type [Symbol, Array<Symbol>] The name(s) of the type the value is expected to be
526
531
  # @param name [String, Symbol, nil] The name of the argument.
527
532
  # @raise [ArgumentError] if value is not of the correct type.
528
533
  def assert_type(value, type, name = nil)
529
- klass = Sass::Script::Value.const_get(type)
530
- if value.is_a?(klass)
531
- value.check_deprecated_interp if type == :String
534
+ valid_types = Array(type)
535
+ found_type = valid_types.find do |t|
536
+ value.is_a?(Sass::Script::Value.const_get(t)) ||
537
+ t == :Map && value.is_a?(Sass::Script::Value::List) && value.value.empty?
538
+ end
539
+
540
+ if found_type
541
+ value.check_deprecated_interp if found_type == :String
532
542
  return
533
543
  end
534
544
 
535
- return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.value.empty?
536
- err = "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
545
+ err = if valid_types.size == 1
546
+ "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
547
+ else
548
+ type_names = valid_types.map {|t| TYPE_NAMES[t] || t.to_s.downcase}
549
+ "#{value.inspect} is not any of #{type_names.join(', ')}"
550
+ end
537
551
  err = "$#{name.to_s.tr('_', '-')}: " + err if name
538
552
  raise ArgumentError.new(err)
539
553
  end
@@ -634,23 +648,27 @@ module Sass::Script
634
648
  # inclusive
635
649
  # @return [Sass::Script::Value::Color]
636
650
  # @raise [ArgumentError] if any parameter is the wrong type or out of bounds
637
- def rgb(red, green, blue)
638
- if calc?(red) || calc?(green) || calc?(blue)
651
+ def rgb(red, green = nil, blue = nil)
652
+ if green.nil?
653
+ return unquoted_string("rgb(#{red})") if var?(red)
654
+ raise ArgumentError.new("wrong number of arguments (1 for 3)")
655
+ elsif blue.nil?
656
+ return unquoted_string("rgb(#{red}, #{green})") if var?(red) || var?(green)
657
+ raise ArgumentError.new("wrong number of arguments (2 for 3)")
658
+ end
659
+
660
+ if special_number?(red) || special_number?(green) || special_number?(blue)
639
661
  return unquoted_string("rgb(#{red}, #{green}, #{blue})")
640
662
  end
641
663
  assert_type red, :Number, :red
642
664
  assert_type green, :Number, :green
643
665
  assert_type blue, :Number, :blue
644
666
 
645
- color_attrs = [red, green, blue].map do |c|
646
- if c.is_unit?("%")
647
- c.value * 255 / 100.0
648
- elsif c.unitless?
649
- c.value
650
- else
651
- raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
652
- end
653
- end
667
+ color_attrs = [
668
+ percentage_or_unitless(red, 255, "red"),
669
+ percentage_or_unitless(green, 255, "green"),
670
+ percentage_or_unitless(blue, 255, "blue")
671
+ ]
654
672
 
655
673
  # Don't store the string representation for function-created colors, both
656
674
  # because it's not very useful and because some functions aren't supported
@@ -658,6 +676,8 @@ module Sass::Script
658
676
  Sass::Script::Value::Color.new(color_attrs)
659
677
  end
660
678
  declare :rgb, [:red, :green, :blue]
679
+ declare :rgb, [:red, :green]
680
+ declare :rgb, [:red]
661
681
 
662
682
  # Creates a {Sass::Script::Value::Color Color} from red, green, blue, and
663
683
  # alpha values.
@@ -692,20 +712,39 @@ module Sass::Script
692
712
  # is the wrong type
693
713
  def rgba(*args)
694
714
  case args.size
715
+ when 1
716
+ return unquoted_string("rgba(#{args.first})") if var?(args.first)
717
+ raise ArgumentError.new("wrong number of arguments (1 for 4)")
695
718
  when 2
696
719
  color, alpha = args
697
720
 
721
+ if var?(color)
722
+ return unquoted_string("rgba(#{color}, #{alpha})")
723
+ elsif var?(alpha)
724
+ if color.is_a?(Sass::Script::Value::Color)
725
+ return unquoted_string("rgba(#{color.red}, #{color.green}, #{color.blue}, #{alpha})")
726
+ else
727
+ return unquoted_string("rgba(#{color}, #{alpha})")
728
+ end
729
+ end
730
+
698
731
  assert_type color, :Color, :color
699
- if calc?(alpha)
732
+ if special_number?(alpha)
700
733
  unquoted_string("rgba(#{color.red}, #{color.green}, #{color.blue}, #{alpha})")
701
734
  else
702
735
  assert_type alpha, :Number, :alpha
703
- check_alpha_unit alpha, 'rgba'
704
- color.with(:alpha => alpha.value)
736
+ color.with(:alpha => percentage_or_unitless(alpha, 1, "alpha"))
737
+ end
738
+ when 3
739
+ if var?(args[0]) || var?(args[1]) || var?(args[2])
740
+ unquoted_string("rgba(#{args.join(', ')})")
741
+ else
742
+ raise ArgumentError.new("wrong number of arguments (3 for 4)")
705
743
  end
706
744
  when 4
707
745
  red, green, blue, alpha = args
708
- if calc?(red) || calc?(green) || calc?(blue) || calc?(alpha)
746
+ if special_number?(red) || special_number?(green) ||
747
+ special_number?(blue) || special_number?(alpha)
709
748
  unquoted_string("rgba(#{red}, #{green}, #{blue}, #{alpha})")
710
749
  else
711
750
  rgba(rgb(red, green, blue), alpha)
@@ -715,7 +754,9 @@ module Sass::Script
715
754
  end
716
755
  end
717
756
  declare :rgba, [:red, :green, :blue, :alpha]
757
+ declare :rgba, [:red, :green, :blue]
718
758
  declare :rgba, [:color, :alpha]
759
+ declare :rgba, [:red]
719
760
 
720
761
  # Creates a {Sass::Script::Value::Color Color} from hue, saturation, and
721
762
  # lightness values. Uses the algorithm from the [CSS3 spec][].
@@ -733,14 +774,24 @@ module Sass::Script
733
774
  # @return [Sass::Script::Value::Color]
734
775
  # @raise [ArgumentError] if `$saturation` or `$lightness` are out of bounds
735
776
  # or any parameter is the wrong type
736
- def hsl(hue, saturation, lightness)
737
- if calc?(hue) || calc?(saturation) || calc?(lightness)
777
+ def hsl(hue, saturation = nil, lightness = nil)
778
+ if saturation.nil?
779
+ return unquoted_string("hsl(#{hue})") if var?(hue)
780
+ raise ArgumentError.new("wrong number of arguments (1 for 3)")
781
+ elsif lightness.nil?
782
+ return unquoted_string("hsl(#{hue}, #{saturation})") if var?(hue) || var?(saturation)
783
+ raise ArgumentError.new("wrong number of arguments (2 for 3)")
784
+ end
785
+
786
+ if special_number?(hue) || special_number?(saturation) || special_number?(lightness)
738
787
  unquoted_string("hsl(#{hue}, #{saturation}, #{lightness})")
739
788
  else
740
789
  hsla(hue, saturation, lightness, number(1))
741
790
  end
742
791
  end
743
792
  declare :hsl, [:hue, :saturation, :lightness]
793
+ declare :hsl, [:hue, :saturation]
794
+ declare :hsl, [:hue]
744
795
 
745
796
  # Creates a {Sass::Script::Value::Color Color} from hue,
746
797
  # saturation, lightness, and alpha values. Uses the algorithm from
@@ -761,15 +812,29 @@ module Sass::Script
761
812
  # @return [Sass::Script::Value::Color]
762
813
  # @raise [ArgumentError] if `$saturation`, `$lightness`, or `$alpha` are out
763
814
  # of bounds or any parameter is the wrong type
764
- def hsla(hue, saturation, lightness, alpha)
765
- if calc?(hue) || calc?(saturation) || calc?(lightness) || calc?(alpha)
815
+ def hsla(hue, saturation = nil, lightness = nil, alpha = nil)
816
+ if saturation.nil?
817
+ return unquoted_string("hsla(#{hue})") if var?(hue)
818
+ raise ArgumentError.new("wrong number of arguments (1 for 4)")
819
+ elsif lightness.nil?
820
+ return unquoted_string("hsla(#{hue}, #{saturation})") if var?(hue) || var?(saturation)
821
+ raise ArgumentError.new("wrong number of arguments (2 for 4)")
822
+ elsif alpha.nil?
823
+ if var?(hue) || var?(saturation) || var?(lightness)
824
+ return unquoted_string("hsla(#{hue}, #{saturation}, #{lightness})")
825
+ else
826
+ raise ArgumentError.new("wrong number of arguments (2 for 4)")
827
+ end
828
+ end
829
+
830
+ if special_number?(hue) || special_number?(saturation) ||
831
+ special_number?(lightness) || special_number?(alpha)
766
832
  return unquoted_string("hsla(#{hue}, #{saturation}, #{lightness}, #{alpha})")
767
833
  end
768
834
  assert_type hue, :Number, :hue
769
835
  assert_type saturation, :Number, :saturation
770
836
  assert_type lightness, :Number, :lightness
771
837
  assert_type alpha, :Number, :alpha
772
- check_alpha_unit alpha, 'hsla'
773
838
 
774
839
  h = hue.value
775
840
  s = saturation.value
@@ -779,9 +844,13 @@ module Sass::Script
779
844
  # because it's not very useful and because some functions aren't supported
780
845
  # on older browsers.
781
846
  Sass::Script::Value::Color.new(
782
- :hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
847
+ :hue => h, :saturation => s, :lightness => l,
848
+ :alpha => percentage_or_unitless(alpha, 1, "alpha"))
783
849
  end
784
850
  declare :hsla, [:hue, :saturation, :lightness, :alpha]
851
+ declare :hsla, [:hue, :saturation, :lightness]
852
+ declare :hsla, [:hue, :saturation]
853
+ declare :hsla, [:hue]
785
854
 
786
855
  # Gets the red component of a color. Calculated from HSL where necessary via
787
856
  # [this algorithm][hsl-to-rgb].
@@ -1384,20 +1453,28 @@ module Sass::Script
1384
1453
  #
1385
1454
  # @overload invert($color)
1386
1455
  # @param $color [Sass::Script::Value::Color]
1456
+ # @overload invert($color, $weight: 100%)
1457
+ # @param $color [Sass::Script::Value::Color]
1458
+ # @param $weight [Sass::Script::Value::Number] The relative weight of the
1459
+ # color color's inverse
1387
1460
  # @return [Sass::Script::Value::Color]
1388
- # @raise [ArgumentError] if `$color` isn't a color
1389
- def invert(color)
1461
+ # @raise [ArgumentError] if `$color` isn't a color or `$weight`
1462
+ # isn't a percentage between 0% and 100%
1463
+ def invert(color, weight = number(100))
1390
1464
  if color.is_a?(Sass::Script::Value::Number)
1391
1465
  return identifier("invert(#{color})")
1392
1466
  end
1393
1467
 
1394
1468
  assert_type color, :Color, :color
1395
- color.with(
1469
+ inv = color.with(
1396
1470
  :red => (255 - color.red),
1397
1471
  :green => (255 - color.green),
1398
1472
  :blue => (255 - color.blue))
1473
+
1474
+ mix(inv, color, weight)
1399
1475
  end
1400
1476
  declare :invert, [:color]
1477
+ declare :invert, [:color, :weight]
1401
1478
 
1402
1479
  # Removes quotes from a string. If the string is already unquoted, this will
1403
1480
  # return it unmodified.
@@ -1413,13 +1490,11 @@ module Sass::Script
1413
1490
  def unquote(string)
1414
1491
  unless string.is_a?(Sass::Script::Value::String)
1415
1492
  # Don't warn multiple times for the same source line.
1416
- # rubocop:disable GlobalVars
1417
1493
  $_sass_warned_for_unquote ||= Set.new
1418
1494
  frame = environment.stack.frames.last
1419
1495
  key = [frame.filename, frame.line] if frame
1420
1496
  return string if frame && $_sass_warned_for_unquote.include?(key)
1421
1497
  $_sass_warned_for_unquote << key if frame
1422
- # rubocop:enable GlobalVars
1423
1498
 
1424
1499
  Sass::Util.sass_warn(<<MESSAGE.strip)
1425
1500
  DEPRECATION WARNING: Passing #{string.to_sass}, a non-string value, to unquote()
@@ -1612,6 +1687,10 @@ MESSAGE
1612
1687
  # type-of(#fff) => color
1613
1688
  # type-of(blue) => color
1614
1689
  # type-of(null) => null
1690
+ # type-of(a b c) => list
1691
+ # type-of((a: 1, b: 2)) => map
1692
+ # type-of(get-function("foo")) => function
1693
+ #
1615
1694
  # @overload type_of($value)
1616
1695
  # @param $value [Sass::Script::Value::Base] The value to inspect
1617
1696
  # @return [Sass::Script::Value::String] The unquoted string name of the
@@ -1639,6 +1718,12 @@ MESSAGE
1639
1718
  #
1640
1719
  # * `at-error` indicates that the Sass `@error` directive is supported.
1641
1720
  #
1721
+ # * `custom-property` indicates that the [Custom Properties Level 1][] spec
1722
+ # is supported. This means that custom properties are parsed statically,
1723
+ # with only interpolation treated as SassScript.
1724
+ #
1725
+ # [Custom Properties Level 1]: https://www.w3.org/TR/css-variables-1/
1726
+ #
1642
1727
  # @example
1643
1728
  # feature-exists(some-feature-that-exists) => true
1644
1729
  # feature-exists(what-is-this-i-dont-know) => false
@@ -1653,6 +1738,55 @@ MESSAGE
1653
1738
  end
1654
1739
  declare :feature_exists, [:feature]
1655
1740
 
1741
+ # Returns a reference to a function for later invocation with the `call()` function.
1742
+ #
1743
+ # If `$css` is `false`, the function reference may refer to a function
1744
+ # defined in your stylesheet or built-in to the host environment. If it's
1745
+ # `true` it will refer to a plain-CSS function.
1746
+ #
1747
+ # @example
1748
+ # get-function("rgb")
1749
+ #
1750
+ # @function myfunc { @return "something"; }
1751
+ # get-function("myfunc")
1752
+ #
1753
+ # @overload get_function($name, $css: false)
1754
+ # @param name [Sass::Script::Value::String] The name of the function being referenced.
1755
+ # @param css [Sass::Script::Value::Bool] Whether to get a plain CSS function.
1756
+ #
1757
+ # @return [Sass::Script::Value::Function] A function reference.
1758
+ def get_function(name, kwargs = {})
1759
+ assert_type name, :String, :name
1760
+
1761
+ css = if kwargs.has_key?("css")
1762
+ v = kwargs.delete("css")
1763
+ assert_type v, :Bool, :css
1764
+ v.value
1765
+ else
1766
+ false
1767
+ end
1768
+
1769
+ if kwargs.any?
1770
+ raise ArgumentError.new("Illegal keyword argument '#{kwargs.keys.first}'")
1771
+ end
1772
+
1773
+ if css
1774
+ return Sass::Script::Value::Function.new(
1775
+ Sass::Callable.new(name.value, nil, nil, nil, nil, nil, "function", :css))
1776
+ end
1777
+
1778
+ callable = environment.caller.function(name.value) ||
1779
+ (Sass::Script::Functions.callable?(name.value.tr("-", "_")) &&
1780
+ Sass::Callable.new(name.value, nil, nil, nil, nil, nil, "function", :builtin))
1781
+
1782
+ if callable
1783
+ Sass::Script::Value::Function.new(callable)
1784
+ else
1785
+ raise Sass::SyntaxError.new("Function not found: #{name}")
1786
+ end
1787
+ end
1788
+ declare :get_function, [:name], :var_kwargs => true
1789
+
1656
1790
  # Returns the unit(s) associated with a number. Complex units are sorted in
1657
1791
  # alphabetical order by numerator and denominator.
1658
1792
  #
@@ -1855,7 +1989,7 @@ MESSAGE
1855
1989
  index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1856
1990
  new_list = list.to_a.dup
1857
1991
  new_list[index] = value
1858
- Sass::Script::Value::List.new(new_list, list.separator)
1992
+ list.with_contents(new_list)
1859
1993
  end
1860
1994
  declare :set_nth, [:list, :n, :value]
1861
1995
 
@@ -1896,6 +2030,9 @@ MESSAGE
1896
2030
  # list. If both lists have fewer than two items, spaces are used for the
1897
2031
  # resulting list.
1898
2032
  #
2033
+ # Unless `$bracketed` is passed, the resulting list is bracketed if the
2034
+ # first parameter is.
2035
+ #
1899
2036
  # Like all list functions, `join()` returns a new list rather than modifying
1900
2037
  # its arguments in place.
1901
2038
  #
@@ -1905,27 +2042,70 @@ MESSAGE
1905
2042
  # join(10px, 20px) => 10px 20px
1906
2043
  # join(10px, 20px, comma) => 10px, 20px
1907
2044
  # join((blue, red), (#abc, #def), space) => blue red #abc #def
1908
- # @overload join($list1, $list2, $separator: auto)
2045
+ # join([10px], 20px) => [10px 20px]
2046
+ # @overload join($list1, $list2, $separator: auto, $bracketed: auto)
1909
2047
  # @param $list1 [Sass::Script::Value::Base]
1910
2048
  # @param $list2 [Sass::Script::Value::Base]
1911
2049
  # @param $separator [Sass::Script::Value::String] The list separator to use.
1912
2050
  # If this is `comma` or `space`, that separator will be used. If this is
1913
2051
  # `auto` (the default), the separator is determined as explained above.
2052
+ # @param $bracketed [Sass::Script::Value::Base] Whether the resulting list
2053
+ # will be bracketed. If this is `auto` (the default), the separator is
2054
+ # determined as explained above.
1914
2055
  # @return [Sass::Script::Value::List]
1915
- def join(list1, list2, separator = identifier("auto"))
2056
+ def join(list1, list2,
2057
+ separator = identifier("auto"), bracketed = identifier("auto"),
2058
+ kwargs = nil, *rest)
2059
+ if separator.is_a?(Hash)
2060
+ kwargs = separator
2061
+ separator = identifier("auto")
2062
+ elsif bracketed.is_a?(Hash)
2063
+ kwargs = bracketed
2064
+ bracketed = identifier("auto")
2065
+ elsif rest.last.is_a?(Hash)
2066
+ rest.unshift kwargs
2067
+ kwargs = rest.pop
2068
+ end
2069
+
2070
+ unless rest.empty?
2071
+ # Add 4 to rest.length because we don't want to count the kwargs hash,
2072
+ # which is always passed.
2073
+ raise ArgumentError.new("wrong number of arguments (#{rest.length + 4} for 2..4)")
2074
+ end
2075
+
2076
+ if kwargs
2077
+ separator = kwargs.delete("separator") || separator
2078
+ bracketed = kwargs.delete("bracketed") || bracketed
2079
+
2080
+ unless kwargs.empty?
2081
+ name, val = kwargs.to_a.first
2082
+ raise ArgumentError.new("Unknown argument $#{name} (#{val})")
2083
+ end
2084
+ end
2085
+
1916
2086
  assert_type separator, :String, :separator
1917
2087
  unless %w(auto space comma).include?(separator.value)
1918
2088
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1919
2089
  end
1920
- sep = if separator.value == 'auto'
1921
- list1.separator || list2.separator || :space
1922
- else
1923
- separator.value.to_sym
1924
- end
1925
- list(list1.to_a + list2.to_a, sep)
2090
+
2091
+ list(list1.to_a + list2.to_a,
2092
+ separator:
2093
+ if separator.value == 'auto'
2094
+ list1.separator || list2.separator || :space
2095
+ else
2096
+ separator.value.to_sym
2097
+ end,
2098
+ bracketed:
2099
+ if bracketed.is_a?(Sass::Script::Value::String) && bracketed.value == 'auto'
2100
+ list1.bracketed
2101
+ else
2102
+ bracketed.to_bool
2103
+ end)
1926
2104
  end
1927
- declare :join, [:list1, :list2]
1928
- declare :join, [:list1, :list2, :separator]
2105
+ # We don't actually take variable arguments or keyword arguments, but this
2106
+ # is the best way to take either `$separator` or `$bracketed` as keywords
2107
+ # without complaining about the other missing.
2108
+ declare :join, [:list1, :list2], :var_args => true, :var_kwargs => true
1929
2109
 
1930
2110
  # Appends a single value onto the end of a list.
1931
2111
  #
@@ -1953,12 +2133,13 @@ MESSAGE
1953
2133
  unless %w(auto space comma).include?(separator.value)
1954
2134
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1955
2135
  end
1956
- sep = if separator.value == 'auto'
1957
- list.separator || :space
1958
- else
1959
- separator.value.to_sym
1960
- end
1961
- list(list.to_a + [val], sep)
2136
+ list.with_contents(list.to_a + [val],
2137
+ separator:
2138
+ if separator.value == 'auto'
2139
+ list.separator || :space
2140
+ else
2141
+ separator.value.to_sym
2142
+ end)
1962
2143
  end
1963
2144
  declare :append, [:list, :val]
1964
2145
  declare :append, [:list, :val, :separator]
@@ -2028,7 +2209,20 @@ MESSAGE
2028
2209
  def list_separator(list)
2029
2210
  identifier((list.separator || :space).to_s)
2030
2211
  end
2031
- declare :separator, [:list]
2212
+ declare :list_separator, [:list]
2213
+
2214
+ # Returns whether a list uses square brackets.
2215
+ #
2216
+ # @example
2217
+ # is-bracketed(1px 2px 3px) => false
2218
+ # is-bracketed([1px, 2px, 3px]) => true
2219
+ # @overload is_bracketed($list)
2220
+ # @param $list [Sass::Script::Value::Base]
2221
+ # @return [Sass::Script::Value::Bool]
2222
+ def is_bracketed(list)
2223
+ bool(list.bracketed)
2224
+ end
2225
+ declare :is_bracketed, [:list]
2032
2226
 
2033
2227
  # Returns the value in a map associated with the given key. If the map
2034
2228
  # doesn't have such a key, returns `null`.
@@ -2214,10 +2408,24 @@ MESSAGE
2214
2408
  # $fn: nth;
2215
2409
  # call($fn, (a b c), 2) => b
2216
2410
  #
2217
- # @overload call($name, $args...)
2218
- # @param $name [String] The name of the function to call.
2411
+ # @overload call($function, $args...)
2412
+ # @param $function [Sass::Script::Value::Function] The function to call.
2219
2413
  def call(name, *args)
2220
- assert_type name, :String, :name
2414
+ unless name.is_a?(Sass::Script::Value::String) ||
2415
+ name.is_a?(Sass::Script::Value::Function)
2416
+ assert_type name, :Function, :function
2417
+ end
2418
+ if name.is_a?(Sass::Script::Value::String)
2419
+ name = if function_exists(name).to_bool
2420
+ get_function(name)
2421
+ else
2422
+ get_function(name, "css" => bool(true))
2423
+ end
2424
+ Sass::Util.sass_warn(<<WARNING)
2425
+ DEPRECATION WARNING: Passing a string to call() is deprecated and will be illegal
2426
+ in Sass 4.0. Use call(#{name.to_sass}) instead.
2427
+ WARNING
2428
+ end
2221
2429
  kwargs = args.last.is_a?(Hash) ? args.pop : {}
2222
2430
  funcall = Sass::Script::Tree::Funcall.new(
2223
2431
  name.value,
@@ -2225,6 +2433,8 @@ MESSAGE
2225
2433
  Sass::Util.map_vals(kwargs) {|v| Sass::Script::Tree::Literal.new(v)},
2226
2434
  nil,
2227
2435
  nil)
2436
+ funcall.line = environment.stack.frames.last.line
2437
+ funcall.filename = environment.stack.frames.last.filename
2228
2438
  funcall.options = options
2229
2439
  perform(funcall)
2230
2440
  end
@@ -2313,12 +2523,12 @@ MESSAGE
2313
2523
  #
2314
2524
  # @overload function_exists($name)
2315
2525
  # @param name [Sass::Script::Value::String] The name of the function to
2316
- # check.
2526
+ # check or a function reference.
2317
2527
  # @return [Sass::Script::Value::Bool] Whether the function is defined.
2318
2528
  def function_exists(name)
2319
2529
  assert_type name, :String, :name
2320
2530
  exists = Sass::Script::Functions.callable?(name.value.tr("-", "_"))
2321
- exists ||= environment.function(name.value)
2531
+ exists ||= environment.caller.function(name.value)
2322
2532
  bool(exists)
2323
2533
  end
2324
2534
  declare :function_exists, [:name]
@@ -2341,6 +2551,31 @@ MESSAGE
2341
2551
  end
2342
2552
  declare :mixin_exists, [:name]
2343
2553
 
2554
+ # Check whether a mixin was passed a content block.
2555
+ #
2556
+ # Unless `content-exists()` is called directly from a mixin, an error will be raised.
2557
+ #
2558
+ # @example
2559
+ # @mixin needs-content {
2560
+ # @if not content-exists() {
2561
+ # @error "You must pass a content block!"
2562
+ # }
2563
+ # @content;
2564
+ # }
2565
+ #
2566
+ # @overload content_exists()
2567
+ # @return [Sass::Script::Value::Bool] Whether a content block was passed to the mixin.
2568
+ def content_exists
2569
+ # frames.last is the stack frame for this function,
2570
+ # so we use frames[-2] to get the frame before that.
2571
+ mixin_frame = environment.stack.frames[-2]
2572
+ unless mixin_frame && mixin_frame.type == :mixin
2573
+ raise Sass::SyntaxError.new("Cannot call content-exists() except within a mixin.")
2574
+ end
2575
+ bool(!environment.caller.content.nil?)
2576
+ end
2577
+ declare :content_exists, []
2578
+
2344
2579
  # Return a string containing the value as its Sass representation.
2345
2580
  #
2346
2581
  # @overload inspect($value)
@@ -2663,10 +2898,7 @@ MESSAGE
2663
2898
  yield(value.value), value.numerator_units, value.denominator_units)
2664
2899
  end
2665
2900
 
2666
- # @comment
2667
- # rubocop:disable ParameterLists
2668
2901
  def _adjust(color, amount, attr, range, op, units = "")
2669
- # rubocop:enable ParameterLists
2670
2902
  assert_type color, :Color, :color
2671
2903
  assert_type amount, :Number, :amount
2672
2904
  Sass::Util.check_range('Amount', range, amount, units)
@@ -2674,19 +2906,14 @@ MESSAGE
2674
2906
  color.with(attr => color.send(attr).send(op, amount.value))
2675
2907
  end
2676
2908
 
2677
- def check_alpha_unit(alpha, function)
2678
- return if alpha.unitless?
2679
-
2680
- if alpha.is_unit?("%")
2681
- Sass::Util.sass_warn(<<WARNING)
2682
- DEPRECATION WARNING: Passing a percentage as the alpha value to #{function}() will be
2683
- interpreted differently in future versions of Sass. For now, use #{alpha.value} instead.
2684
- WARNING
2909
+ def percentage_or_unitless(number, max, name)
2910
+ if number.unitless?
2911
+ number.value
2912
+ elsif number.is_unit?("%")
2913
+ max * number.value / 100.0;
2685
2914
  else
2686
- Sass::Util.sass_warn(<<WARNING)
2687
- DEPRECATION WARNING: Passing a number with units as the alpha value to #{function}() is
2688
- deprecated and will be an error in future versions of Sass. Use #{alpha.value} instead.
2689
- WARNING
2915
+ raise ArgumentError.new(
2916
+ "$#{name}: Expected #{number} to have no units or \"%\"");
2690
2917
  end
2691
2918
  end
2692
2919
  end