sass 3.3.0 → 3.4.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
data/lib/sass/util.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'erb'
2
3
  require 'set'
3
4
  require 'enumerator'
@@ -12,12 +13,14 @@ require 'sass/util/subset_map'
12
13
 
13
14
  module Sass
14
15
  # A module containing various useful functions.
16
+ # @comment
17
+ # rubocop:disable ModuleLength
15
18
  module Util
16
19
  extend self
17
20
 
18
21
  # An array of ints representing the Ruby version number.
19
22
  # @api public
20
- RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
23
+ RUBY_VERSION_COMPONENTS = RUBY_VERSION.split(".").map {|s| s.to_i}
21
24
 
22
25
  # The Ruby engine we're running under. Defaults to `"ruby"`
23
26
  # if the top-level constant is undefined.
@@ -137,6 +140,18 @@ module Sass
137
140
  [[value, range.first].max, range.last].min
138
141
  end
139
142
 
143
+ # Like [Fixnum.round], but leaves rooms for slight floating-point
144
+ # differences.
145
+ #
146
+ # @param value [Numeric]
147
+ # @return [Numeric]
148
+ def round(value)
149
+ # If the number is within epsilon of X.5, round up (or down for negative
150
+ # numbers).
151
+ return value.round if (value % 1) - 0.5 <= -1 * Script::Value::Number.epsilon
152
+ value > 0 ? value.ceil : value.floor
153
+ end
154
+
140
155
  # Concatenates all strings that are adjacent in an array,
141
156
  # while leaving other elements as they are.
142
157
  #
@@ -162,6 +177,43 @@ module Sass
162
177
  end
163
178
  end
164
179
 
180
+ # Non-destructively replaces all occurrences of a subsequence in an array
181
+ # with another subsequence.
182
+ #
183
+ # @example
184
+ # replace_subseq([1, 2, 3, 4, 5], [2, 3], [:a, :b])
185
+ # #=> [1, :a, :b, 4, 5]
186
+ #
187
+ # @param arr [Array] The array whose subsequences will be replaced.
188
+ # @param subseq [Array] The subsequence to find and replace.
189
+ # @param replacement [Array] The sequence that `subseq` will be replaced with.
190
+ # @return [Array] `arr` with `subseq` replaced with `replacement`.
191
+ def replace_subseq(arr, subseq, replacement)
192
+ new = []
193
+ matched = []
194
+ i = 0
195
+ arr.each do |elem|
196
+ if elem != subseq[i]
197
+ new.push(*matched)
198
+ matched = []
199
+ i = 0
200
+ new << elem
201
+ next
202
+ end
203
+
204
+ if i == subseq.length - 1
205
+ matched = []
206
+ i = 0
207
+ new.push(*replacement)
208
+ else
209
+ matched << elem
210
+ i += 1
211
+ end
212
+ end
213
+ new.push(*matched)
214
+ new
215
+ end
216
+
165
217
  # Intersperses a value in an enumerable, as would be done with `Array#join`
166
218
  # but without concatenating the array together afterwards.
167
219
  #
@@ -227,7 +279,7 @@ module Sass
227
279
  # # [2, 4, 5]]
228
280
  def paths(arrs)
229
281
  arrs.inject([[]]) do |paths, arr|
230
- flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
282
+ arr.map {|e| paths.map {|path| path + [e]}}.flatten(1)
231
283
  end
232
284
  end
233
285
 
@@ -260,7 +312,7 @@ module Sass
260
312
  # @return [Array]
261
313
  def hash_to_a(hash)
262
314
  return hash.to_a unless ruby1_8? || defined?(Test::Unit)
263
- hash.sort_by {|k, v| k}
315
+ hash.sort_by {|k, _v| k}
264
316
  end
265
317
 
266
318
  # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
@@ -287,6 +339,18 @@ module Sass
287
339
  arr
288
340
  end
289
341
 
342
+ # Like `String.upcase`, but only ever upcases ASCII letters.
343
+ def upcase(string)
344
+ return string.upcase unless ruby2_4?
345
+ string.upcase(:ascii)
346
+ end
347
+
348
+ # Like `String.downcase`, but only ever downcases ASCII letters.
349
+ def downcase(string)
350
+ return string.downcase unless ruby2_4?
351
+ string.downcase(:ascii)
352
+ end
353
+
290
354
  # Returns a sub-array of `minuend` containing only elements that are also in
291
355
  # `subtrahend`. Ensures that the return value has the same order as
292
356
  # `minuend`, even on Rubinius where that's not guaranteed by `Array#-`.
@@ -300,6 +364,18 @@ module Sass
300
364
  minuend.select {|e| set.include?(e)}
301
365
  end
302
366
 
367
+ # Returns the maximum of `val1` and `val2`. We use this over \{Array.max} to
368
+ # avoid unnecessary garbage collection.
369
+ def max(val1, val2)
370
+ val1 > val2 ? val1 : val2
371
+ end
372
+
373
+ # Returns the minimum of `val1` and `val2`. We use this over \{Array.min} to
374
+ # avoid unnecessary garbage collection.
375
+ def min(val1, val2)
376
+ val1 <= val2 ? val1 : val2
377
+ end
378
+
303
379
  # Returns a string description of the character that caused an
304
380
  # `Encoding::UndefinedConversionError`.
305
381
  #
@@ -354,13 +430,13 @@ module Sass
354
430
  # Returns information about the caller of the previous method.
355
431
  #
356
432
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
357
- # @return [[String, Fixnum, (String, nil)]]
433
+ # @return [[String, Integer, (String, nil)]]
358
434
  # An array containing the filename, line, and method name of the caller.
359
435
  # The method name may be nil
360
436
  def caller_info(entry = nil)
361
437
  # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
362
438
  entry ||= caller[1]
363
- info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
439
+ info = entry.scan(/^((?:[A-Za-z]:)?.*?):(-?.*?)(?::.*`(.+)')?$/).first
364
440
  info[1] = info[1].to_i
365
441
  # This is added by Rubinius to designate a block, but we don't care about it.
366
442
  info[2].sub!(/ \{\}\Z/, '') if info[2]
@@ -448,7 +524,7 @@ module Sass
448
524
  #
449
525
  # @param msg [String]
450
526
  def sass_warn(msg)
451
- msg = msg + "\n" unless ruby1?
527
+ msg += "\n" unless ruby1?
452
528
  Sass.logger.warn(msg)
453
529
  end
454
530
 
@@ -507,8 +583,17 @@ module Sass
507
583
  #
508
584
  # @return [Boolean]
509
585
  def listen_geq_2?
510
- require 'listen/version'
511
- version_geq(::Listen::VERSION, '2.0.0')
586
+ return @listen_geq_2 if defined?(@listen_geq_2)
587
+ @listen_geq_2 =
588
+ begin
589
+ # Make sure we're loading listen/version from the same place that
590
+ # we're loading listen itself.
591
+ load_listen!
592
+ require 'listen/version'
593
+ version_geq(::Listen::VERSION, '2.0.0')
594
+ rescue LoadError
595
+ false
596
+ end
512
597
  end
513
598
 
514
599
  # Returns an ActionView::Template* class.
@@ -563,7 +648,7 @@ module Sass
563
648
 
564
649
  # Returns an array of ints representing the JRuby version number.
565
650
  #
566
- # @return [Array<Fixnum>]
651
+ # @return [Array<Integer>]
567
652
  def jruby_version
568
653
  @jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
569
654
  end
@@ -572,7 +657,7 @@ module Sass
572
657
  #
573
658
  # @param path [String]
574
659
  def glob(path)
575
- path = path.gsub('\\', '/') if windows?
660
+ path = path.tr('\\', '/') if windows?
576
661
  if block_given?
577
662
  Dir.glob(path) {|f| yield(f)}
578
663
  else
@@ -583,13 +668,99 @@ module Sass
583
668
  # Like `Pathname.new`, but normalizes Windows paths to always use backslash
584
669
  # separators.
585
670
  #
586
- # `Pathname.relative_path_from` can break if the two pathnames aren't
671
+ # `Pathname#relative_path_from` can break if the two pathnames aren't
587
672
  # consistent in their slash style.
673
+ #
674
+ # @param path [String]
675
+ # @return [Pathname]
588
676
  def pathname(path)
589
677
  path = path.tr("/", "\\") if windows?
590
678
  Pathname.new(path)
591
679
  end
592
680
 
681
+ # Like `Pathname#cleanpath`, but normalizes Windows paths to always use
682
+ # backslash separators. Normally, `Pathname#cleanpath` actually does the
683
+ # reverse -- it will convert backslashes to forward slashes, which can break
684
+ # `Pathname#relative_path_from`.
685
+ #
686
+ # @param path [String, Pathname]
687
+ # @return [Pathname]
688
+ def cleanpath(path)
689
+ path = Pathname.new(path) unless path.is_a?(Pathname)
690
+ pathname(path.cleanpath.to_s)
691
+ end
692
+
693
+ # Returns `path` with all symlinks resolved.
694
+ #
695
+ # @param path [String, Pathname]
696
+ # @return [Pathname]
697
+ def realpath(path)
698
+ path = Pathname.new(path) unless path.is_a?(Pathname)
699
+
700
+ # Explicitly DON'T run #pathname here. We don't want to convert
701
+ # to Windows directory separators because we're comparing these
702
+ # against the paths returned by Listen, which use forward
703
+ # slashes everywhere.
704
+ begin
705
+ path.realpath
706
+ rescue SystemCallError
707
+ # If [path] doesn't actually exist, don't bail, just
708
+ # return the original.
709
+ path
710
+ end
711
+ end
712
+
713
+ # Returns `path` relative to `from`.
714
+ #
715
+ # This is like `Pathname#relative_path_from` except it accepts both strings
716
+ # and pathnames, it handles Windows path separators correctly, and it throws
717
+ # an error rather than crashing if the paths use different encodings
718
+ # (https://github.com/ruby/ruby/pull/713).
719
+ #
720
+ # @param path [String, Pathname]
721
+ # @param from [String, Pathname]
722
+ # @return [Pathname?]
723
+ def relative_path_from(path, from)
724
+ pathname(path.to_s).relative_path_from(pathname(from.to_s))
725
+ rescue NoMethodError => e
726
+ raise e unless e.name == :zero?
727
+
728
+ # Work around https://github.com/ruby/ruby/pull/713.
729
+ path = path.to_s
730
+ from = from.to_s
731
+ raise ArgumentError("Incompatible path encodings: #{path.inspect} is #{path.encoding}, " +
732
+ "#{from.inspect} is #{from.encoding}")
733
+ end
734
+
735
+ # Converts `path` to a "file:" URI. This handles Windows paths correctly.
736
+ #
737
+ # @param path [String, Pathname]
738
+ # @return [String]
739
+ def file_uri_from_path(path)
740
+ path = path.to_s if path.is_a?(Pathname)
741
+ path = path.tr('\\', '/') if windows?
742
+ path = Sass::Util.escape_uri(path)
743
+ return path.start_with?('/') ? "file://" + path : path unless windows?
744
+ return "file:///" + path.tr("\\", "/") if path =~ %r{^[a-zA-Z]:[/\\]}
745
+ return "file:" + path.tr("\\", "/") if path =~ %r{\\\\[^\\]+\\[^\\/]+}
746
+ path.tr("\\", "/")
747
+ end
748
+
749
+ # Retries a filesystem operation if it fails on Windows. Windows
750
+ # has weird and flaky locking rules that can cause operations to fail.
751
+ #
752
+ # @yield [] The filesystem operation.
753
+ def retry_on_windows
754
+ return yield unless windows?
755
+
756
+ begin
757
+ yield
758
+ rescue SystemCallError
759
+ sleep 0.1
760
+ yield
761
+ end
762
+ end
763
+
593
764
  # Prepare a value for a destructuring assignment (e.g. `a, b =
594
765
  # val`). This works around a performance bug when using
595
766
  # ActiveSupport, and only needs to be called when `val` is likely
@@ -610,7 +781,7 @@ module Sass
610
781
  # @return [Boolean]
611
782
  def ruby1?
612
783
  return @ruby1 if defined?(@ruby1)
613
- @ruby1 = Sass::Util::RUBY_VERSION[0] <= 1
784
+ @ruby1 = RUBY_VERSION_COMPONENTS[0] <= 1
614
785
  end
615
786
 
616
787
  # Whether or not this is running under Ruby 1.8 or lower.
@@ -624,16 +795,28 @@ module Sass
624
795
  # We have to fall back to 1.8 behavior.
625
796
  return @ruby1_8 if defined?(@ruby1_8)
626
797
  @ruby1_8 = ironruby? ||
627
- (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
798
+ (RUBY_VERSION_COMPONENTS[0] == 1 && RUBY_VERSION_COMPONENTS[1] < 9)
799
+ end
800
+
801
+ # Whether or not this is running under Ruby 1.9.2 exactly.
802
+ #
803
+ # @return [Boolean]
804
+ def ruby1_9_2?
805
+ return @ruby1_9_2 if defined?(@ruby1_9_2)
806
+ @ruby1_9_2 = RUBY_VERSION_COMPONENTS == [1, 9, 2]
628
807
  end
629
808
 
630
- # Whether or not this is running under Ruby 1.8.6 or lower.
631
- # Note that lower versions are not officially supported.
809
+ # Whether or not this is running under Ruby 2.4 or higher.
632
810
  #
633
811
  # @return [Boolean]
634
- def ruby1_8_6?
635
- return @ruby1_8_6 if defined?(@ruby1_8_6)
636
- @ruby1_8_6 = ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
812
+ def ruby2_4?
813
+ return @ruby2_4 if defined?(@ruby2_4)
814
+ @ruby2_4 =
815
+ if RUBY_VERSION_COMPONENTS[0] == 2
816
+ RUBY_VERSION_COMPONENTS[1] >= 4
817
+ else
818
+ RUBY_VERSION_COMPONENTS[0] > 2
819
+ end
637
820
  end
638
821
 
639
822
  # Wehter or not this is running under JRuby 1.6 or lower.
@@ -677,117 +860,80 @@ module Sass
677
860
  end
678
861
 
679
862
  return Hash[pairs_or_hash] unless ruby1_8?
680
- (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
863
+ (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*pairs_or_hash.flatten(1)]
681
864
  end
682
865
 
683
- # Checks that the encoding of a string is valid in Ruby 1.9
684
- # and cleans up potential encoding gotchas like the UTF-8 BOM.
685
- # If it's not, yields an error string describing the invalid character
686
- # and the line on which it occurrs.
687
- #
688
- # @param str [String] The string of which to check the encoding
689
- # @yield [msg] A block in which an encoding error can be raised.
690
- # Only yields if there is an encoding error
691
- # @yieldparam msg [String] The error message to be raised
692
- # @return [String] `str`, potentially with encoding gotchas like BOMs removed
693
- def check_encoding(str)
694
- if ruby1_8?
695
- return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
696
- elsif str.valid_encoding?
697
- # Get rid of the Unicode BOM if possible
698
- if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
699
- return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
700
- else
701
- return str
702
- end
703
- end
704
-
705
- encoding = str.encoding
706
- newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
707
- str.force_encoding("binary").split(newlines).each_with_index do |line, i|
708
- begin
709
- line.encode(encoding)
710
- rescue Encoding::UndefinedConversionError => e
711
- yield <<MSG.rstrip, i + 1
712
- Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}
713
- MSG
714
- end
715
- end
716
- str
866
+ unless ruby1_8?
867
+ CHARSET_REGEXP = /\A@charset "([^"]+)"/
868
+ bom = "\uFEFF"
869
+ UTF_8_BOM = bom.encode("UTF-8").force_encoding('BINARY')
870
+ UTF_16BE_BOM = bom.encode("UTF-16BE").force_encoding('BINARY')
871
+ UTF_16LE_BOM = bom.encode("UTF-16LE").force_encoding('BINARY')
717
872
  end
718
873
 
719
874
  # Like {\#check\_encoding}, but also checks for a `@charset` declaration
720
875
  # at the beginning of the file and uses that encoding if it exists.
721
876
  #
722
- # The Sass encoding rules are simple.
723
- # If a `@charset` declaration exists,
724
- # we assume that that's the original encoding of the document.
725
- # Otherwise, we use whatever encoding Ruby has.
726
- # Then we convert that to UTF-8 to process internally.
727
- # The UTF-8 end result is what's returned by this method.
877
+ # Sass follows CSS's decoding rules.
728
878
  #
729
879
  # @param str [String] The string of which to check the encoding
730
- # @yield [msg] A block in which an encoding error can be raised.
731
- # Only yields if there is an encoding error
732
- # @yieldparam msg [String] The error message to be raised
733
880
  # @return [(String, Encoding)] The original string encoded as UTF-8,
734
881
  # and the source encoding of the string (or `nil` under Ruby 1.8)
735
882
  # @raise [Encoding::UndefinedConversionError] if the source encoding
736
883
  # cannot be converted to UTF-8
737
884
  # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
738
- def check_sass_encoding(str, &block)
739
- return check_encoding(str, &block), nil if ruby1_8?
740
- # We allow any printable ASCII characters but double quotes in the charset decl
741
- bin = str.dup.force_encoding("BINARY")
742
- encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
743
- re = Sass::Util::CHARSET_REGEXPS[enc]
744
- re && bin =~ re
885
+ # @raise [Sass::SyntaxError] If the document declares an encoding that
886
+ # doesn't match its contents, or it doesn't declare an encoding and its
887
+ # contents are invalid in the native encoding.
888
+ def check_sass_encoding(str)
889
+ # On Ruby 1.8 we can't do anything complicated with encodings.
890
+ # Instead, we just strip out a UTF-8 BOM if it exists and
891
+ # sanitize according to Section 3.3 of CSS Syntax Level 3. We
892
+ # don't sanitize null characters since they might be components
893
+ # of other characters.
894
+ if ruby1_8?
895
+ return str.gsub(/\A\xEF\xBB\xBF/, '').gsub(/\r\n?|\f/, "\n"), nil
745
896
  end
746
- charset, bom = $1, $2
747
- if charset
748
- charset = charset.force_encoding(encoding).encode("UTF-8")
749
- if (endianness = encoding[/[BL]E$/])
750
- begin
751
- Encoding.find(charset + endianness)
752
- charset << endianness
753
- rescue ArgumentError # Encoding charset + endianness doesn't exist
897
+
898
+ # Determine the fallback encoding following section 3.2 of CSS Syntax Level 3 and Encodings:
899
+ # http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#determine-the-fallback-encoding
900
+ # http://encoding.spec.whatwg.org/#decode
901
+ binary = str.dup.force_encoding("BINARY")
902
+ if binary.start_with?(UTF_8_BOM)
903
+ binary.slice! 0, UTF_8_BOM.length
904
+ str = binary.force_encoding('UTF-8')
905
+ elsif binary.start_with?(UTF_16BE_BOM)
906
+ binary.slice! 0, UTF_16BE_BOM.length
907
+ str = binary.force_encoding('UTF-16BE')
908
+ elsif binary.start_with?(UTF_16LE_BOM)
909
+ binary.slice! 0, UTF_16LE_BOM.length
910
+ str = binary.force_encoding('UTF-16LE')
911
+ elsif binary =~ CHARSET_REGEXP
912
+ charset = $1.force_encoding('US-ASCII')
913
+ # Ruby 1.9.2 doesn't recognize a UTF-16 encoding without an endian marker.
914
+ if ruby1_9_2? && charset.downcase == 'utf-16'
915
+ encoding = Encoding.find('UTF-8')
916
+ else
917
+ encoding = Encoding.find(charset)
918
+ if encoding.name == 'UTF-16' || encoding.name == 'UTF-16BE'
919
+ encoding = Encoding.find('UTF-8')
754
920
  end
755
921
  end
756
- str.force_encoding(charset)
757
- elsif bom
758
- str.force_encoding(encoding)
922
+ str = binary.force_encoding(encoding)
923
+ elsif str.encoding.name == "ASCII-8BIT"
924
+ # Normally we want to fall back on believing the Ruby string
925
+ # encoding, but if that's just binary we want to make sure
926
+ # it's valid UTF-8.
927
+ str = str.force_encoding('utf-8')
759
928
  end
760
929
 
761
- str = check_encoding(str, &block)
762
- return str.encode("UTF-8"), str.encoding
763
- end
930
+ find_encoding_error(str) unless str.valid_encoding?
764
931
 
765
- unless ruby1_8?
766
- # @private
767
- def _enc(string, encoding)
768
- string.encode(encoding).force_encoding("BINARY")
769
- end
770
-
771
- # We could automatically add in any non-ASCII-compatible encodings here,
772
- # but there's not really a good way to do that
773
- # without manually checking that each encoding
774
- # encodes all ASCII characters properly,
775
- # which takes long enough to affect the startup time of the CLI.
776
- ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
777
-
778
- CHARSET_REGEXPS = Hash.new do |h, e|
779
- h[e] =
780
- begin
781
- # /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
782
- Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
783
- _enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
784
- _enc("\uFEFF", e)})/)
785
- rescue Encoding::ConverterNotFoundError => _
786
- nil # JRuby on Java 5 doesn't support UTF-32
787
- rescue
788
- # /\A@charset "(.*?)"/
789
- Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
790
- end
932
+ begin
933
+ # If the string is valid, preprocess it according to section 3.3 of CSS Syntax Level 3.
934
+ return str.encode("UTF-8").gsub(/\r\n?|\f/, "\n").tr("\u0000", "�"), str.encoding
935
+ rescue EncodingError
936
+ find_encoding_error(str)
791
937
  end
792
938
  end
793
939
 
@@ -820,7 +966,7 @@ MSG
820
966
  # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
821
967
  #
822
968
  # @param enum [Enumerable] The enumerable to get the enumerator for
823
- # @param n [Fixnum] The size of each cons
969
+ # @param n [Integer] The size of each cons
824
970
  # @return [Enumerator] The consed enumerator
825
971
  def enum_cons(enum, n)
826
972
  ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
@@ -829,7 +975,7 @@ MSG
829
975
  # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
830
976
  #
831
977
  # @param enum [Enumerable] The enumerable to get the enumerator for
832
- # @param n [Fixnum] The size of each slice
978
+ # @param n [Integer] The size of each slice
833
979
  # @return [Enumerator] The consed enumerator
834
980
  def enum_slice(enum, n)
835
981
  ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
@@ -856,22 +1002,11 @@ MSG
856
1002
  # Returns the ASCII code of the given character.
857
1003
  #
858
1004
  # @param c [String] All characters but the first are ignored.
859
- # @return [Fixnum] The ASCII code of `c`.
1005
+ # @return [Integer] The ASCII code of `c`.
860
1006
  def ord(c)
861
1007
  ruby1_8? ? c[0] : c.ord
862
1008
  end
863
1009
 
864
- # Flattens the first `n` nested arrays in a cross-version manner.
865
- #
866
- # @param arr [Array] The array to flatten
867
- # @param n [Fixnum] The number of levels to flatten
868
- # @return [Array] The flattened array
869
- def flatten(arr, n)
870
- return arr.flatten(n) unless ruby1_8_6?
871
- return arr if n == 0
872
- arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
873
- end
874
-
875
1010
  # Flattens the first level of nested arrays in `arrs`. Unlike
876
1011
  # `Array#flatten`, this orders the result by taking the first
877
1012
  # values from each array in order, then the second, and so on.
@@ -890,27 +1025,6 @@ MSG
890
1025
  result
891
1026
  end
892
1027
 
893
- # Returns the hash code for a set in a cross-version manner.
894
- # Aggravatingly, this is order-dependent in Ruby 1.8.6.
895
- #
896
- # @param set [Set]
897
- # @return [Fixnum] The order-independent hashcode of `set`
898
- def set_hash(set)
899
- return set.hash unless ruby1_8_6?
900
- set.map {|e| e.hash}.uniq.sort.hash
901
- end
902
-
903
- # Tests the hash-equality of two sets in a cross-version manner.
904
- # Aggravatingly, this is order-dependent in Ruby 1.8.6.
905
- #
906
- # @param set1 [Set]
907
- # @param set2 [Set]
908
- # @return [Boolean] Whether or not the sets are hashcode equal
909
- def set_eql?(set1, set2)
910
- return set1.eql?(set2) unless ruby1_8_6?
911
- set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
912
- end
913
-
914
1028
  # Like `Object#inspect`, but preserves non-ASCII characters rather than
915
1029
  # escaping them under Ruby 1.9.2. This is necessary so that the
916
1030
  # precompiled Haml template can be `#encode`d into `@options[:encoding]`
@@ -919,7 +1033,7 @@ MSG
919
1033
  # @param obj {Object}
920
1034
  # @return {String}
921
1035
  def inspect_obj(obj)
922
- return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
1036
+ return obj.inspect unless version_geq(RUBY_VERSION, "1.9.2")
923
1037
  return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
924
1038
  return obj.inspect unless obj.is_a?(String)
925
1039
  '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
@@ -1013,11 +1127,11 @@ MSG
1013
1127
 
1014
1128
  # Converts the argument into a valid JSON value.
1015
1129
  #
1016
- # @param v [Fixnum, String, Array, Boolean, nil]
1130
+ # @param v [Integer, String, Array, Boolean, nil]
1017
1131
  # @return [String]
1018
1132
  def json_value_of(v)
1019
1133
  case v
1020
- when Fixnum
1134
+ when Integer
1021
1135
  v.to_s
1022
1136
  when String
1023
1137
  "\"" + json_escape_string(v) + "\""
@@ -1039,7 +1153,7 @@ MSG
1039
1153
  VLQ_BASE_MASK = VLQ_BASE - 1
1040
1154
  VLQ_CONTINUATION_BIT = VLQ_BASE
1041
1155
 
1042
- BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1156
+ BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1043
1157
  BASE64_DIGIT_MAP = begin
1044
1158
  map = {}
1045
1159
  Sass::Util.enum_with_index(BASE64_DIGITS).map do |digit, i|
@@ -1050,7 +1164,7 @@ MSG
1050
1164
 
1051
1165
  # Encodes `value` as VLQ (http://en.wikipedia.org/wiki/VLQ).
1052
1166
  #
1053
- # @param value [Fixnum]
1167
+ # @param value [Integer]
1054
1168
  # @return [String] The encoded value
1055
1169
  def encode_vlq(value)
1056
1170
  if value < 0
@@ -1128,15 +1242,25 @@ MSG
1128
1242
  # rename operation.
1129
1243
  #
1130
1244
  # @param filename [String] The file to write to.
1245
+ # @param perms [Integer] The permissions used for creating this file.
1246
+ # Will be masked by the process umask. Defaults to readable/writeable
1247
+ # by all users however the umask usually changes this to only be writable
1248
+ # by the process's user.
1131
1249
  # @yieldparam tmpfile [Tempfile] The temp file that can be written to.
1132
1250
  # @return The value returned by the block.
1133
- def atomic_create_and_write_file(filename)
1251
+ def atomic_create_and_write_file(filename, perms = 0666)
1134
1252
  require 'tempfile'
1135
1253
  tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
1136
1254
  tmpfile.binmode if tmpfile.respond_to?(:binmode)
1137
1255
  result = yield tmpfile
1138
1256
  tmpfile.close
1139
1257
  ATOMIC_WRITE_MUTEX.synchronize do
1258
+ begin
1259
+ File.chmod(perms & ~File.umask, tmpfile.path)
1260
+ rescue Errno::EPERM
1261
+ # If we don't have permissions to chmod the file, don't let that crash
1262
+ # the compilation. See issue 1215.
1263
+ end
1140
1264
  File.rename tmpfile.path, filename
1141
1265
  end
1142
1266
  result
@@ -1147,15 +1271,71 @@ MSG
1147
1271
  tmpfile.unlink if tmpfile
1148
1272
  end
1149
1273
 
1274
+ def load_listen!
1275
+ if defined?(gem)
1276
+ begin
1277
+ gem 'listen', '>= 1.1.0', '< 3.0.0'
1278
+ require 'listen'
1279
+ rescue Gem::LoadError
1280
+ dir = scope("vendor/listen/lib")
1281
+ $LOAD_PATH.unshift dir
1282
+ begin
1283
+ require 'listen'
1284
+ rescue LoadError => e
1285
+ if version_geq(RUBY_VERSION, "1.9.3")
1286
+ version_constraint = "~> 3.0"
1287
+ else
1288
+ version_constraint = "~> 1.1"
1289
+ end
1290
+ e.message << "\n" <<
1291
+ "Run \"gem install listen --version '#{version_constraint}'\" to get it."
1292
+ raise e
1293
+ end
1294
+ end
1295
+ else
1296
+ begin
1297
+ require 'listen'
1298
+ rescue LoadError => e
1299
+ dir = scope("vendor/listen/lib")
1300
+ if $LOAD_PATH.include?(dir)
1301
+ raise e unless File.exist?(scope(".git"))
1302
+ e.message << "\n" <<
1303
+ 'Run "git submodule update --init" to get the bundled version.'
1304
+ else
1305
+ $LOAD_PATH.unshift dir
1306
+ retry
1307
+ end
1308
+ end
1309
+ end
1310
+ end
1311
+
1150
1312
  private
1151
1313
 
1152
- # rubocop:disable LineLength
1314
+ def find_encoding_error(str)
1315
+ encoding = str.encoding
1316
+ cr = Regexp.quote("\r".encode(encoding).force_encoding('BINARY'))
1317
+ lf = Regexp.quote("\n".encode(encoding).force_encoding('BINARY'))
1318
+ ff = Regexp.quote("\f".encode(encoding).force_encoding('BINARY'))
1319
+ line_break = /#{cr}#{lf}?|#{ff}|#{lf}/
1320
+
1321
+ str.force_encoding("binary").split(line_break).each_with_index do |line, i|
1322
+ begin
1323
+ line.encode(encoding)
1324
+ rescue Encoding::UndefinedConversionError => e
1325
+ raise Sass::SyntaxError.new(
1326
+ "Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}",
1327
+ :line => i + 1)
1328
+ end
1329
+ end
1330
+
1331
+ # We shouldn't get here, but it's possible some weird encoding stuff causes it.
1332
+ return str, str.encoding
1333
+ end
1153
1334
 
1154
1335
  # Calculates the memoization table for the Least Common Subsequence algorithm.
1155
1336
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
1156
1337
  def lcs_table(x, y)
1157
1338
  # This method does not take a block as an explicit parameter for performance reasons.
1158
- # rubocop:enable LineLength
1159
1339
  c = Array.new(x.size) {[]}
1160
1340
  x.size.times {|i| c[i][0] = 0}
1161
1341
  y.size.times {|j| c[0][j] = 0}
@@ -1171,12 +1351,12 @@ MSG
1171
1351
  end
1172
1352
  c
1173
1353
  end
1174
- # rubocop:disable ParameterLists, LineLength
1354
+ # rubocop:disable ParameterLists
1175
1355
 
1176
1356
  # Computes a single longest common subsequence for arrays x and y.
1177
1357
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
1178
1358
  def lcs_backtrace(c, x, y, i, j, &block)
1179
- # rubocop:enable ParameterList, LineLengths
1359
+ # rubocop:enable ParameterList
1180
1360
  return [] if i == 0 || j == 0
1181
1361
  if (v = yield(x[i], y[j]))
1182
1362
  return lcs_backtrace(c, x, y, i - 1, j - 1, &block) << v