sass 3.1.0 → 3.3.0

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 (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
data/lib/sass/util.rb CHANGED
@@ -2,8 +2,10 @@ require 'erb'
2
2
  require 'set'
3
3
  require 'enumerator'
4
4
  require 'stringio'
5
- require 'strscan'
6
5
  require 'rbconfig'
6
+ require 'uri'
7
+ require 'thread'
8
+ require 'pathname'
7
9
 
8
10
  require 'sass/root'
9
11
  require 'sass/util/subset_map'
@@ -38,7 +40,7 @@ module Sass
38
40
  # @param arr [Array<(Object, Object)>] An array of pairs
39
41
  # @return [Hash] A hash
40
42
  def to_hash(arr)
41
- Hash[arr.compact]
43
+ ordered_hash(*arr.compact)
42
44
  end
43
45
 
44
46
  # Maps the keys in a hash according to a block.
@@ -54,7 +56,7 @@ module Sass
54
56
  # @see #map_vals
55
57
  # @see #map_hash
56
58
  def map_keys(hash)
57
- to_hash(hash.map {|k, v| [yield(k), v]})
59
+ map_hash(hash) {|k, v| [yield(k), v]}
58
60
  end
59
61
 
60
62
  # Maps the values in a hash according to a block.
@@ -70,7 +72,14 @@ module Sass
70
72
  # @see #map_keys
71
73
  # @see #map_hash
72
74
  def map_vals(hash)
73
- to_hash(hash.map {|k, v| [k, yield(v)]})
75
+ # We don't delegate to map_hash for performance here
76
+ # because map_hash does more than is necessary.
77
+ rv = hash.class.new
78
+ hash = hash.as_stored if hash.is_a?(NormalizedMap)
79
+ hash.each do |k, v|
80
+ rv[k] = yield(v)
81
+ end
82
+ rv
74
83
  end
75
84
 
76
85
  # Maps the key-value pairs of a hash according to a block.
@@ -86,8 +95,16 @@ module Sass
86
95
  # @return [Hash] The mapped hash
87
96
  # @see #map_keys
88
97
  # @see #map_vals
89
- def map_hash(hash, &block)
90
- to_hash(hash.map(&block))
98
+ def map_hash(hash)
99
+ # Copy and modify is more performant than mapping to an array and using
100
+ # to_hash on the result.
101
+ rv = hash.class.new
102
+ hash.each do |k, v|
103
+ new_key, new_value = yield(k, v)
104
+ new_key = hash.denormalize(new_key) if hash.is_a?(NormalizedMap) && new_key == k
105
+ rv[new_key] = new_value
106
+ end
107
+ rv
91
108
  end
92
109
 
93
110
  # Computes the powerset of the given array.
@@ -155,6 +172,19 @@ module Sass
155
172
  enum.inject([]) {|a, e| a << e << val}[0...-1]
156
173
  end
157
174
 
175
+ def slice_by(enum)
176
+ results = []
177
+ enum.each do |value|
178
+ key = yield(value)
179
+ if !results.empty? && results.last.first == key
180
+ results.last.last << value
181
+ else
182
+ results << [key, [value]]
183
+ end
184
+ end
185
+ results
186
+ end
187
+
158
188
  # Substitutes a sub-array of one array with another sub-array.
159
189
  #
160
190
  # @param ary [Array] The array in which to make the substitution
@@ -164,8 +194,8 @@ module Sass
164
194
  res = ary.dup
165
195
  i = 0
166
196
  while i < res.size
167
- if res[i...i+from.size] == from
168
- res[i...i+from.size] = to
197
+ if res[i...i + from.size] == from
198
+ res[i...i + from.size] = to
169
199
  end
170
200
  i += 1
171
201
  end
@@ -216,15 +246,120 @@ module Sass
216
246
  x = [nil, *x]
217
247
  y = [nil, *y]
218
248
  block ||= proc {|a, b| a == b && a}
219
- lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
249
+ lcs_backtrace(lcs_table(x, y, &block), x, y, x.size - 1, y.size - 1, &block)
250
+ end
251
+
252
+ # Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
253
+ # with the following exceptions:
254
+ #
255
+ # * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
256
+ # * In Ruby 1.9 when running tests, this is ordered in the same way it would
257
+ # be under Ruby 1.8 (sorted key order rather than insertion order).
258
+ #
259
+ # @param hash [Hash]
260
+ # @return [Array]
261
+ def hash_to_a(hash)
262
+ return hash.to_a unless ruby1_8? || defined?(Test::Unit)
263
+ hash.sort_by {|k, v| k}
264
+ end
265
+
266
+ # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
267
+ # order. Unlike {Util#hash_to_a}, the resulting order isn't sorted key order;
268
+ # instead, it's the same order as `#group_by` has under Ruby 1.9 (key
269
+ # appearance order).
270
+ #
271
+ # @param enum [Enumerable]
272
+ # @return [Array<[Object, Array]>] An array of pairs.
273
+ def group_by_to_a(enum)
274
+ return enum.group_by {|e| yield(e)}.to_a unless ruby1_8?
275
+ order = {}
276
+ arr = []
277
+ groups = enum.group_by do |e|
278
+ res = yield(e)
279
+ unless order.include?(res)
280
+ order[res] = order.size
281
+ end
282
+ res
283
+ end
284
+ groups.each do |key, vals|
285
+ arr[order[key]] = [key, vals]
286
+ end
287
+ arr
288
+ end
289
+
290
+ # Returns a sub-array of `minuend` containing only elements that are also in
291
+ # `subtrahend`. Ensures that the return value has the same order as
292
+ # `minuend`, even on Rubinius where that's not guaranteed by `Array#-`.
293
+ #
294
+ # @param minuend [Array]
295
+ # @param subtrahend [Array]
296
+ # @return [Array]
297
+ def array_minus(minuend, subtrahend)
298
+ return minuend - subtrahend unless rbx?
299
+ set = Set.new(minuend) - subtrahend
300
+ minuend.select {|e| set.include?(e)}
301
+ end
302
+
303
+ # Returns a string description of the character that caused an
304
+ # `Encoding::UndefinedConversionError`.
305
+ #
306
+ # @param e [Encoding::UndefinedConversionError]
307
+ # @return [String]
308
+ def undefined_conversion_error_char(e)
309
+ # Rubinius (as of 2.0.0.rc1) pre-quotes the error character.
310
+ return e.error_char if rbx?
311
+ # JRuby (as of 1.7.2) doesn't have an error_char field on
312
+ # Encoding::UndefinedConversionError.
313
+ return e.error_char.dump unless jruby?
314
+ e.message[/^"[^"]+"/] # "
315
+ end
316
+
317
+ # Asserts that `value` falls within `range` (inclusive), leaving
318
+ # room for slight floating-point errors.
319
+ #
320
+ # @param name [String] The name of the value. Used in the error message.
321
+ # @param range [Range] The allowed range of values.
322
+ # @param value [Numeric, Sass::Script::Value::Number] The value to check.
323
+ # @param unit [String] The unit of the value. Used in error reporting.
324
+ # @return [Numeric] `value` adjusted to fall within range, if it
325
+ # was outside by a floating-point margin.
326
+ def check_range(name, range, value, unit = '')
327
+ grace = (-0.00001..0.00001)
328
+ str = value.to_s
329
+ value = value.value if value.is_a?(Sass::Script::Value::Number)
330
+ return value if range.include?(value)
331
+ return range.first if grace.include?(value - range.first)
332
+ return range.last if grace.include?(value - range.last)
333
+ raise ArgumentError.new(
334
+ "#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
335
+ end
336
+
337
+ # Returns whether or not `seq1` is a subsequence of `seq2`. That is, whether
338
+ # or not `seq2` contains every element in `seq1` in the same order (and
339
+ # possibly more elements besides).
340
+ #
341
+ # @param seq1 [Array]
342
+ # @param seq2 [Array]
343
+ # @return [Boolean]
344
+ def subsequence?(seq1, seq2)
345
+ i = j = 0
346
+ loop do
347
+ return true if i == seq1.size
348
+ return false if j == seq2.size
349
+ i += 1 if seq1[i] == seq2[j]
350
+ j += 1
351
+ end
220
352
  end
221
353
 
222
354
  # Returns information about the caller of the previous method.
223
355
  #
224
356
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
225
- # @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
357
+ # @return [[String, Fixnum, (String, nil)]]
358
+ # An array containing the filename, line, and method name of the caller.
226
359
  # The method name may be nil
227
- def caller_info(entry = caller[1])
360
+ def caller_info(entry = nil)
361
+ # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
362
+ entry ||= caller[1]
228
363
  info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
229
364
  info[1] = info[1].to_i
230
365
  # This is added by Rubinius to designate a block, but we don't care about it.
@@ -278,6 +413,17 @@ module Sass
278
413
  raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
279
414
  end
280
415
 
416
+ # Prints a deprecation warning for the caller method.
417
+ #
418
+ # @param obj [Object] `self`
419
+ # @param message [String] A message describing what to do instead.
420
+ def deprecated(obj, message = nil)
421
+ obj_class = obj.is_a?(Class) ? "#{obj}." : "#{obj.class}#"
422
+ full_message = "DEPRECATION WARNING: #{obj_class}#{caller_info[2]} " +
423
+ "will be removed in a future version of Sass.#{("\n" + message) if message}"
424
+ Sass::Util.sass_warn full_message
425
+ end
426
+
281
427
  # Silence all output to STDERR within a block.
282
428
  #
283
429
  # @yield A block in which no output will be printed to STDERR
@@ -288,24 +434,22 @@ module Sass
288
434
  $stderr = the_real_stderr
289
435
  end
290
436
 
291
- @@silence_warnings = false
292
437
  # Silences all Sass warnings within a block.
293
438
  #
294
439
  # @yield A block in which no Sass warnings will be printed
295
440
  def silence_sass_warnings
296
- old_silence_warnings = @@silence_warnings
297
- @@silence_warnings = true
441
+ old_level, Sass.logger.log_level = Sass.logger.log_level, :error
298
442
  yield
299
443
  ensure
300
- @@silence_warnings = old_silence_warnings
444
+ Sass.logger.log_level = old_level
301
445
  end
302
446
 
303
447
  # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
304
448
  #
305
449
  # @param msg [String]
306
450
  def sass_warn(msg)
307
- return if @@silence_warnings
308
- warn(msg)
451
+ msg = msg + "\n" unless ruby1?
452
+ Sass.logger.warn(msg)
309
453
  end
310
454
 
311
455
  ## Cross Rails Version Compatibility
@@ -321,7 +465,7 @@ module Sass
321
465
  raise "ERROR: Rails.root is nil!"
322
466
  end
323
467
  return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
324
- return nil
468
+ nil
325
469
  end
326
470
 
327
471
  # Returns the environment of the Rails application,
@@ -332,7 +476,7 @@ module Sass
332
476
  def rails_env
333
477
  return ::Rails.env.to_s if defined?(::Rails.env)
334
478
  return RAILS_ENV.to_s if defined?(RAILS_ENV)
335
- return nil
479
+ nil
336
480
  end
337
481
 
338
482
  # Returns whether this environment is using ActionPack
@@ -358,6 +502,15 @@ module Sass
358
502
  version_geq(ActionPack::VERSION::STRING, version)
359
503
  end
360
504
 
505
+ # Returns whether this environment is using Listen
506
+ # version 2.0.0 or greater.
507
+ #
508
+ # @return [Boolean]
509
+ def listen_geq_2?
510
+ require 'listen/version'
511
+ version_geq(::Listen::VERSION, '2.0.0')
512
+ end
513
+
361
514
  # Returns an ActionView::Template* class.
362
515
  # In pre-3.0 versions of Rails, most of these classes
363
516
  # were of the form `ActionView::TemplateFoo`,
@@ -368,27 +521,98 @@ module Sass
368
521
  # or `ActionView::Template::Error`.
369
522
  def av_template_class(name)
370
523
  return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
371
- return ActionView::Template.const_get(name.to_s)
524
+ ActionView::Template.const_get(name.to_s)
372
525
  end
373
526
 
374
527
  ## Cross-OS Compatibility
528
+ #
529
+ # These methods are cached because some of them are called quite frequently
530
+ # and even basic checks like String#== are too costly to be called repeatedly.
375
531
 
376
532
  # Whether or not this is running on Windows.
377
533
  #
378
534
  # @return [Boolean]
379
535
  def windows?
380
- RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
536
+ return @windows if defined?(@windows)
537
+ @windows = (RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i)
381
538
  end
382
539
 
383
540
  # Whether or not this is running on IronRuby.
384
541
  #
385
542
  # @return [Boolean]
386
543
  def ironruby?
387
- RUBY_ENGINE == "ironruby"
544
+ return @ironruby if defined?(@ironruby)
545
+ @ironruby = RUBY_ENGINE == "ironruby"
546
+ end
547
+
548
+ # Whether or not this is running on Rubinius.
549
+ #
550
+ # @return [Boolean]
551
+ def rbx?
552
+ return @rbx if defined?(@rbx)
553
+ @rbx = RUBY_ENGINE == "rbx"
554
+ end
555
+
556
+ # Whether or not this is running on JRuby.
557
+ #
558
+ # @return [Boolean]
559
+ def jruby?
560
+ return @jruby if defined?(@jruby)
561
+ @jruby = RUBY_PLATFORM =~ /java/
562
+ end
563
+
564
+ # Returns an array of ints representing the JRuby version number.
565
+ #
566
+ # @return [Array<Fixnum>]
567
+ def jruby_version
568
+ @jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
569
+ end
570
+
571
+ # Like `Dir.glob`, but works with backslash-separated paths on Windows.
572
+ #
573
+ # @param path [String]
574
+ def glob(path)
575
+ path = path.gsub('\\', '/') if windows?
576
+ if block_given?
577
+ Dir.glob(path) {|f| yield(f)}
578
+ else
579
+ Dir.glob(path)
580
+ end
581
+ end
582
+
583
+ # Like `Pathname.new`, but normalizes Windows paths to always use backslash
584
+ # separators.
585
+ #
586
+ # `Pathname.relative_path_from` can break if the two pathnames aren't
587
+ # consistent in their slash style.
588
+ def pathname(path)
589
+ path = path.tr("/", "\\") if windows?
590
+ Pathname.new(path)
591
+ end
592
+
593
+ # Prepare a value for a destructuring assignment (e.g. `a, b =
594
+ # val`). This works around a performance bug when using
595
+ # ActiveSupport, and only needs to be called when `val` is likely
596
+ # to be `nil` reasonably often.
597
+ #
598
+ # See [this bug report](http://redmine.ruby-lang.org/issues/4917).
599
+ #
600
+ # @param val [Object]
601
+ # @return [Object]
602
+ def destructure(val)
603
+ val || []
388
604
  end
389
605
 
390
606
  ## Cross-Ruby-Version Compatibility
391
607
 
608
+ # Whether or not this is running under a Ruby version under 2.0.
609
+ #
610
+ # @return [Boolean]
611
+ def ruby1?
612
+ return @ruby1 if defined?(@ruby1)
613
+ @ruby1 = Sass::Util::RUBY_VERSION[0] <= 1
614
+ end
615
+
392
616
  # Whether or not this is running under Ruby 1.8 or lower.
393
617
  #
394
618
  # Note that IronRuby counts as Ruby 1.8,
@@ -398,7 +622,9 @@ module Sass
398
622
  def ruby1_8?
399
623
  # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
400
624
  # We have to fall back to 1.8 behavior.
401
- ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
625
+ return @ruby1_8 if defined?(@ruby1_8)
626
+ @ruby1_8 = ironruby? ||
627
+ (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
402
628
  end
403
629
 
404
630
  # Whether or not this is running under Ruby 1.8.6 or lower.
@@ -406,7 +632,52 @@ module Sass
406
632
  #
407
633
  # @return [Boolean]
408
634
  def ruby1_8_6?
409
- ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
635
+ return @ruby1_8_6 if defined?(@ruby1_8_6)
636
+ @ruby1_8_6 = ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
637
+ end
638
+
639
+ # Wehter or not this is running under JRuby 1.6 or lower.
640
+ def jruby1_6?
641
+ return @jruby1_6 if defined?(@jruby1_6)
642
+ @jruby1_6 = jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
643
+ end
644
+
645
+ # Whether or not this is running under MacRuby.
646
+ #
647
+ # @return [Boolean]
648
+ def macruby?
649
+ return @macruby if defined?(@macruby)
650
+ @macruby = RUBY_ENGINE == 'macruby'
651
+ end
652
+
653
+ require 'sass/util/ordered_hash' if ruby1_8?
654
+
655
+ # Converts a hash or a list of pairs into an order-preserving hash.
656
+ #
657
+ # On Ruby 1.8.7, this uses the orderedhash gem to simulate an
658
+ # order-preserving hash. On Ruby 1.9 and up, it just uses the native Hash
659
+ # class, since that preserves the order itself.
660
+ #
661
+ # @overload ordered_hash(hash)
662
+ # @param hash [Hash] a normal hash to convert to an ordered hash
663
+ # @return [Hash]
664
+ # @overload ordered_hash(*pairs)
665
+ # @example
666
+ # ordered_hash([:foo, "bar"], [:baz, "bang"])
667
+ # #=> {:foo => "bar", :baz => "bang"}
668
+ # ordered_hash #=> {}
669
+ # @param pairs [Array<(Object, Object)>] the list of key/value pairs for
670
+ # the hash.
671
+ # @return [Hash]
672
+ def ordered_hash(*pairs_or_hash)
673
+ if pairs_or_hash.length == 1 && pairs_or_hash.first.is_a?(Hash)
674
+ hash = pairs_or_hash.first
675
+ return hash unless ruby1_8?
676
+ return OrderedHash.new.merge hash
677
+ end
678
+
679
+ return Hash[pairs_or_hash] unless ruby1_8?
680
+ (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
410
681
  end
411
682
 
412
683
  # Checks that the encoding of a string is valid in Ruby 1.9
@@ -438,11 +709,11 @@ module Sass
438
709
  line.encode(encoding)
439
710
  rescue Encoding::UndefinedConversionError => e
440
711
  yield <<MSG.rstrip, i + 1
441
- Invalid #{encoding.name} character #{e.error_char.dump}
712
+ Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}
442
713
  MSG
443
714
  end
444
715
  end
445
- return str
716
+ str
446
717
  end
447
718
 
448
719
  # Like {\#check\_encoding}, but also checks for a `@charset` declaration
@@ -469,12 +740,13 @@ MSG
469
740
  # We allow any printable ASCII characters but double quotes in the charset decl
470
741
  bin = str.dup.force_encoding("BINARY")
471
742
  encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
472
- bin =~ Sass::Util::CHARSET_REGEXPS[enc]
743
+ re = Sass::Util::CHARSET_REGEXPS[enc]
744
+ re && bin =~ re
473
745
  end
474
746
  charset, bom = $1, $2
475
747
  if charset
476
748
  charset = charset.force_encoding(encoding).encode("UTF-8")
477
- if endianness = encoding[/[BL]E$/]
749
+ if (endianness = encoding[/[BL]E$/])
478
750
  begin
479
751
  Encoding.find(charset + endianness)
480
752
  charset << endianness
@@ -510,6 +782,8 @@ MSG
510
782
  Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
511
783
  _enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
512
784
  _enc("\uFEFF", e)})/)
785
+ rescue Encoding::ConverterNotFoundError => _
786
+ nil # JRuby on Java 5 doesn't support UTF-32
513
787
  rescue
514
788
  # /\A@charset "(.*?)"/
515
789
  Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
@@ -561,6 +835,24 @@ MSG
561
835
  ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
562
836
  end
563
837
 
838
+ # Destructively removes all elements from an array that match a block, and
839
+ # returns the removed elements.
840
+ #
841
+ # @param array [Array] The array from which to remove elements.
842
+ # @yield [el] Called for each element.
843
+ # @yieldparam el [*] The element to test.
844
+ # @yieldreturn [Boolean] Whether or not to extract the element.
845
+ # @return [Array] The extracted elements.
846
+ def extract!(array)
847
+ out = []
848
+ array.reject! do |e|
849
+ next false unless yield e
850
+ out << e
851
+ true
852
+ end
853
+ out
854
+ end
855
+
564
856
  # Returns the ASCII code of the given character.
565
857
  #
566
858
  # @param c [String] All characters but the first are ignored.
@@ -580,6 +872,24 @@ MSG
580
872
  arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
581
873
  end
582
874
 
875
+ # Flattens the first level of nested arrays in `arrs`. Unlike
876
+ # `Array#flatten`, this orders the result by taking the first
877
+ # values from each array in order, then the second, and so on.
878
+ #
879
+ # @param arrs [Array] The array to flatten.
880
+ # @return [Array] The flattened array.
881
+ def flatten_vertically(arrs)
882
+ result = []
883
+ arrs = arrs.map {|sub| sub.is_a?(Array) ? sub.dup : Array(sub)}
884
+ until arrs.empty?
885
+ arrs.reject! do |arr|
886
+ result << arr.shift
887
+ arr.empty?
888
+ end
889
+ end
890
+ result
891
+ end
892
+
583
893
  # Returns the hash code for a set in a cross-version manner.
584
894
  # Aggravatingly, this is order-dependent in Ruby 1.8.6.
585
895
  #
@@ -601,8 +911,9 @@ MSG
601
911
  set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
602
912
  end
603
913
 
604
- # Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
605
- # This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
914
+ # Like `Object#inspect`, but preserves non-ASCII characters rather than
915
+ # escaping them under Ruby 1.9.2. This is necessary so that the
916
+ # precompiled Haml template can be `#encode`d into `@options[:encoding]`
606
917
  # before being evaluated.
607
918
  #
608
919
  # @param obj {Object}
@@ -614,6 +925,181 @@ MSG
614
925
  '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
615
926
  end
616
927
 
928
+ # Extracts the non-string vlaues from an array containing both strings and non-strings.
929
+ # These values are replaced with escape sequences.
930
+ # This can be undone using \{#inject\_values}.
931
+ #
932
+ # This is useful e.g. when we want to do string manipulation
933
+ # on an interpolated string.
934
+ #
935
+ # The precise format of the resulting string is not guaranteed.
936
+ # However, it is guaranteed that newlines and whitespace won't be affected.
937
+ #
938
+ # @param arr [Array] The array from which values are extracted.
939
+ # @return [(String, Array)] The resulting string, and an array of extracted values.
940
+ def extract_values(arr)
941
+ values = []
942
+ mapped = arr.map do |e|
943
+ next e.gsub('{', '{{') if e.is_a?(String)
944
+ values << e
945
+ next "{#{values.count - 1}}"
946
+ end
947
+ return mapped.join, values
948
+ end
949
+
950
+ # Undoes \{#extract\_values} by transforming a string with escape sequences
951
+ # into an array of strings and non-string values.
952
+ #
953
+ # @param str [String] The string with escape sequences.
954
+ # @param values [Array] The array of values to inject.
955
+ # @return [Array] The array of strings and values.
956
+ def inject_values(str, values)
957
+ return [str.gsub('{{', '{')] if values.empty?
958
+ # Add an extra { so that we process the tail end of the string
959
+ result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
960
+ [pre, esc ? '{' : '', n ? values[n.to_i] : '']
961
+ end.flatten(1)
962
+ result[-2] = '' # Get rid of the extra {
963
+ merge_adjacent_strings(result).reject {|s| s == ''}
964
+ end
965
+
966
+ # Allows modifications to be performed on the string form
967
+ # of an array containing both strings and non-strings.
968
+ #
969
+ # @param arr [Array] The array from which values are extracted.
970
+ # @yield [str] A block in which string manipulation can be done to the array.
971
+ # @yieldparam str [String] The string form of `arr`.
972
+ # @yieldreturn [String] The modified string.
973
+ # @return [Array] The modified, interpolated array.
974
+ def with_extracted_values(arr)
975
+ str, vals = extract_values(arr)
976
+ str = yield str
977
+ inject_values(str, vals)
978
+ end
979
+
980
+ # Builds a sourcemap file name given the generated CSS file name.
981
+ #
982
+ # @param css [String] The generated CSS file name.
983
+ # @return [String] The source map file name.
984
+ def sourcemap_name(css)
985
+ css + ".map"
986
+ end
987
+
988
+ # Escapes certain characters so that the result can be used
989
+ # as the JSON string value. Returns the original string if
990
+ # no escaping is necessary.
991
+ #
992
+ # @param s [String] The string to be escaped
993
+ # @return [String] The escaped string
994
+ def json_escape_string(s)
995
+ return s if s !~ /["\\\b\f\n\r\t]/
996
+
997
+ result = ""
998
+ s.split("").each do |c|
999
+ case c
1000
+ when '"', "\\"
1001
+ result << "\\" << c
1002
+ when "\n" then result << "\\n"
1003
+ when "\t" then result << "\\t"
1004
+ when "\r" then result << "\\r"
1005
+ when "\f" then result << "\\f"
1006
+ when "\b" then result << "\\b"
1007
+ else
1008
+ result << c
1009
+ end
1010
+ end
1011
+ result
1012
+ end
1013
+
1014
+ # Converts the argument into a valid JSON value.
1015
+ #
1016
+ # @param v [Fixnum, String, Array, Boolean, nil]
1017
+ # @return [String]
1018
+ def json_value_of(v)
1019
+ case v
1020
+ when Fixnum
1021
+ v.to_s
1022
+ when String
1023
+ "\"" + json_escape_string(v) + "\""
1024
+ when Array
1025
+ "[" + v.map {|x| json_value_of(x)}.join(",") + "]"
1026
+ when NilClass
1027
+ "null"
1028
+ when TrueClass
1029
+ "true"
1030
+ when FalseClass
1031
+ "false"
1032
+ else
1033
+ raise ArgumentError.new("Unknown type: #{v.class.name}")
1034
+ end
1035
+ end
1036
+
1037
+ VLQ_BASE_SHIFT = 5
1038
+ VLQ_BASE = 1 << VLQ_BASE_SHIFT
1039
+ VLQ_BASE_MASK = VLQ_BASE - 1
1040
+ VLQ_CONTINUATION_BIT = VLQ_BASE
1041
+
1042
+ BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1043
+ BASE64_DIGIT_MAP = begin
1044
+ map = {}
1045
+ Sass::Util.enum_with_index(BASE64_DIGITS).map do |digit, i|
1046
+ map[digit] = i
1047
+ end
1048
+ map
1049
+ end
1050
+
1051
+ # Encodes `value` as VLQ (http://en.wikipedia.org/wiki/VLQ).
1052
+ #
1053
+ # @param value [Fixnum]
1054
+ # @return [String] The encoded value
1055
+ def encode_vlq(value)
1056
+ if value < 0
1057
+ value = ((-value) << 1) | 1
1058
+ else
1059
+ value <<= 1
1060
+ end
1061
+
1062
+ result = ''
1063
+ begin
1064
+ digit = value & VLQ_BASE_MASK
1065
+ value >>= VLQ_BASE_SHIFT
1066
+ if value > 0
1067
+ digit |= VLQ_CONTINUATION_BIT
1068
+ end
1069
+ result << BASE64_DIGITS[digit]
1070
+ end while value > 0
1071
+ result
1072
+ end
1073
+
1074
+ # This is a hack around the fact that you can't instantiate a URI parser on
1075
+ # 1.8, so we have to have this hacky stuff to work around it. When 1.8
1076
+ # support is dropped, we can remove this method.
1077
+ #
1078
+ # @private
1079
+ URI_ESCAPE = URI.const_defined?("DEFAULT_PARSER") ? URI::DEFAULT_PARSER : URI
1080
+
1081
+ # URI-escape `string`.
1082
+ #
1083
+ # @param string [String]
1084
+ # @return [String]
1085
+ def escape_uri(string)
1086
+ URI_ESCAPE.escape string
1087
+ end
1088
+
1089
+ # A cross-platform implementation of `File.absolute_path`.
1090
+ #
1091
+ # @param path [String]
1092
+ # @param dir_string [String] The directory to consider [path] relative to.
1093
+ # @return [String] The absolute version of `path`.
1094
+ def absolute_path(path, dir_string = nil)
1095
+ # Ruby 1.8 doesn't support File.absolute_path.
1096
+ return File.absolute_path(path, dir_string) unless ruby1_8?
1097
+
1098
+ # File.expand_path expands "~", which we don't want.
1099
+ return File.expand_path(path, dir_string) unless path[0] == ?~
1100
+ File.expand_path(File.join(".", path), dir_string)
1101
+ end
1102
+
617
1103
  ## Static Method Stuff
618
1104
 
619
1105
  # The context in which the ERB for \{#def\_static\_method} will be run.
@@ -627,17 +1113,49 @@ MSG
627
1113
  #
628
1114
  # @param name [Symbol] The name of the variable
629
1115
  # @return [Boolean]
630
- def method_missing(name, *args, &block)
631
- super unless args.empty? && block.nil?
1116
+ def method_missing(name, *args)
1117
+ super unless args.empty? && !block_given?
632
1118
  @set.include?(name)
633
1119
  end
634
1120
  end
635
1121
 
1122
+ # @private
1123
+ ATOMIC_WRITE_MUTEX = Mutex.new
1124
+
1125
+ # This creates a temp file and yields it for writing. When the
1126
+ # write is complete, the file is moved into the desired location.
1127
+ # The atomicity of this operation is provided by the filesystem's
1128
+ # rename operation.
1129
+ #
1130
+ # @param filename [String] The file to write to.
1131
+ # @yieldparam tmpfile [Tempfile] The temp file that can be written to.
1132
+ # @return The value returned by the block.
1133
+ def atomic_create_and_write_file(filename)
1134
+ require 'tempfile'
1135
+ tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
1136
+ tmpfile.binmode if tmpfile.respond_to?(:binmode)
1137
+ result = yield tmpfile
1138
+ tmpfile.close
1139
+ ATOMIC_WRITE_MUTEX.synchronize do
1140
+ File.rename tmpfile.path, filename
1141
+ end
1142
+ result
1143
+ ensure
1144
+ # close and remove the tempfile if it still exists,
1145
+ # presumably due to an error during write
1146
+ tmpfile.close if tmpfile
1147
+ tmpfile.unlink if tmpfile
1148
+ end
1149
+
636
1150
  private
637
1151
 
1152
+ # rubocop:disable LineLength
1153
+
638
1154
  # Calculates the memoization table for the Least Common Subsequence algorithm.
639
1155
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
640
1156
  def lcs_table(x, y)
1157
+ # This method does not take a block as an explicit parameter for performance reasons.
1158
+ # rubocop:enable LineLength
641
1159
  c = Array.new(x.size) {[]}
642
1160
  x.size.times {|i| c[i][0] = 0}
643
1161
  y.size.times {|j| c[0][j] = 0}
@@ -645,25 +1163,33 @@ MSG
645
1163
  (1...y.size).each do |j|
646
1164
  c[i][j] =
647
1165
  if yield x[i], y[j]
648
- c[i-1][j-1] + 1
1166
+ c[i - 1][j - 1] + 1
649
1167
  else
650
- [c[i][j-1], c[i-1][j]].max
1168
+ [c[i][j - 1], c[i - 1][j]].max
651
1169
  end
652
1170
  end
653
1171
  end
654
- return c
1172
+ c
655
1173
  end
1174
+ # rubocop:disable ParameterLists, LineLength
656
1175
 
657
1176
  # Computes a single longest common subsequence for arrays x and y.
658
1177
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
659
1178
  def lcs_backtrace(c, x, y, i, j, &block)
1179
+ # rubocop:enable ParameterList, LineLengths
660
1180
  return [] if i == 0 || j == 0
661
- if v = yield(x[i], y[j])
662
- return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
1181
+ if (v = yield(x[i], y[j]))
1182
+ return lcs_backtrace(c, x, y, i - 1, j - 1, &block) << v
663
1183
  end
664
1184
 
665
- return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
666
- return lcs_backtrace(c, x, y, i-1, j, &block)
1185
+ return lcs_backtrace(c, x, y, i, j - 1, &block) if c[i][j - 1] > c[i - 1][j]
1186
+ lcs_backtrace(c, x, y, i - 1, j, &block)
667
1187
  end
1188
+
1189
+ singleton_methods.each {|method| module_function method}
668
1190
  end
669
1191
  end
1192
+
1193
+ require 'sass/util/multibyte_string_scanner'
1194
+ require 'sass/util/normalized_map'
1195
+ require 'sass/util/cross_platform_random'