sass 3.4.25 → 3.5.0.pre.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/README.md +1 -1
  4. data/Rakefile +13 -157
  5. data/VERSION +1 -1
  6. data/VERSION_DATE +1 -1
  7. data/VERSION_NAME +1 -1
  8. data/lib/sass.rb +3 -10
  9. data/lib/sass/cache_stores/filesystem.rb +1 -1
  10. data/lib/sass/css.rb +2 -3
  11. data/lib/sass/engine.rb +46 -32
  12. data/lib/sass/environment.rb +27 -6
  13. data/lib/sass/error.rb +5 -5
  14. data/lib/sass/exec/base.rb +3 -12
  15. data/lib/sass/features.rb +1 -0
  16. data/lib/sass/importers/filesystem.rb +2 -2
  17. data/lib/sass/plugin.rb +1 -1
  18. data/lib/sass/plugin/compiler.rb +21 -51
  19. data/lib/sass/plugin/configuration.rb +1 -1
  20. data/lib/sass/plugin/rack.rb +3 -3
  21. data/lib/sass/plugin/staleness_checker.rb +3 -3
  22. data/lib/sass/railtie.rb +1 -1
  23. data/lib/sass/script.rb +3 -3
  24. data/lib/sass/script/functions.rb +238 -47
  25. data/lib/sass/script/lexer.rb +8 -6
  26. data/lib/sass/script/parser.rb +76 -75
  27. data/lib/sass/script/tree/funcall.rb +35 -30
  28. data/lib/sass/script/tree/list_literal.rb +23 -8
  29. data/lib/sass/script/tree/map_literal.rb +2 -2
  30. data/lib/sass/script/tree/node.rb +2 -10
  31. data/lib/sass/script/tree/operation.rb +16 -50
  32. data/lib/sass/script/value.rb +2 -0
  33. data/lib/sass/script/value/arg_list.rb +1 -1
  34. data/lib/sass/script/value/base.rb +20 -3
  35. data/lib/sass/script/value/callable.rb +25 -0
  36. data/lib/sass/script/value/color.rb +10 -10
  37. data/lib/sass/script/value/function.rb +19 -0
  38. data/lib/sass/script/value/helpers.rb +16 -7
  39. data/lib/sass/script/value/list.rb +33 -12
  40. data/lib/sass/script/value/map.rb +2 -2
  41. data/lib/sass/script/value/number.rb +3 -3
  42. data/lib/sass/script/value/string.rb +12 -5
  43. data/lib/sass/scss/parser.rb +101 -45
  44. data/lib/sass/scss/rx.rb +5 -11
  45. data/lib/sass/scss/static_parser.rb +0 -7
  46. data/lib/sass/selector.rb +4 -0
  47. data/lib/sass/selector/abstract_sequence.rb +5 -5
  48. data/lib/sass/selector/comma_sequence.rb +3 -15
  49. data/lib/sass/selector/pseudo.rb +4 -0
  50. data/lib/sass/selector/sequence.rb +30 -3
  51. data/lib/sass/selector/simple.rb +13 -7
  52. data/lib/sass/selector/simple_sequence.rb +1 -1
  53. data/lib/sass/shared.rb +3 -5
  54. data/lib/sass/source/map.rb +4 -4
  55. data/lib/sass/source/position.rb +4 -4
  56. data/lib/sass/stack.rb +21 -1
  57. data/lib/sass/tree/charset_node.rb +1 -1
  58. data/lib/sass/tree/comment_node.rb +1 -1
  59. data/lib/sass/tree/node.rb +3 -3
  60. data/lib/sass/tree/prop_node.rb +46 -54
  61. data/lib/sass/tree/rule_node.rb +7 -15
  62. data/lib/sass/tree/visitors/check_nesting.rb +1 -1
  63. data/lib/sass/tree/visitors/convert.rb +2 -3
  64. data/lib/sass/tree/visitors/cssize.rb +1 -10
  65. data/lib/sass/tree/visitors/deep_copy.rb +2 -2
  66. data/lib/sass/tree/visitors/perform.rb +23 -12
  67. data/lib/sass/tree/visitors/set_options.rb +1 -1
  68. data/lib/sass/tree/visitors/to_css.rb +46 -12
  69. data/lib/sass/util.rb +16 -321
  70. data/lib/sass/util/multibyte_string_scanner.rb +127 -131
  71. data/lib/sass/util/normalized_map.rb +1 -8
  72. data/lib/sass/version.rb +2 -2
  73. data/test/sass-spec.yml +1 -1
  74. data/test/sass/compiler_test.rb +4 -14
  75. data/test/sass/conversion_test.rb +113 -162
  76. data/test/sass/css2sass_test.rb +17 -19
  77. data/test/sass/css_variable_test.rb +176 -70
  78. data/test/sass/encoding_test.rb +2 -32
  79. data/test/sass/engine_test.rb +114 -65
  80. data/test/sass/extend_test.rb +37 -51
  81. data/test/sass/functions_test.rb +57 -15
  82. data/test/sass/importer_test.rb +2 -2
  83. data/test/sass/more_templates/more1.sass +10 -10
  84. data/test/sass/more_templates/more_import.sass +2 -2
  85. data/test/sass/plugin_test.rb +9 -12
  86. data/test/sass/script_conversion_test.rb +9 -0
  87. data/test/sass/script_test.rb +38 -48
  88. data/test/sass/scss/css_test.rb +5 -19
  89. data/test/sass/scss/scss_test.rb +58 -39
  90. data/test/sass/source_map_test.rb +26 -28
  91. data/test/sass/templates/_partial.sass +1 -1
  92. data/test/sass/templates/basic.sass +10 -10
  93. data/test/sass/templates/bork1.sass +1 -1
  94. data/test/sass/templates/bork5.sass +1 -1
  95. data/test/sass/templates/compact.sass +10 -10
  96. data/test/sass/templates/complex.sass +187 -187
  97. data/test/sass/templates/compressed.sass +10 -10
  98. data/test/sass/templates/expanded.sass +10 -10
  99. data/test/sass/templates/import.sass +2 -2
  100. data/test/sass/templates/importee.sass +3 -3
  101. data/test/sass/templates/mixins.sass +22 -22
  102. data/test/sass/templates/multiline.sass +4 -4
  103. data/test/sass/templates/nested.sass +13 -13
  104. data/test/sass/templates/parent_ref.sass +12 -12
  105. data/test/sass/templates/script.sass +70 -70
  106. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  107. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  108. data/test/sass/templates/subdir/subdir.sass +3 -3
  109. data/test/sass/templates/units.sass +10 -10
  110. data/test/sass/util/multibyte_string_scanner_test.rb +139 -149
  111. data/test/sass/util_test.rb +0 -36
  112. data/test/test_helper.rb +39 -0
  113. metadata +30 -57
  114. data/extra/sass-spec-ref.sh +0 -32
  115. data/lib/sass/deprecation.rb +0 -55
  116. data/lib/sass/script/css_variable_warning.rb +0 -52
  117. data/lib/sass/util/cross_platform_random.rb +0 -19
  118. data/lib/sass/util/ordered_hash.rb +0 -192
  119. data/test/sass/results/import_charset_1_8.css +0 -5
  120. data/test/sass/templates/import_charset_1_8.sass +0 -6
  121. data/vendor/listen/CHANGELOG.md +0 -1
  122. data/vendor/listen/CONTRIBUTING.md +0 -38
  123. data/vendor/listen/Gemfile +0 -20
  124. data/vendor/listen/Guardfile +0 -8
  125. data/vendor/listen/LICENSE +0 -20
  126. data/vendor/listen/README.md +0 -349
  127. data/vendor/listen/Rakefile +0 -5
  128. data/vendor/listen/Vagrantfile +0 -96
  129. data/vendor/listen/lib/listen.rb +0 -54
  130. data/vendor/listen/lib/listen/adapter.rb +0 -327
  131. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -75
  132. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -48
  133. data/vendor/listen/lib/listen/adapters/linux.rb +0 -81
  134. data/vendor/listen/lib/listen/adapters/polling.rb +0 -58
  135. data/vendor/listen/lib/listen/adapters/windows.rb +0 -91
  136. data/vendor/listen/lib/listen/directory_record.rb +0 -406
  137. data/vendor/listen/lib/listen/listener.rb +0 -323
  138. data/vendor/listen/lib/listen/turnstile.rb +0 -32
  139. data/vendor/listen/lib/listen/version.rb +0 -3
  140. data/vendor/listen/listen.gemspec +0 -28
  141. data/vendor/listen/spec/listen/adapter_spec.rb +0 -149
  142. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  143. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  144. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  145. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  146. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  147. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1250
  148. data/vendor/listen/spec/listen/listener_spec.rb +0 -258
  149. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  150. data/vendor/listen/spec/listen_spec.rb +0 -67
  151. data/vendor/listen/spec/spec_helper.rb +0 -25
  152. data/vendor/listen/spec/support/adapter_helper.rb +0 -666
  153. data/vendor/listen/spec/support/directory_record_helper.rb +0 -57
  154. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  155. data/vendor/listen/spec/support/listeners_helper.rb +0 -179
  156. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -81,7 +81,7 @@ module Sass
81
81
  inherited_hash_reader :function
82
82
 
83
83
  # @param options [{Symbol => Object}] The options hash. See
84
- # {file:SASS_REFERENCE.md#Options the Sass options documentation}.
84
+ # {file:SASS_REFERENCE.md#options the Sass options documentation}.
85
85
  # @param parent [Environment] See \{#parent}
86
86
  def initialize(parent = nil, options = nil)
87
87
  @parent = parent
@@ -175,6 +175,10 @@ module Sass
175
175
 
176
176
  # A read-only wrapper for a lexical environment for SassScript.
177
177
  class ReadOnlyEnvironment < BaseEnvironment
178
+ def initialize(parent = nil, options = nil)
179
+ super
180
+ @content_cached = nil
181
+ end
178
182
  # The read-only environment of the caller of this environment's mixin or function.
179
183
  #
180
184
  # @see BaseEnvironment#caller
@@ -185,14 +189,31 @@ module Sass
185
189
  @caller ||= env.is_a?(ReadOnlyEnvironment) ? env : ReadOnlyEnvironment.new(env, env.options)
186
190
  end
187
191
 
188
- # The read-only content passed to this environment.
192
+ # The content passed to this environment. If the content's environment isn't already
193
+ # read-only, it's made read-only.
189
194
  #
190
195
  # @see BaseEnvironment#content
191
- # @return {ReadOnlyEnvironment}
196
+ #
197
+ # @return {[Array<Sass::Tree::Node>, ReadOnlyEnvironment]?} The content nodes and
198
+ # the lexical environment of the content block.
199
+ # Returns `nil` when there is no content in this environment.
192
200
  def content
193
- return @content if @content
194
- env = super
195
- @content ||= env.is_a?(ReadOnlyEnvironment) ? env : ReadOnlyEnvironment.new(env, env.options)
201
+ # Return the cached content from a previous invocation if any
202
+ return @content if @content_cached
203
+ # get the content with a read-write environment from the superclass
204
+ read_write_content = super
205
+ if read_write_content
206
+ tree, env = read_write_content
207
+ # make the content's environment read-only
208
+ if env && !env.is_a?(ReadOnlyEnvironment)
209
+ env = ReadOnlyEnvironment.new(env, env.options)
210
+ end
211
+ @content_cached = true
212
+ @content = [tree, env]
213
+ else
214
+ @content_cached = true
215
+ @content = nil
216
+ end
196
217
  end
197
218
  end
198
219
 
@@ -69,14 +69,14 @@ module Sass
69
69
  # The name of the mixin in which the error occurred.
70
70
  # This could be `nil` if the error occurred outside a mixin.
71
71
  #
72
- # @return [String]
72
+ # @return [Fixnum]
73
73
  def sass_mixin
74
74
  sass_backtrace.first[:mixin]
75
75
  end
76
76
 
77
77
  # The line of the Sass template on which the error occurred.
78
78
  #
79
- # @return [Integer]
79
+ # @return [Fixnum]
80
80
  def sass_line
81
81
  sass_backtrace.first[:line]
82
82
  end
@@ -142,7 +142,7 @@ module Sass
142
142
  msg = lines[0] + lines[1..-1].
143
143
  map {|l| "\n" + (" " * "Error: ".size) + l}.join
144
144
  "Error: #{msg}" +
145
- Sass::Util.enum_with_index(sass_backtrace).map do |entry, i|
145
+ sass_backtrace.each_with_index.map do |entry, i|
146
146
  "\n #{i == 0 ? 'on' : 'from'} line #{entry[:line]}" +
147
147
  " of #{entry[:filename] || default_filename}" +
148
148
  (entry[:mixin] ? ", in `#{entry[:mixin]}'" : "")
@@ -153,7 +153,7 @@ module Sass
153
153
  # Returns an error report for an exception in CSS format.
154
154
  #
155
155
  # @param e [Exception]
156
- # @param line_offset [Integer] The number of the first line of the Sass template.
156
+ # @param line_offset [Fixnum] The number of the first line of the Sass template.
157
157
  # @return [String] The error report
158
158
  # @raise [Exception] `e`, if the
159
159
  # {file:SASS_REFERENCE.md#full_exception-option `:full_exception`} option
@@ -186,7 +186,7 @@ END
186
186
  section = e.sass_template.rstrip.split("\n")[min...line_num + 5]
187
187
  return e.sass_backtrace_str if section.nil? || section.empty?
188
188
 
189
- e.sass_backtrace_str + "\n\n" + Sass::Util.enum_with_index(section).
189
+ e.sass_backtrace_str + "\n\n" + section.each_with_index.
190
190
  map {|line, i| "#{line_offset + min + i}: #{line}"}.join("\n")
191
191
  end
192
192
  end
@@ -23,7 +23,7 @@ module Sass::Exec
23
23
  # at_exit is a bit of a hack, but it allows us to rethrow when --trace
24
24
  # is active and get both the built-in exception formatting and the
25
25
  # correct exit code.
26
- at_exit {exit Sass::Util.windows? ? 13 : 65} if e.is_a?(Sass::SyntaxError)
26
+ at_exit {exit 65} if e.is_a?(Sass::SyntaxError)
27
27
 
28
28
  raise e if @options[:trace] || e.is_a?(SystemExit)
29
29
 
@@ -90,18 +90,9 @@ module Sass::Exec
90
90
  #
91
91
  # @param opts [OptionParser]
92
92
  def encoding_option(opts)
93
- encoding_desc = if Sass::Util.ruby1_8?
94
- 'Does not work in Ruby 1.8.'
95
- else
96
- 'Specify the default encoding for input files.'
97
- end
93
+ encoding_desc = 'Specify the default encoding for input files.'
98
94
  opts.on('-E', '--default-encoding ENCODING', encoding_desc) do |encoding|
99
- if Sass::Util.ruby1_8?
100
- $stderr.puts "Specifying the encoding is not supported in ruby 1.8."
101
- exit 1
102
- else
103
- Encoding.default_external = encoding
104
- end
95
+ Encoding.default_external = encoding
105
96
  end
106
97
  end
107
98
 
@@ -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.
@@ -12,7 +12,7 @@ module Sass
12
12
  # when it's used as a plugin for various frameworks.
13
13
  # All Rack-enabled frameworks are supported out of the box.
14
14
  # The plugin is
15
- # {file:SASS_REFERENCE.md#Rack_Rails_Merb_Plugin automatically activated for Rails and Merb}.
15
+ # {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
16
16
  # Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
17
17
  #
18
18
  # This module has a large set of callbacks available
@@ -31,7 +31,7 @@ module Sass::Plugin
31
31
  # Creates a new compiler.
32
32
  #
33
33
  # @param opts [{Symbol => Object}]
34
- # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
34
+ # See {file:SASS_REFERENCE.md#options the Sass options documentation}.
35
35
  def initialize(opts = {})
36
36
  @watched_files = Set.new
37
37
  options.merge!(opts)
@@ -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 'listen'
381
+ Listen.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
@@ -27,7 +27,7 @@ module Sass
27
27
  end
28
28
 
29
29
  # An options hash.
30
- # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
30
+ # See {file:SASS_REFERENCE.md#options the Sass options documentation}.
31
31
  #
32
32
  # @return [{Symbol => Object}]
33
33
  def options
@@ -9,19 +9,19 @@ module Sass
9
9
  #
10
10
  # ## Customize
11
11
  #
12
- # Sass::Plugin.options.merge!(
12
+ # Sass::Plugin.options.merge(
13
13
  # :cache_location => './tmp/sass-cache',
14
14
  # :never_update => environment != :production,
15
15
  # :full_exception => environment != :production)
16
16
  #
17
- # {file:SASS_REFERENCE.md#Options See the Reference for more options}.
17
+ # {file:SASS_REFERENCE.md#options See the Reference for more options}.
18
18
  #
19
19
  # ## Use
20
20
  #
21
21
  # Put your Sass files in `public/stylesheets/sass`.
22
22
  # Your CSS will be generated in `public/stylesheets`,
23
23
  # and regenerated every request if necessary.
24
- # The locations and frequency {file:SASS_REFERENCE.md#Options can be customized}.
24
+ # The locations and frequency {file:SASS_REFERENCE.md#options can be customized}.
25
25
  # That's all there is to it!
26
26
  class Rack
27
27
  # The delay, in seconds, between update checks.
@@ -39,7 +39,7 @@ module Sass
39
39
  # for checking the staleness of several stylesheets at once.
40
40
  #
41
41
  # @param options [{Symbol => Object}]
42
- # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
42
+ # See {file:SASS_REFERENCE.md#options the Sass options documentation}.
43
43
  def initialize(options)
44
44
  # URIs that are being actively checked for staleness. Protects against
45
45
  # import loops.
@@ -72,7 +72,7 @@ module Sass
72
72
  # Returns whether a Sass or SCSS stylesheet has been modified since a given time.
73
73
  #
74
74
  # @param template_file [String] The location of the Sass or SCSS template.
75
- # @param mtime [Time] The modification time to check against.
75
+ # @param mtime [Fixnum] The modification time to check against.
76
76
  # @param importer [Sass::Importers::Base] The importer used to locate the stylesheet.
77
77
  # Defaults to the filesystem importer.
78
78
  # @return [Boolean] Whether the stylesheet has been modified.
@@ -103,7 +103,7 @@ module Sass
103
103
  # so it's better to use when checking multiple stylesheets at once.
104
104
  #
105
105
  # @param template_file [String] The location of the Sass or SCSS template.
106
- # @param mtime [Time] The modification time to check against.
106
+ # @param mtime [Fixnum] The modification time to check against.
107
107
  # @param importer [Sass::Importers::Base] The importer used to locate the stylesheet.
108
108
  # Defaults to the filesystem importer.
109
109
  # @return [Boolean] Whether the stylesheet has been modified.
@@ -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
@@ -16,12 +16,12 @@ module Sass
16
16
  # Parses a string of SassScript
17
17
  #
18
18
  # @param value [String] The SassScript
19
- # @param line [Integer] The number of the line on which the SassScript appeared.
19
+ # @param line [Fixnum] The number of the line on which the SassScript appeared.
20
20
  # Used for error reporting
21
- # @param offset [Integer] The number of characters in on `line` that the SassScript started.
21
+ # @param offset [Fixnum] The number of characters in on `line` that the SassScript started.
22
22
  # Used for error reporting
23
23
  # @param options [{Symbol => Object}] An options hash;
24
- # see {file:SASS_REFERENCE.md#Options the Sass options documentation}
24
+ # see {file:SASS_REFERENCE.md#options the Sass options documentation}
25
25
  # @return [Script::Tree::Node] The root node of the parse tree
26
26
  def self.parse(value, line, offset, options = {})
27
27
  Parser.parse(value, line, offset, options)
@@ -77,7 +77,7 @@ module Sass::Script
77
77
  # \{#complement complement($color)}
78
78
  # : Returns the complement of a color.
79
79
  #
80
- # \{#invert invert($color)}
80
+ # \{#invert invert($color, \[$weight\])}
81
81
  # : Returns the inverse of a color.
82
82
  #
83
83
  # ## Opacity Functions
@@ -176,7 +176,7 @@ module Sass::Script
176
176
  # \{#set-nth set-nth($list, $n, $value)}
177
177
  # : Replaces the nth item in a list.
178
178
  #
179
- # \{#join join($list1, $list2, \[$separator\])}
179
+ # \{#join join($list1, $list2, \[$separator, $bracketed\])}
180
180
  # : Joins together two lists into one.
181
181
  #
182
182
  # \{#append append($list1, $val, \[$separator\])}
@@ -191,6 +191,9 @@ module Sass::Script
191
191
  # \{#list_separator list-separator($list)}
192
192
  # : Returns the separator of a list.
193
193
  #
194
+ # \{#is_bracketed is-bracketed($list)}
195
+ # : Returns whether a list has square brackets.
196
+ #
194
197
  # ## Map Functions {#map-functions}
195
198
  #
196
199
  # Maps in Sass are immutable; all map functions return a new map rather than
@@ -276,6 +279,9 @@ module Sass::Script
276
279
  # \{#mixin_exists mixin-exists($name)}
277
280
  # : Returns whether a mixin with the given name exists.
278
281
  #
282
+ # \{#content_exists content-exists()}
283
+ # : Returns whether the current mixin was passed a content block.
284
+ #
279
285
  # \{#inspect inspect($value)}
280
286
  # : Returns the string representation of a value as it would be represented in Sass.
281
287
  #
@@ -291,8 +297,12 @@ module Sass::Script
291
297
  # \{#comparable comparable($number1, $number2)}
292
298
  # : Returns whether two numbers can be added, subtracted, or compared.
293
299
  #
294
- # \{#call call($name, $args...)}
295
- # : Dynamically calls a Sass function.
300
+ # \{#call call($function, $args...)}
301
+ # : Dynamically calls a Sass function reference returned by `get-function`.
302
+ #
303
+ # \{#get_function get-function($name)}
304
+ # : Looks up a function with the given name in the current lexical scope
305
+ # and returns a reference to it.
296
306
  #
297
307
  # ## Miscellaneous Functions
298
308
  #
@@ -431,8 +441,8 @@ module Sass::Script
431
441
  # If no signatures match, the first signature is returned for error messaging.
432
442
  #
433
443
  # @param method_name [Symbol] The name of the Ruby function to be called.
434
- # @param arg_arity [Integer] The number of unnamed arguments the function was passed.
435
- # @param kwarg_arity [Integer] The number of keyword arguments the function was passed.
444
+ # @param arg_arity [Fixnum] The number of unnamed arguments the function was passed.
445
+ # @param kwarg_arity [Fixnum] The number of keyword arguments the function was passed.
436
446
  #
437
447
  # @return [{Symbol => Object}, nil]
438
448
  # The signature options for the matching signature,
@@ -471,14 +481,14 @@ module Sass::Script
471
481
  # @param seed [Integer]
472
482
  # @return [Integer] The same seed.
473
483
  def self.random_seed=(seed)
474
- @random_number_generator = Sass::Util::CrossPlatformRandom.new(seed)
484
+ @random_number_generator = Random.new(seed)
475
485
  end
476
486
 
477
487
  # Get Sass's internal random number generator.
478
488
  #
479
489
  # @return [Random]
480
490
  def self.random_number_generator
481
- @random_number_generator ||= Sass::Util::CrossPlatformRandom.new
491
+ @random_number_generator ||= Random.new
482
492
  end
483
493
 
484
494
  # The context in which methods in {Script::Functions} are evaluated.
@@ -522,18 +532,27 @@ module Sass::Script
522
532
  # assert_type value, :String
523
533
  # assert_type value, :Number
524
534
  # @param value [Sass::Script::Value::Base] A SassScript value
525
- # @param type [Symbol] The name of the type the value is expected to be
535
+ # @param type [Symbol, Array<Symbol>] The name(s) of the type the value is expected to be
526
536
  # @param name [String, Symbol, nil] The name of the argument.
527
537
  # @raise [ArgumentError] if value is not of the correct type.
528
538
  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
539
+ valid_types = Array(type)
540
+ found_type = valid_types.find do |t|
541
+ value.is_a?(Sass::Script::Value.const_get(t)) ||
542
+ t == :Map && value.is_a?(Sass::Script::Value::List) && value.value.empty?
543
+ end
544
+
545
+ if found_type
546
+ value.check_deprecated_interp if found_type == :String
532
547
  return
533
548
  end
534
549
 
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}"
550
+ err = if valid_types.size == 1
551
+ "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
552
+ else
553
+ type_names = valid_types.map {|t| TYPE_NAMES[t] || t.to_s.downcase}
554
+ "#{value.inspect} is not any of #{type_names.join(', ')}"
555
+ end
537
556
  err = "$#{name.to_s.tr('_', '-')}: " + err if name
538
557
  raise ArgumentError.new(err)
539
558
  end
@@ -1305,7 +1324,7 @@ module Sass::Script
1305
1324
  # @param $color1 [Sass::Script::Value::Color]
1306
1325
  # @param $color2 [Sass::Script::Value::Color]
1307
1326
  # @param $weight [Sass::Script::Value::Number] The relative weight of each
1308
- # color. Closer to `100%` gives more weight to `$color1`, closer to `0%`
1327
+ # color. Closer to `0%` gives more weight to `$color1`, closer to `100%`
1309
1328
  # gives more weight to `$color2`
1310
1329
  # @return [Sass::Script::Value::Color]
1311
1330
  # @raise [ArgumentError] if `$weight` is out of bounds or any parameter is
@@ -1384,20 +1403,28 @@ module Sass::Script
1384
1403
  #
1385
1404
  # @overload invert($color)
1386
1405
  # @param $color [Sass::Script::Value::Color]
1406
+ # @overload invert($color, $weight: 100%)
1407
+ # @param $color [Sass::Script::Value::Color]
1408
+ # @param $weight [Sass::Script::Value::Number] The relative weight of the
1409
+ # color color's inverse
1387
1410
  # @return [Sass::Script::Value::Color]
1388
- # @raise [ArgumentError] if `$color` isn't a color
1389
- def invert(color)
1411
+ # @raise [ArgumentError] if `$color` isn't a color or `$weight`
1412
+ # isn't a percentage between 0% and 100%
1413
+ def invert(color, weight = number(100))
1390
1414
  if color.is_a?(Sass::Script::Value::Number)
1391
1415
  return identifier("invert(#{color})")
1392
1416
  end
1393
1417
 
1394
1418
  assert_type color, :Color, :color
1395
- color.with(
1419
+ inv = color.with(
1396
1420
  :red => (255 - color.red),
1397
1421
  :green => (255 - color.green),
1398
1422
  :blue => (255 - color.blue))
1423
+
1424
+ mix(inv, color, weight)
1399
1425
  end
1400
1426
  declare :invert, [:color]
1427
+ declare :invert, [:color, :weight]
1401
1428
 
1402
1429
  # Removes quotes from a string. If the string is already unquoted, this will
1403
1430
  # return it unmodified.
@@ -1546,7 +1573,7 @@ MESSAGE
1546
1573
  # @param $start-at [Sass::Script::Value::Number] The index of the first
1547
1574
  # character of the substring. If this is negative, it counts from the end
1548
1575
  # of `$string`
1549
- # @param $end-at [Sass::Script::Value::Number] The index of the last
1576
+ # @param $end-before [Sass::Script::Value::Number] The index of the last
1550
1577
  # character of the substring. If this is negative, it counts from the end
1551
1578
  # of `$string`. Defaults to -1
1552
1579
  # @return [Sass::Script::Value::String] The substring. This will be quoted
@@ -1565,7 +1592,7 @@ MESSAGE
1565
1592
  s = string.value.length + s if s < 0
1566
1593
  s = 0 if s < 0
1567
1594
  e = string.value.length + e if e < 0
1568
- return Sass::Script::Value::String.new("", string.type) if e < 0
1595
+ e = 0 if s < 0
1569
1596
  extracted = string.value.slice(s..e)
1570
1597
  Sass::Script::Value::String.new(extracted || "", string.type)
1571
1598
  end
@@ -1583,7 +1610,7 @@ MESSAGE
1583
1610
  # @raise [ArgumentError] if `$string` isn't a string
1584
1611
  def to_upper_case(string)
1585
1612
  assert_type string, :String, :string
1586
- Sass::Script::Value::String.new(Sass::Util.upcase(string.value), string.type)
1613
+ Sass::Script::Value::String.new(string.value.upcase, string.type)
1587
1614
  end
1588
1615
  declare :to_upper_case, [:string]
1589
1616
 
@@ -1598,7 +1625,7 @@ MESSAGE
1598
1625
  # @raise [ArgumentError] if `$string` isn't a string
1599
1626
  def to_lower_case(string)
1600
1627
  assert_type string, :String, :string
1601
- Sass::Script::Value::String.new(Sass::Util.downcase(string.value), string.type)
1628
+ Sass::Script::Value::String.new(string.value.downcase, string.type)
1602
1629
  end
1603
1630
  declare :to_lower_case, [:string]
1604
1631
 
@@ -1612,6 +1639,10 @@ MESSAGE
1612
1639
  # type-of(#fff) => color
1613
1640
  # type-of(blue) => color
1614
1641
  # type-of(null) => null
1642
+ # type-of(a b c) => list
1643
+ # type-of((a: 1, b: 2)) => map
1644
+ # type-of(get-function("foo")) => function
1645
+ #
1615
1646
  # @overload type_of($value)
1616
1647
  # @param $value [Sass::Script::Value::Base] The value to inspect
1617
1648
  # @return [Sass::Script::Value::String] The unquoted string name of the
@@ -1639,6 +1670,12 @@ MESSAGE
1639
1670
  #
1640
1671
  # * `at-error` indicates that the Sass `@error` directive is supported.
1641
1672
  #
1673
+ # * `custom-property` indicates that the [Custom Properties Level 1][] spec
1674
+ # is supported. This means that custom properties are parsed statically,
1675
+ # with only interpolation treated as SassScript.
1676
+ #
1677
+ # [Custom Properties Level 1]: https://www.w3.org/TR/css-variables-1/
1678
+ #
1642
1679
  # @example
1643
1680
  # feature-exists(some-feature-that-exists) => true
1644
1681
  # feature-exists(what-is-this-i-dont-know) => false
@@ -1653,6 +1690,56 @@ MESSAGE
1653
1690
  end
1654
1691
  declare :feature_exists, [:feature]
1655
1692
 
1693
+ # Returns a reference to a function for later invocation with the `call` function.
1694
+ #
1695
+ # The function reference created may refer to a function defined in
1696
+ # your stylesheet, built-in to the host environment, or a CSS native
1697
+ # function. As such, this function never returns an error, but the
1698
+ # you can use `function-exists()` to check if the function reference
1699
+ # created points at a Sass function.
1700
+ #
1701
+ # @example
1702
+ # get-function("rgb")
1703
+ #
1704
+ # @function myfunc { @return "something"; }
1705
+ # get-function("myfunc")
1706
+ #
1707
+ # @overload get_function($name)
1708
+ # @param name [Sass::Script::Value::String] The name of the function being referenced.
1709
+ #
1710
+ # @return [Sass::Script::Value::Function] A function reference.
1711
+ def get_function(name, kwargs = {})
1712
+ assert_type name, :String, :name
1713
+
1714
+ css = if kwargs.has_key?("css")
1715
+ v = kwargs.delete("css")
1716
+ assert_type v, :Bool, :css
1717
+ v.value
1718
+ else
1719
+ false
1720
+ end
1721
+
1722
+ if kwargs.any?
1723
+ raise ArgumentError.new("Illegal keyword argument '#{kwargs.keys.first}'")
1724
+ end
1725
+
1726
+ if css
1727
+ return Sass::Script::Value::Function.new(
1728
+ Sass::Callable.new(name.value, nil, nil, nil, nil, nil, "function", :css))
1729
+ end
1730
+
1731
+ callable = environment.caller.function(name.value) ||
1732
+ (Sass::Script::Functions.callable?(name.value.tr("-", "_")) &&
1733
+ Sass::Callable.new(name.value, nil, nil, nil, nil, nil, "function", :builtin))
1734
+
1735
+ if callable
1736
+ Sass::Script::Value::Function.new(callable)
1737
+ else
1738
+ raise Sass::SyntaxError.new("Function not found: #{name}")
1739
+ end
1740
+ end
1741
+ declare :get_function, [:name], :var_kwargs => true
1742
+
1656
1743
  # Returns the unit(s) associated with a number. Complex units are sorted in
1657
1744
  # alphabetical order by numerator and denominator.
1658
1745
  #
@@ -1855,7 +1942,7 @@ MESSAGE
1855
1942
  index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1856
1943
  new_list = list.to_a.dup
1857
1944
  new_list[index] = value
1858
- Sass::Script::Value::List.new(new_list, list.separator)
1945
+ list.with_contents(new_list)
1859
1946
  end
1860
1947
  declare :set_nth, [:list, :n, :value]
1861
1948
 
@@ -1896,6 +1983,9 @@ MESSAGE
1896
1983
  # list. If both lists have fewer than two items, spaces are used for the
1897
1984
  # resulting list.
1898
1985
  #
1986
+ # Unless `$bracketed` is passed, the resulting list is bracketed if the
1987
+ # first parameter is.
1988
+ #
1899
1989
  # Like all list functions, `join()` returns a new list rather than modifying
1900
1990
  # its arguments in place.
1901
1991
  #
@@ -1905,27 +1995,73 @@ MESSAGE
1905
1995
  # join(10px, 20px) => 10px 20px
1906
1996
  # join(10px, 20px, comma) => 10px, 20px
1907
1997
  # join((blue, red), (#abc, #def), space) => blue red #abc #def
1908
- # @overload join($list1, $list2, $separator: auto)
1998
+ # join([10px], 20px) => [10px 20px]
1999
+ # @overload join($list1, $list2, $separator: auto, $bracketed: auto)
1909
2000
  # @param $list1 [Sass::Script::Value::Base]
1910
2001
  # @param $list2 [Sass::Script::Value::Base]
1911
2002
  # @param $separator [Sass::Script::Value::String] The list separator to use.
1912
2003
  # If this is `comma` or `space`, that separator will be used. If this is
1913
2004
  # `auto` (the default), the separator is determined as explained above.
2005
+ # @param $bracketed [Sass::Script::Value::Base] Whether the resulting list
2006
+ # will be bracketed. If this is `auto` (the default), the separator is
2007
+ # determined as explained above.
1914
2008
  # @return [Sass::Script::Value::List]
1915
- def join(list1, list2, separator = identifier("auto"))
2009
+ # @comment
2010
+ # rubocop:disable ParameterLists
2011
+ def join(list1, list2,
2012
+ separator = identifier("auto"), bracketed = identifier("auto"),
2013
+ kwargs = nil, *rest)
2014
+ # rubocop:enable ParameterLists
2015
+ if separator.is_a?(Hash)
2016
+ kwargs = separator
2017
+ separator = identifier("auto")
2018
+ elsif bracketed.is_a?(Hash)
2019
+ kwargs = bracketed
2020
+ bracketed = identifier("auto")
2021
+ elsif rest.last.is_a?(Hash)
2022
+ rest.unshift kwargs
2023
+ kwargs = rest.pop
2024
+ end
2025
+
2026
+ unless rest.empty?
2027
+ # Add 4 to rest.length because we don't want to count the kwargs hash,
2028
+ # which is always passed.
2029
+ raise ArgumentError.new("wrong number of arguments (#{rest.length + 4} for 2..4)")
2030
+ end
2031
+
2032
+ if kwargs
2033
+ separator = kwargs.delete("separator") || separator
2034
+ bracketed = kwargs.delete("bracketed") || bracketed
2035
+
2036
+ unless kwargs.empty?
2037
+ name, val = kwargs.to_a.first
2038
+ raise ArgumentError.new("Unknown argument $#{name} (#{val})")
2039
+ end
2040
+ end
2041
+
1916
2042
  assert_type separator, :String, :separator
1917
2043
  unless %w(auto space comma).include?(separator.value)
1918
2044
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1919
2045
  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)
2046
+
2047
+ list(list1.to_a + list2.to_a,
2048
+ separator:
2049
+ if separator.value == 'auto'
2050
+ list1.separator || list2.separator || :space
2051
+ else
2052
+ separator.value.to_sym
2053
+ end,
2054
+ bracketed:
2055
+ if bracketed.is_a?(Sass::Script::Value::String) && bracketed.value == 'auto'
2056
+ list1.bracketed
2057
+ else
2058
+ bracketed.to_bool
2059
+ end)
1926
2060
  end
1927
- declare :join, [:list1, :list2]
1928
- declare :join, [:list1, :list2, :separator]
2061
+ # We don't actually take variable arguments or keyword arguments, but this
2062
+ # is the best way to take either `$separator` or `$bracketed` as keywords
2063
+ # without complaining about the other missing.
2064
+ declare :join, [:list1, :list2], :var_args => true, :var_kwargs => true
1929
2065
 
1930
2066
  # Appends a single value onto the end of a list.
1931
2067
  #
@@ -1953,12 +2089,13 @@ MESSAGE
1953
2089
  unless %w(auto space comma).include?(separator.value)
1954
2090
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1955
2091
  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)
2092
+ list.with_contents(list.to_a + [val],
2093
+ separator:
2094
+ if separator.value == 'auto'
2095
+ list.separator || :space
2096
+ else
2097
+ separator.value.to_sym
2098
+ end)
1962
2099
  end
1963
2100
  declare :append, [:list, :val]
1964
2101
  declare :append, [:list, :val, :separator]
@@ -2028,7 +2165,20 @@ MESSAGE
2028
2165
  def list_separator(list)
2029
2166
  identifier((list.separator || :space).to_s)
2030
2167
  end
2031
- declare :separator, [:list]
2168
+ declare :list_separator, [:list]
2169
+
2170
+ # Returns whether a list uses square brackets.
2171
+ #
2172
+ # @example
2173
+ # is-bracketed(1px 2px 3px) => false
2174
+ # is-bracketed([1px, 2px, 3px]) => true
2175
+ # @overload is_bracketed($list)
2176
+ # @param $list [Sass::Script::Value::Base]
2177
+ # @return [Sass::Script::Value::Bool]
2178
+ def is_bracketed(list)
2179
+ bool(list.bracketed)
2180
+ end
2181
+ declare :is_bracketed, [:list]
2032
2182
 
2033
2183
  # Returns the value in a map associated with the given key. If the map
2034
2184
  # doesn't have such a key, returns `null`.
@@ -2214,10 +2364,24 @@ MESSAGE
2214
2364
  # $fn: nth;
2215
2365
  # call($fn, (a b c), 2) => b
2216
2366
  #
2217
- # @overload call($name, $args...)
2218
- # @param $name [String] The name of the function to call.
2367
+ # @overload call($function, $args...)
2368
+ # @param $function [Sass::Script::Value::Function] The function to call.
2219
2369
  def call(name, *args)
2220
- assert_type name, :String, :name
2370
+ unless name.is_a?(Sass::Script::Value::String) ||
2371
+ name.is_a?(Sass::Script::Value::Function)
2372
+ assert_type name, :Function, :function
2373
+ end
2374
+ if name.is_a?(Sass::Script::Value::String)
2375
+ name = if function_exists(name).to_bool
2376
+ get_function(name)
2377
+ else
2378
+ get_function(name, "css" => bool(true))
2379
+ end
2380
+ Sass::Util.sass_warn(<<WARNING)
2381
+ DEPRECATION WARNING: Passing a string to call() is deprecated and will be illegal
2382
+ in Sass 4.0. Use call(#{name.to_sass}) instead.
2383
+ WARNING
2384
+ end
2221
2385
  kwargs = args.last.is_a?(Hash) ? args.pop : {}
2222
2386
  funcall = Sass::Script::Tree::Funcall.new(
2223
2387
  name.value,
@@ -2225,6 +2389,8 @@ MESSAGE
2225
2389
  Sass::Util.map_vals(kwargs) {|v| Sass::Script::Tree::Literal.new(v)},
2226
2390
  nil,
2227
2391
  nil)
2392
+ funcall.line = environment.stack.frames.last.line
2393
+ funcall.filename = environment.stack.frames.last.filename
2228
2394
  funcall.options = options
2229
2395
  perform(funcall)
2230
2396
  end
@@ -2313,12 +2479,12 @@ MESSAGE
2313
2479
  #
2314
2480
  # @overload function_exists($name)
2315
2481
  # @param name [Sass::Script::Value::String] The name of the function to
2316
- # check.
2482
+ # check or a function reference.
2317
2483
  # @return [Sass::Script::Value::Bool] Whether the function is defined.
2318
2484
  def function_exists(name)
2319
2485
  assert_type name, :String, :name
2320
2486
  exists = Sass::Script::Functions.callable?(name.value.tr("-", "_"))
2321
- exists ||= environment.function(name.value)
2487
+ exists ||= environment.caller.function(name.value)
2322
2488
  bool(exists)
2323
2489
  end
2324
2490
  declare :function_exists, [:name]
@@ -2341,6 +2507,31 @@ MESSAGE
2341
2507
  end
2342
2508
  declare :mixin_exists, [:name]
2343
2509
 
2510
+ # Check whether a mixin was passed a content block.
2511
+ #
2512
+ # Unless `content-exists()` is called directly from a mixin, an error will be raised.
2513
+ #
2514
+ # @example
2515
+ # @mixin needs-content {
2516
+ # @if not content-exists() {
2517
+ # @error "You must pass a content block!"
2518
+ # }
2519
+ # @content;
2520
+ # }
2521
+ #
2522
+ # @overload content_exists()
2523
+ # @return [Sass::Script::Value::Bool] Whether a content block was passed to the mixin.
2524
+ def content_exists
2525
+ # frames.last is the stack frame for this function,
2526
+ # so we use frames[-2] to get the frame before that.
2527
+ mixin_frame = environment.stack.frames[-2]
2528
+ unless mixin_frame && mixin_frame.type == :mixin
2529
+ raise Sass::SyntaxError.new("Cannot call content-exists() except within a mixin.")
2530
+ end
2531
+ bool(!environment.caller.content.nil?)
2532
+ end
2533
+ declare :content_exists, []
2534
+
2344
2535
  # Return a string containing the value as its Sass representation.
2345
2536
  #
2346
2537
  # @overload inspect($value)
@@ -2510,7 +2701,7 @@ MESSAGE
2510
2701
 
2511
2702
  extends = Sass::Util::SubsetMap.new
2512
2703
  begin
2513
- extender.populate_extends(extends, extendee, nil, [], true)
2704
+ extender.populate_extends(extends, extendee)
2514
2705
  selector.do_extend(extends).to_sass_script
2515
2706
  rescue Sass::SyntaxError => e
2516
2707
  raise ArgumentError.new(e.to_s)
@@ -2553,7 +2744,7 @@ MESSAGE
2553
2744
 
2554
2745
  extends = Sass::Util::SubsetMap.new
2555
2746
  begin
2556
- replacement.populate_extends(extends, original, nil, [], true)
2747
+ replacement.populate_extends(extends, original)
2557
2748
  selector.do_extend(extends, [], true).to_sass_script
2558
2749
  rescue Sass::SyntaxError => e
2559
2750
  raise ArgumentError.new(e.to_s)