sass 3.4.25 → 3.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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