sass 3.4.25 → 3.5.0.pre.rc.1

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