sass4 4.0.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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/AGENTS.md +534 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/CONTRIBUTING.md +148 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +242 -0
  8. data/VERSION +1 -0
  9. data/VERSION_NAME +1 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/sass-spec-ref.sh +40 -0
  14. data/extra/update_watch.rb +13 -0
  15. data/init.rb +18 -0
  16. data/lib/sass/cache_stores/base.rb +88 -0
  17. data/lib/sass/cache_stores/chain.rb +34 -0
  18. data/lib/sass/cache_stores/filesystem.rb +60 -0
  19. data/lib/sass/cache_stores/memory.rb +46 -0
  20. data/lib/sass/cache_stores/null.rb +25 -0
  21. data/lib/sass/cache_stores.rb +15 -0
  22. data/lib/sass/callbacks.rb +67 -0
  23. data/lib/sass/css.rb +407 -0
  24. data/lib/sass/deprecation.rb +55 -0
  25. data/lib/sass/engine.rb +1236 -0
  26. data/lib/sass/environment.rb +236 -0
  27. data/lib/sass/error.rb +198 -0
  28. data/lib/sass/exec/base.rb +188 -0
  29. data/lib/sass/exec/sass_convert.rb +283 -0
  30. data/lib/sass/exec/sass_scss.rb +436 -0
  31. data/lib/sass/exec.rb +9 -0
  32. data/lib/sass/features.rb +48 -0
  33. data/lib/sass/importers/base.rb +182 -0
  34. data/lib/sass/importers/deprecated_path.rb +51 -0
  35. data/lib/sass/importers/filesystem.rb +221 -0
  36. data/lib/sass/importers.rb +23 -0
  37. data/lib/sass/logger/base.rb +47 -0
  38. data/lib/sass/logger/delayed.rb +50 -0
  39. data/lib/sass/logger/log_level.rb +45 -0
  40. data/lib/sass/logger.rb +17 -0
  41. data/lib/sass/media.rb +210 -0
  42. data/lib/sass/plugin/compiler.rb +552 -0
  43. data/lib/sass/plugin/configuration.rb +134 -0
  44. data/lib/sass/plugin/generic.rb +15 -0
  45. data/lib/sass/plugin/merb.rb +48 -0
  46. data/lib/sass/plugin/rack.rb +60 -0
  47. data/lib/sass/plugin/rails.rb +47 -0
  48. data/lib/sass/plugin/staleness_checker.rb +199 -0
  49. data/lib/sass/plugin.rb +134 -0
  50. data/lib/sass/railtie.rb +10 -0
  51. data/lib/sass/repl.rb +57 -0
  52. data/lib/sass/root.rb +7 -0
  53. data/lib/sass/script/css_lexer.rb +33 -0
  54. data/lib/sass/script/css_parser.rb +36 -0
  55. data/lib/sass/script/functions.rb +3103 -0
  56. data/lib/sass/script/lexer.rb +518 -0
  57. data/lib/sass/script/parser.rb +1164 -0
  58. data/lib/sass/script/tree/funcall.rb +314 -0
  59. data/lib/sass/script/tree/interpolation.rb +220 -0
  60. data/lib/sass/script/tree/list_literal.rb +119 -0
  61. data/lib/sass/script/tree/literal.rb +49 -0
  62. data/lib/sass/script/tree/map_literal.rb +64 -0
  63. data/lib/sass/script/tree/node.rb +119 -0
  64. data/lib/sass/script/tree/operation.rb +149 -0
  65. data/lib/sass/script/tree/selector.rb +26 -0
  66. data/lib/sass/script/tree/string_interpolation.rb +125 -0
  67. data/lib/sass/script/tree/unary_operation.rb +69 -0
  68. data/lib/sass/script/tree/variable.rb +57 -0
  69. data/lib/sass/script/tree.rb +16 -0
  70. data/lib/sass/script/value/arg_list.rb +36 -0
  71. data/lib/sass/script/value/base.rb +258 -0
  72. data/lib/sass/script/value/bool.rb +35 -0
  73. data/lib/sass/script/value/callable.rb +25 -0
  74. data/lib/sass/script/value/color.rb +704 -0
  75. data/lib/sass/script/value/function.rb +19 -0
  76. data/lib/sass/script/value/helpers.rb +298 -0
  77. data/lib/sass/script/value/list.rb +135 -0
  78. data/lib/sass/script/value/map.rb +70 -0
  79. data/lib/sass/script/value/null.rb +44 -0
  80. data/lib/sass/script/value/number.rb +564 -0
  81. data/lib/sass/script/value/string.rb +138 -0
  82. data/lib/sass/script/value.rb +13 -0
  83. data/lib/sass/script.rb +66 -0
  84. data/lib/sass/scss/css_parser.rb +61 -0
  85. data/lib/sass/scss/parser.rb +1343 -0
  86. data/lib/sass/scss/rx.rb +134 -0
  87. data/lib/sass/scss/static_parser.rb +351 -0
  88. data/lib/sass/scss.rb +14 -0
  89. data/lib/sass/selector/abstract_sequence.rb +112 -0
  90. data/lib/sass/selector/comma_sequence.rb +195 -0
  91. data/lib/sass/selector/pseudo.rb +291 -0
  92. data/lib/sass/selector/sequence.rb +661 -0
  93. data/lib/sass/selector/simple.rb +124 -0
  94. data/lib/sass/selector/simple_sequence.rb +348 -0
  95. data/lib/sass/selector.rb +327 -0
  96. data/lib/sass/shared.rb +76 -0
  97. data/lib/sass/source/map.rb +209 -0
  98. data/lib/sass/source/position.rb +39 -0
  99. data/lib/sass/source/range.rb +41 -0
  100. data/lib/sass/stack.rb +140 -0
  101. data/lib/sass/supports.rb +225 -0
  102. data/lib/sass/tree/at_root_node.rb +83 -0
  103. data/lib/sass/tree/charset_node.rb +22 -0
  104. data/lib/sass/tree/comment_node.rb +82 -0
  105. data/lib/sass/tree/content_node.rb +9 -0
  106. data/lib/sass/tree/css_import_node.rb +68 -0
  107. data/lib/sass/tree/debug_node.rb +18 -0
  108. data/lib/sass/tree/directive_node.rb +59 -0
  109. data/lib/sass/tree/each_node.rb +24 -0
  110. data/lib/sass/tree/error_node.rb +18 -0
  111. data/lib/sass/tree/extend_node.rb +43 -0
  112. data/lib/sass/tree/for_node.rb +36 -0
  113. data/lib/sass/tree/function_node.rb +44 -0
  114. data/lib/sass/tree/if_node.rb +52 -0
  115. data/lib/sass/tree/import_node.rb +75 -0
  116. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  117. data/lib/sass/tree/media_node.rb +48 -0
  118. data/lib/sass/tree/mixin_def_node.rb +38 -0
  119. data/lib/sass/tree/mixin_node.rb +52 -0
  120. data/lib/sass/tree/node.rb +240 -0
  121. data/lib/sass/tree/prop_node.rb +162 -0
  122. data/lib/sass/tree/return_node.rb +19 -0
  123. data/lib/sass/tree/root_node.rb +44 -0
  124. data/lib/sass/tree/rule_node.rb +153 -0
  125. data/lib/sass/tree/supports_node.rb +38 -0
  126. data/lib/sass/tree/trace_node.rb +33 -0
  127. data/lib/sass/tree/variable_node.rb +36 -0
  128. data/lib/sass/tree/visitors/base.rb +72 -0
  129. data/lib/sass/tree/visitors/check_nesting.rb +173 -0
  130. data/lib/sass/tree/visitors/convert.rb +350 -0
  131. data/lib/sass/tree/visitors/cssize.rb +362 -0
  132. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  133. data/lib/sass/tree/visitors/extend.rb +64 -0
  134. data/lib/sass/tree/visitors/perform.rb +572 -0
  135. data/lib/sass/tree/visitors/set_options.rb +139 -0
  136. data/lib/sass/tree/visitors/to_css.rb +440 -0
  137. data/lib/sass/tree/warn_node.rb +18 -0
  138. data/lib/sass/tree/while_node.rb +18 -0
  139. data/lib/sass/util/multibyte_string_scanner.rb +151 -0
  140. data/lib/sass/util/normalized_map.rb +122 -0
  141. data/lib/sass/util/subset_map.rb +109 -0
  142. data/lib/sass/util/test.rb +9 -0
  143. data/lib/sass/util.rb +1137 -0
  144. data/lib/sass/version.rb +120 -0
  145. data/lib/sass.rb +102 -0
  146. data/rails/init.rb +1 -0
  147. metadata +283 -0
data/lib/sass/util.rb ADDED
@@ -0,0 +1,1137 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'erb'
3
+ require 'set'
4
+ require 'enumerator'
5
+ require 'stringio'
6
+ require 'rbconfig'
7
+ require 'uri'
8
+ require 'thread'
9
+ require 'pathname'
10
+
11
+ require 'sass/root'
12
+ require 'sass/util/subset_map'
13
+
14
+ module Sass
15
+ # A module containing various useful functions.
16
+ module Util
17
+ extend self
18
+
19
+ # An array of ints representing the Ruby version number.
20
+ # @api public
21
+ RUBY_VERSION_COMPONENTS = RUBY_VERSION.split(".").map {|s| s.to_i}
22
+
23
+ # The Ruby engine we're running under. Defaults to `"ruby"`
24
+ # if the top-level constant is undefined.
25
+ # @api public
26
+ RUBY_ENGINE = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "ruby"
27
+
28
+ # Returns the path of a file relative to the Sass root directory.
29
+ #
30
+ # @param file [String] The filename relative to the Sass root
31
+ # @return [String] The filename relative to the the working directory
32
+ def scope(file)
33
+ File.join(Sass::ROOT_DIR, file)
34
+ end
35
+
36
+ # Maps the keys in a hash according to a block.
37
+ #
38
+ # @example
39
+ # map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
40
+ # #=> {"foo" => "bar", "baz" => "bang"}
41
+ # @param hash [Hash] The hash to map
42
+ # @yield [key] A block in which the keys are transformed
43
+ # @yieldparam key [Object] The key that should be mapped
44
+ # @yieldreturn [Object] The new value for the key
45
+ # @return [Hash] The mapped hash
46
+ # @see #map_vals
47
+ # @see #map_hash
48
+ def map_keys(hash)
49
+ map_hash(hash) {|k, v| [yield(k), v]}
50
+ end
51
+
52
+ # Maps the values in a hash according to a block.
53
+ #
54
+ # @example
55
+ # map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
56
+ # #=> {:foo => :bar, :baz => :bang}
57
+ # @param hash [Hash] The hash to map
58
+ # @yield [value] A block in which the values are transformed
59
+ # @yieldparam value [Object] The value that should be mapped
60
+ # @yieldreturn [Object] The new value for the value
61
+ # @return [Hash] The mapped hash
62
+ # @see #map_keys
63
+ # @see #map_hash
64
+ def map_vals(hash)
65
+ # We don't delegate to map_hash for performance here
66
+ # because map_hash does more than is necessary.
67
+ rv = hash.class.new
68
+ hash = hash.as_stored if hash.is_a?(NormalizedMap)
69
+ hash.each do |k, v|
70
+ rv[k] = yield(v)
71
+ end
72
+ rv
73
+ end
74
+
75
+ # Maps the key-value pairs of a hash according to a block.
76
+ #
77
+ # @example
78
+ # map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
79
+ # #=> {"foo" => :bar, "baz" => :bang}
80
+ # @param hash [Hash] The hash to map
81
+ # @yield [key, value] A block in which the key-value pairs are transformed
82
+ # @yieldparam [key] The hash key
83
+ # @yieldparam [value] The hash value
84
+ # @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
85
+ # @return [Hash] The mapped hash
86
+ # @see #map_keys
87
+ # @see #map_vals
88
+ def map_hash(hash)
89
+ # Copy and modify is more performant than mapping to an array and using
90
+ # to_hash on the result.
91
+ rv = hash.class.new
92
+ hash.each do |k, v|
93
+ new_key, new_value = yield(k, v)
94
+ new_key = hash.denormalize(new_key) if hash.is_a?(NormalizedMap) && new_key == k
95
+ rv[new_key] = new_value
96
+ end
97
+ rv
98
+ end
99
+
100
+ # Computes the powerset of the given array.
101
+ # This is the set of all subsets of the array.
102
+ #
103
+ # @example
104
+ # powerset([1, 2, 3]) #=>
105
+ # Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
106
+ # @param arr [Enumerable]
107
+ # @return [Set<Set>] The subsets of `arr`
108
+ def powerset(arr)
109
+ arr.inject([Set.new].to_set) do |powerset, el|
110
+ new_powerset = Set.new
111
+ powerset.each do |subset|
112
+ new_powerset << subset
113
+ new_powerset << subset + [el]
114
+ end
115
+ new_powerset
116
+ end
117
+ end
118
+
119
+ # Restricts a number to falling within a given range.
120
+ # Returns the number if it falls within the range,
121
+ # or the closest value in the range if it doesn't.
122
+ #
123
+ # @param value [Numeric]
124
+ # @param range [Range<Numeric>]
125
+ # @return [Numeric]
126
+ def restrict(value, range)
127
+ [[value, range.first].max, range.last].min
128
+ end
129
+
130
+ # Like [Fixnum.round], but leaves rooms for slight floating-point
131
+ # differences.
132
+ #
133
+ # @param value [Numeric]
134
+ # @return [Numeric]
135
+ def round(value)
136
+ # If the number is within epsilon of X.5, round up (or down for negative
137
+ # numbers).
138
+ mod = value % 1
139
+ mod_is_half = (mod - 0.5).abs < Script::Value::Number.epsilon
140
+ if value > 0
141
+ !mod_is_half && mod < 0.5 ? value.floor : value.ceil
142
+ else
143
+ mod_is_half || mod < 0.5 ? value.floor : value.ceil
144
+ end
145
+ end
146
+
147
+ # Concatenates all strings that are adjacent in an array,
148
+ # while leaving other elements as they are.
149
+ #
150
+ # @example
151
+ # merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
152
+ # #=> [1, "foobar", 2, "baz"]
153
+ # @param arr [Array]
154
+ # @return [Array] The enumerable with strings merged
155
+ def merge_adjacent_strings(arr)
156
+ # Optimize for the common case of one element
157
+ return arr if arr.size < 2
158
+ arr.inject([]) do |a, e|
159
+ if e.is_a?(String)
160
+ if a.last.is_a?(String)
161
+ a.last << e
162
+ else
163
+ a << e.dup
164
+ end
165
+ else
166
+ a << e
167
+ end
168
+ a
169
+ end
170
+ end
171
+
172
+ # Non-destructively replaces all occurrences of a subsequence in an array
173
+ # with another subsequence.
174
+ #
175
+ # @example
176
+ # replace_subseq([1, 2, 3, 4, 5], [2, 3], [:a, :b])
177
+ # #=> [1, :a, :b, 4, 5]
178
+ #
179
+ # @param arr [Array] The array whose subsequences will be replaced.
180
+ # @param subseq [Array] The subsequence to find and replace.
181
+ # @param replacement [Array] The sequence that `subseq` will be replaced with.
182
+ # @return [Array] `arr` with `subseq` replaced with `replacement`.
183
+ def replace_subseq(arr, subseq, replacement)
184
+ new = []
185
+ matched = []
186
+ i = 0
187
+ arr.each do |elem|
188
+ if elem != subseq[i]
189
+ new.push(*matched)
190
+ matched = []
191
+ i = 0
192
+ new << elem
193
+ next
194
+ end
195
+
196
+ if i == subseq.length - 1
197
+ matched = []
198
+ i = 0
199
+ new.push(*replacement)
200
+ else
201
+ matched << elem
202
+ i += 1
203
+ end
204
+ end
205
+ new.push(*matched)
206
+ new
207
+ end
208
+
209
+ # Intersperses a value in an enumerable, as would be done with `Array#join`
210
+ # but without concatenating the array together afterwards.
211
+ #
212
+ # @param enum [Enumerable]
213
+ # @param val
214
+ # @return [Array]
215
+ def intersperse(enum, val)
216
+ enum.inject([]) {|a, e| a << e << val}[0...-1]
217
+ end
218
+
219
+ def slice_by(enum)
220
+ results = []
221
+ enum.each do |value|
222
+ key = yield(value)
223
+ if !results.empty? && results.last.first == key
224
+ results.last.last << value
225
+ else
226
+ results << [key, [value]]
227
+ end
228
+ end
229
+ results
230
+ end
231
+
232
+ # Substitutes a sub-array of one array with another sub-array.
233
+ #
234
+ # @param ary [Array] The array in which to make the substitution
235
+ # @param from [Array] The sequence of elements to replace with `to`
236
+ # @param to [Array] The sequence of elements to replace `from` with
237
+ def substitute(ary, from, to)
238
+ res = ary.dup
239
+ i = 0
240
+ while i < res.size
241
+ if res[i...i + from.size] == from
242
+ res[i...i + from.size] = to
243
+ end
244
+ i += 1
245
+ end
246
+ res
247
+ end
248
+
249
+ # Destructively strips whitespace from the beginning and end of the first
250
+ # and last elements, respectively, in the array (if those elements are
251
+ # strings). Preserves CSS escapes at the end of the array.
252
+ #
253
+ # @param arr [Array]
254
+ # @return [Array] `arr`
255
+ def strip_string_array(arr)
256
+ arr.first.lstrip! if arr.first.is_a?(String)
257
+ arr[-1] = Sass::Util.rstrip_except_escapes(arr[-1]) if arr.last.is_a?(String)
258
+ arr
259
+ end
260
+
261
+ # Normalizes identifier escapes.
262
+ #
263
+ # See https://github.com/sass/language/blob/master/accepted/identifier-escapes.md.
264
+ #
265
+ # @param ident [String]
266
+ # @return [String]
267
+ def normalize_ident_escapes(ident, start: true)
268
+ ident.gsub(/(^)?(#{Sass::SCSS::RX::ESCAPE})/) do |s|
269
+ at_start = start && $1
270
+ char = escaped_char(s)
271
+ next char if char =~ (at_start ? Sass::SCSS::RX::NMSTART : Sass::SCSS::RX::NMCHAR)
272
+ if char =~ (at_start ? /[\x0-\x1F\x7F0-9]/ : /[\x0-\x1F\x7F]/)
273
+ "\\#{char.ord.to_s(16)} "
274
+ else
275
+ "\\#{char}"
276
+ end
277
+ end
278
+ end
279
+
280
+ # Returns the character encoded by the given escape sequence.
281
+ #
282
+ # @param escape [String]
283
+ # @return [String]
284
+ def escaped_char(escape)
285
+ if escape =~ /^\\([0-9a-fA-F]{1,6})[ \t\r\n\f]?/
286
+ $1.to_i(16).chr(Encoding::UTF_8)
287
+ else
288
+ escape[1]
289
+ end
290
+ end
291
+
292
+ # Like [String#strip], but preserves escaped whitespace at the end of the
293
+ # string.
294
+ #
295
+ # @param string [String]
296
+ # @return [String]
297
+ def strip_except_escapes(string)
298
+ rstrip_except_escapes(string.lstrip)
299
+ end
300
+
301
+ # Like [String#rstrip], but preserves escaped whitespace at the end of the
302
+ # string.
303
+ #
304
+ # @param string [String]
305
+ # @return [String]
306
+ def rstrip_except_escapes(string)
307
+ string.sub(/(?<!\\)\s+$/, '')
308
+ end
309
+
310
+ # Return an array of all possible paths through the given arrays.
311
+ #
312
+ # @param arrs [Array<Array>]
313
+ # @return [Array<Arrays>]
314
+ #
315
+ # @example
316
+ # paths([[1, 2], [3, 4], [5]]) #=>
317
+ # # [[1, 3, 5],
318
+ # # [2, 3, 5],
319
+ # # [1, 4, 5],
320
+ # # [2, 4, 5]]
321
+ def paths(arrs)
322
+ arrs.inject([[]]) do |paths, arr|
323
+ arr.map {|e| paths.map {|path| path + [e]}}.flatten(1)
324
+ end
325
+ end
326
+
327
+ # Computes a single longest common subsequence for `x` and `y`.
328
+ # If there are more than one longest common subsequences,
329
+ # the one returned is that which starts first in `x`.
330
+ #
331
+ # @param x [Array]
332
+ # @param y [Array]
333
+ # @yield [a, b] An optional block to use in place of a check for equality
334
+ # between elements of `x` and `y`.
335
+ # @yieldreturn [Object, nil] If the two values register as equal,
336
+ # this will return the value to use in the LCS array.
337
+ # @return [Array] The LCS
338
+ def lcs(x, y, &block)
339
+ x = [nil, *x]
340
+ y = [nil, *y]
341
+ block ||= proc {|a, b| a == b && a}
342
+ lcs_backtrace(lcs_table(x, y, &block), x, y, x.size - 1, y.size - 1, &block)
343
+ end
344
+
345
+ # Like `String.upcase`, but only ever upcases ASCII letters.
346
+ def upcase(string)
347
+ return string.upcase unless ruby2_4?
348
+ string.upcase(:ascii)
349
+ end
350
+
351
+ # Like `String.downcase`, but only ever downcases ASCII letters.
352
+ def downcase(string)
353
+ return string.downcase unless ruby2_4?
354
+ string.downcase(:ascii)
355
+ end
356
+
357
+ # Returns a sub-array of `minuend` containing only elements that are also in
358
+ # `subtrahend`. Ensures that the return value has the same order as
359
+ # `minuend`, even on Rubinius where that's not guaranteed by `Array#-`.
360
+ #
361
+ # @param minuend [Array]
362
+ # @param subtrahend [Array]
363
+ # @return [Array]
364
+ def array_minus(minuend, subtrahend)
365
+ return minuend - subtrahend unless rbx?
366
+ set = Set.new(minuend) - subtrahend
367
+ minuend.select {|e| set.include?(e)}
368
+ end
369
+
370
+ # Returns the maximum of `val1` and `val2`. We use this over \{Array.max} to
371
+ # avoid unnecessary garbage collection.
372
+ def max(val1, val2)
373
+ val1 > val2 ? val1 : val2
374
+ end
375
+
376
+ # Returns the minimum of `val1` and `val2`. We use this over \{Array.min} to
377
+ # avoid unnecessary garbage collection.
378
+ def min(val1, val2)
379
+ val1 <= val2 ? val1 : val2
380
+ end
381
+
382
+ # Returns a string description of the character that caused an
383
+ # `Encoding::UndefinedConversionError`.
384
+ #
385
+ # @param e [Encoding::UndefinedConversionError]
386
+ # @return [String]
387
+ def undefined_conversion_error_char(e)
388
+ # Rubinius (as of 2.0.0.rc1) pre-quotes the error character.
389
+ return e.error_char if rbx?
390
+ # JRuby (as of 1.7.2) doesn't have an error_char field on
391
+ # Encoding::UndefinedConversionError.
392
+ return e.error_char.dump unless jruby?
393
+ e.message[/^"[^"]+"/] # "
394
+ end
395
+
396
+ # Asserts that `value` falls within `range` (inclusive), leaving
397
+ # room for slight floating-point errors.
398
+ #
399
+ # @param name [String] The name of the value. Used in the error message.
400
+ # @param range [Range] The allowed range of values.
401
+ # @param value [Numeric, Sass::Script::Value::Number] The value to check.
402
+ # @param unit [String] The unit of the value. Used in error reporting.
403
+ # @return [Numeric] `value` adjusted to fall within range, if it
404
+ # was outside by a floating-point margin.
405
+ def check_range(name, range, value, unit = '')
406
+ grace = (-0.00001..0.00001)
407
+ str = value.to_s
408
+ value = value.value if value.is_a?(Sass::Script::Value::Number)
409
+ return value if range.include?(value)
410
+ return range.first if grace.include?(value - range.first)
411
+ return range.last if grace.include?(value - range.last)
412
+ raise ArgumentError.new(
413
+ "#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
414
+ end
415
+
416
+ # Returns whether or not `seq1` is a subsequence of `seq2`. That is, whether
417
+ # or not `seq2` contains every element in `seq1` in the same order (and
418
+ # possibly more elements besides).
419
+ #
420
+ # @param seq1 [Array]
421
+ # @param seq2 [Array]
422
+ # @return [Boolean]
423
+ def subsequence?(seq1, seq2)
424
+ i = j = 0
425
+ loop do
426
+ return true if i == seq1.size
427
+ return false if j == seq2.size
428
+ i += 1 if seq1[i] == seq2[j]
429
+ j += 1
430
+ end
431
+ end
432
+
433
+ # Returns information about the caller of the previous method.
434
+ #
435
+ # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
436
+ # @return [[String, Integer, (String, nil)]]
437
+ # An array containing the filename, line, and method name of the caller.
438
+ # The method name may be nil
439
+ def caller_info(entry = nil)
440
+ # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
441
+ entry ||= caller[1]
442
+ info = entry.scan(/^((?:[A-Za-z]:)?.*?):(-?.*?)(?::.*`(.+)')?$/).first
443
+ info[1] = info[1].to_i
444
+ # This is added by Rubinius to designate a block, but we don't care about it.
445
+ info[2].sub!(/ \{\}\Z/, '') if info[2]
446
+ info
447
+ end
448
+
449
+ # Returns whether one version string represents a more recent version than another.
450
+ #
451
+ # @param v1 [String] A version string.
452
+ # @param v2 [String] Another version string.
453
+ # @return [Boolean]
454
+ def version_gt(v1, v2)
455
+ # Construct an array to make sure the shorter version is padded with nil
456
+ Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
457
+ p1 ||= "0"
458
+ p2 ||= "0"
459
+ release1 = p1 =~ /^[0-9]+$/
460
+ release2 = p2 =~ /^[0-9]+$/
461
+ if release1 && release2
462
+ # Integer comparison if both are full releases
463
+ p1, p2 = p1.to_i, p2.to_i
464
+ next if p1 == p2
465
+ return p1 > p2
466
+ elsif !release1 && !release2
467
+ # String comparison if both are prereleases
468
+ next if p1 == p2
469
+ return p1 > p2
470
+ else
471
+ # If only one is a release, that one is newer
472
+ return release1
473
+ end
474
+ end
475
+ end
476
+
477
+ # Returns whether one version string represents the same or a more
478
+ # recent version than another.
479
+ #
480
+ # @param v1 [String] A version string.
481
+ # @param v2 [String] Another version string.
482
+ # @return [Boolean]
483
+ def version_geq(v1, v2)
484
+ version_gt(v1, v2) || !version_gt(v2, v1)
485
+ end
486
+
487
+ # Throws a NotImplementedError for an abstract method.
488
+ #
489
+ # @param obj [Object] `self`
490
+ # @raise [NotImplementedError]
491
+ def abstract(obj)
492
+ raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
493
+ end
494
+
495
+ # Prints a deprecation warning for the caller method.
496
+ #
497
+ # @param obj [Object] `self`
498
+ # @param message [String] A message describing what to do instead.
499
+ def deprecated(obj, message = nil)
500
+ obj_class = obj.is_a?(Class) ? "#{obj}." : "#{obj.class}#"
501
+ full_message = "DEPRECATION WARNING: #{obj_class}#{caller_info[2]} " +
502
+ "will be removed in a future version of Sass.#{("\n" + message) if message}"
503
+ Sass::Util.sass_warn full_message
504
+ end
505
+
506
+ # Silences all Sass warnings within a block.
507
+ #
508
+ # @yield A block in which no Sass warnings will be printed
509
+ def silence_sass_warnings
510
+ old_level, Sass.logger.log_level = Sass.logger.log_level, :error
511
+ yield
512
+ ensure
513
+ Sass.logger.log_level = old_level
514
+ end
515
+
516
+ # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
517
+ #
518
+ # @param msg [String]
519
+ def sass_warn(msg)
520
+ Sass.logger.warn("#{msg}\n")
521
+ end
522
+
523
+ ## Cross Rails Version Compatibility
524
+
525
+ # Returns the root of the Rails application,
526
+ # if this is running in a Rails context.
527
+ # Returns `nil` if no such root is defined.
528
+ #
529
+ # @return [String, nil]
530
+ def rails_root
531
+ if defined?(::Rails.root)
532
+ return ::Rails.root.to_s if ::Rails.root
533
+ raise "ERROR: Rails.root is nil!"
534
+ end
535
+ return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
536
+ nil
537
+ end
538
+
539
+ # Returns the environment of the Rails application,
540
+ # if this is running in a Rails context.
541
+ # Returns `nil` if no such environment is defined.
542
+ #
543
+ # @return [String, nil]
544
+ def rails_env
545
+ return ::Rails.env.to_s if defined?(::Rails.env)
546
+ return RAILS_ENV.to_s if defined?(RAILS_ENV)
547
+ nil
548
+ end
549
+
550
+ # Returns whether this environment is using ActionPack
551
+ # version 3.0.0 or greater.
552
+ #
553
+ # @return [Boolean]
554
+ def ap_geq_3?
555
+ ap_geq?("3.0.0.beta1")
556
+ end
557
+
558
+ # Returns whether this environment is using ActionPack
559
+ # of a version greater than or equal to that specified.
560
+ #
561
+ # @param version [String] The string version number to check against.
562
+ # Should be greater than or equal to Rails 3,
563
+ # because otherwise ActionPack::VERSION isn't autoloaded
564
+ # @return [Boolean]
565
+ def ap_geq?(version)
566
+ # The ActionPack module is always loaded automatically in Rails >= 3
567
+ return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
568
+ defined?(ActionPack::VERSION::STRING)
569
+
570
+ version_geq(ActionPack::VERSION::STRING, version)
571
+ end
572
+
573
+ # Returns an ActionView::Template* class.
574
+ # In pre-3.0 versions of Rails, most of these classes
575
+ # were of the form `ActionView::TemplateFoo`,
576
+ # while afterwards they were of the form `ActionView;:Template::Foo`.
577
+ #
578
+ # @param name [#to_s] The name of the class to get.
579
+ # For example, `:Error` will return `ActionView::TemplateError`
580
+ # or `ActionView::Template::Error`.
581
+ def av_template_class(name)
582
+ return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
583
+ ActionView::Template.const_get(name.to_s)
584
+ end
585
+
586
+ ## Cross-OS Compatibility
587
+ #
588
+ # These methods are cached because some of them are called quite frequently
589
+ # and even basic checks like String#== are too costly to be called repeatedly.
590
+
591
+ # Whether or not this is running on Windows.
592
+ #
593
+ # @return [Boolean]
594
+ def windows?
595
+ return @windows if defined?(@windows)
596
+ @windows = (RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i)
597
+ end
598
+
599
+ # Whether or not this is running on IronRuby.
600
+ #
601
+ # @return [Boolean]
602
+ def ironruby?
603
+ return @ironruby if defined?(@ironruby)
604
+ @ironruby = RUBY_ENGINE == "ironruby"
605
+ end
606
+
607
+ # Whether or not this is running on Rubinius.
608
+ #
609
+ # @return [Boolean]
610
+ def rbx?
611
+ return @rbx if defined?(@rbx)
612
+ @rbx = RUBY_ENGINE == "rbx"
613
+ end
614
+
615
+ # Whether or not this is running on JRuby.
616
+ #
617
+ # @return [Boolean]
618
+ def jruby?
619
+ return @jruby if defined?(@jruby)
620
+ @jruby = RUBY_PLATFORM =~ /java/
621
+ end
622
+
623
+ # Returns an array of ints representing the JRuby version number.
624
+ #
625
+ # @return [Array<Integer>]
626
+ def jruby_version
627
+ @jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
628
+ end
629
+
630
+ # Like `Dir.glob`, but works with backslash-separated paths on Windows.
631
+ #
632
+ # @param path [String]
633
+ def glob(path)
634
+ path = path.tr('\\', '/') if windows?
635
+ if block_given?
636
+ Dir.glob(path) {|f| yield(f)}
637
+ else
638
+ Dir.glob(path)
639
+ end
640
+ end
641
+
642
+ # Like `Pathname.new`, but normalizes Windows paths to always use backslash
643
+ # separators.
644
+ #
645
+ # `Pathname#relative_path_from` can break if the two pathnames aren't
646
+ # consistent in their slash style.
647
+ #
648
+ # @param path [String]
649
+ # @return [Pathname]
650
+ def pathname(path)
651
+ path = path.tr("/", "\\") if windows?
652
+ Pathname.new(path)
653
+ end
654
+
655
+ # Like `Pathname#cleanpath`, but normalizes Windows paths to always use
656
+ # backslash separators. Normally, `Pathname#cleanpath` actually does the
657
+ # reverse -- it will convert backslashes to forward slashes, which can break
658
+ # `Pathname#relative_path_from`.
659
+ #
660
+ # @param path [String, Pathname]
661
+ # @return [Pathname]
662
+ def cleanpath(path)
663
+ path = Pathname.new(path) unless path.is_a?(Pathname)
664
+ pathname(path.cleanpath.to_s)
665
+ end
666
+
667
+ # Returns `path` with all symlinks resolved.
668
+ #
669
+ # @param path [String, Pathname]
670
+ # @return [Pathname]
671
+ def realpath(path)
672
+ path = Pathname.new(path) unless path.is_a?(Pathname)
673
+
674
+ # Explicitly DON'T run #pathname here. We don't want to convert
675
+ # to Windows directory separators because we're comparing these
676
+ # against the paths returned by Listen, which use forward
677
+ # slashes everywhere.
678
+ begin
679
+ path.realpath
680
+ rescue SystemCallError
681
+ # If [path] doesn't actually exist, don't bail, just
682
+ # return the original.
683
+ path
684
+ end
685
+ end
686
+
687
+ # Returns `path` relative to `from`.
688
+ #
689
+ # This is like `Pathname#relative_path_from` except it accepts both strings
690
+ # and pathnames, it handles Windows path separators correctly, and it throws
691
+ # an error rather than crashing if the paths use different encodings
692
+ # (https://github.com/ruby/ruby/pull/713).
693
+ #
694
+ # @param path [String, Pathname]
695
+ # @param from [String, Pathname]
696
+ # @return [Pathname?]
697
+ def relative_path_from(path, from)
698
+ pathname(path.to_s).relative_path_from(pathname(from.to_s))
699
+ rescue NoMethodError => e
700
+ raise e unless e.name == :zero?
701
+
702
+ # Work around https://github.com/ruby/ruby/pull/713.
703
+ path = path.to_s
704
+ from = from.to_s
705
+ raise ArgumentError("Incompatible path encodings: #{path.inspect} is #{path.encoding}, " +
706
+ "#{from.inspect} is #{from.encoding}")
707
+ end
708
+
709
+ # Converts `path` to a "file:" URI. This handles Windows paths correctly.
710
+ #
711
+ # @param path [String, Pathname]
712
+ # @return [String]
713
+ def file_uri_from_path(path)
714
+ path = path.to_s if path.is_a?(Pathname)
715
+ path = path.tr('\\', '/') if windows?
716
+ path = URI::DEFAULT_PARSER.escape(path)
717
+ return path.start_with?('/') ? "file://" + path : path unless windows?
718
+ return "file:///" + path.tr("\\", "/") if path =~ %r{^[a-zA-Z]:[/\\]}
719
+ return "file:" + path.tr("\\", "/") if path =~ %r{\\\\[^\\]+\\[^\\/]+}
720
+ path.tr("\\", "/")
721
+ end
722
+
723
+ # Retries a filesystem operation if it fails on Windows. Windows
724
+ # has weird and flaky locking rules that can cause operations to fail.
725
+ #
726
+ # @yield [] The filesystem operation.
727
+ def retry_on_windows
728
+ return yield unless windows?
729
+
730
+ begin
731
+ yield
732
+ rescue SystemCallError
733
+ sleep 0.1
734
+ yield
735
+ end
736
+ end
737
+
738
+ # Prepare a value for a destructuring assignment (e.g. `a, b =
739
+ # val`). This works around a performance bug when using
740
+ # ActiveSupport, and only needs to be called when `val` is likely
741
+ # to be `nil` reasonably often.
742
+ #
743
+ # See [this bug report](http://redmine.ruby-lang.org/issues/4917).
744
+ #
745
+ # @param val [Object]
746
+ # @return [Object]
747
+ def destructure(val)
748
+ val || []
749
+ end
750
+
751
+ CHARSET_REGEXP = /\A@charset "([^"]+)"/
752
+ bom = "\uFEFF"
753
+ UTF_8_BOM = bom.encode("UTF-8").force_encoding('BINARY')
754
+ UTF_16BE_BOM = bom.encode("UTF-16BE").force_encoding('BINARY')
755
+ UTF_16LE_BOM = bom.encode("UTF-16LE").force_encoding('BINARY')
756
+
757
+ ## Cross-Ruby-Version Compatibility
758
+
759
+ # Whether or not this is running under Ruby 2.4 or higher.
760
+ #
761
+ # @return [Boolean]
762
+ def ruby2_4?
763
+ return @ruby2_4 if defined?(@ruby2_4)
764
+ @ruby2_4 =
765
+ if RUBY_VERSION_COMPONENTS[0] == 2
766
+ RUBY_VERSION_COMPONENTS[1] >= 4
767
+ else
768
+ RUBY_VERSION_COMPONENTS[0] > 2
769
+ end
770
+ end
771
+
772
+ # Like {\#check\_encoding}, but also checks for a `@charset` declaration
773
+ # at the beginning of the file and uses that encoding if it exists.
774
+ #
775
+ # Sass follows CSS's decoding rules.
776
+ #
777
+ # @param str [String] The string of which to check the encoding
778
+ # @return [(String, Encoding)] The original string encoded as UTF-8,
779
+ # and the source encoding of the string
780
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
781
+ # cannot be converted to UTF-8
782
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
783
+ # @raise [Sass::SyntaxError] If the document declares an encoding that
784
+ # doesn't match its contents, or it doesn't declare an encoding and its
785
+ # contents are invalid in the native encoding.
786
+ def check_sass_encoding(str)
787
+ # Determine the fallback encoding following section 3.2 of CSS Syntax Level 3 and Encodings:
788
+ # http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#determine-the-fallback-encoding
789
+ # http://encoding.spec.whatwg.org/#decode
790
+ binary = str.dup.force_encoding("BINARY")
791
+ if binary.start_with?(UTF_8_BOM)
792
+ binary.slice! 0, UTF_8_BOM.length
793
+ str = binary.force_encoding('UTF-8')
794
+ elsif binary.start_with?(UTF_16BE_BOM)
795
+ binary.slice! 0, UTF_16BE_BOM.length
796
+ str = binary.force_encoding('UTF-16BE')
797
+ elsif binary.start_with?(UTF_16LE_BOM)
798
+ binary.slice! 0, UTF_16LE_BOM.length
799
+ str = binary.force_encoding('UTF-16LE')
800
+ elsif binary =~ CHARSET_REGEXP
801
+ charset = $1.force_encoding('US-ASCII')
802
+ encoding = Encoding.find(charset)
803
+ if encoding.name == 'UTF-16' || encoding.name == 'UTF-16BE'
804
+ encoding = Encoding.find('UTF-8')
805
+ end
806
+ str = binary.force_encoding(encoding)
807
+ elsif str.encoding.name == "ASCII-8BIT"
808
+ # Normally we want to fall back on believing the Ruby string
809
+ # encoding, but if that's just binary we want to make sure
810
+ # it's valid UTF-8.
811
+ str = str.force_encoding('utf-8')
812
+ end
813
+
814
+ find_encoding_error(str) unless str.valid_encoding?
815
+
816
+ begin
817
+ # If the string is valid, preprocess it according to section 3.3 of CSS Syntax Level 3.
818
+ return str.encode("UTF-8").gsub(/\r\n?|\f/, "\n").tr("\u0000", "�"), str.encoding
819
+ rescue EncodingError
820
+ find_encoding_error(str)
821
+ end
822
+ end
823
+
824
+ # Destructively removes all elements from an array that match a block, and
825
+ # returns the removed elements.
826
+ #
827
+ # @param array [Array] The array from which to remove elements.
828
+ # @yield [el] Called for each element.
829
+ # @yieldparam el [*] The element to test.
830
+ # @yieldreturn [Boolean] Whether or not to extract the element.
831
+ # @return [Array] The extracted elements.
832
+ def extract!(array)
833
+ out = []
834
+ array.reject! do |e|
835
+ next false unless yield e
836
+ out << e
837
+ true
838
+ end
839
+ out
840
+ end
841
+
842
+ # Flattens the first level of nested arrays in `arrs`. Unlike
843
+ # `Array#flatten`, this orders the result by taking the first
844
+ # values from each array in order, then the second, and so on.
845
+ #
846
+ # @param arrs [Array] The array to flatten.
847
+ # @return [Array] The flattened array.
848
+ def flatten_vertically(arrs)
849
+ result = []
850
+ arrs = arrs.map {|sub| sub.is_a?(Array) ? sub.dup : Array(sub)}
851
+ until arrs.empty?
852
+ arrs.reject! do |arr|
853
+ result << arr.shift
854
+ arr.empty?
855
+ end
856
+ end
857
+ result
858
+ end
859
+
860
+ # Like `Object#inspect`, but preserves non-ASCII characters rather than
861
+ # escaping them under Ruby 1.9.2. This is necessary so that the
862
+ # precompiled Haml template can be `#encode`d into `@options[:encoding]`
863
+ # before being evaluated.
864
+ #
865
+ # @param obj {Object}
866
+ # @return {String}
867
+ def inspect_obj(obj)
868
+ return obj.inspect unless version_geq(RUBY_VERSION, "1.9.2")
869
+ return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
870
+ return obj.inspect unless obj.is_a?(String)
871
+ '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
872
+ end
873
+
874
+ # Extracts the non-string vlaues from an array containing both strings and non-strings.
875
+ # These values are replaced with escape sequences.
876
+ # This can be undone using \{#inject\_values}.
877
+ #
878
+ # This is useful e.g. when we want to do string manipulation
879
+ # on an interpolated string.
880
+ #
881
+ # The precise format of the resulting string is not guaranteed.
882
+ # However, it is guaranteed that newlines and whitespace won't be affected.
883
+ #
884
+ # @param arr [Array] The array from which values are extracted.
885
+ # @return [(String, Array)] The resulting string, and an array of extracted values.
886
+ def extract_values(arr)
887
+ values = []
888
+ mapped = arr.map do |e|
889
+ next e.gsub('{', '{{') if e.is_a?(String)
890
+ values << e
891
+ next "{#{values.count - 1}}"
892
+ end
893
+ return mapped.join, values
894
+ end
895
+
896
+ # Undoes \{#extract\_values} by transforming a string with escape sequences
897
+ # into an array of strings and non-string values.
898
+ #
899
+ # @param str [String] The string with escape sequences.
900
+ # @param values [Array] The array of values to inject.
901
+ # @return [Array] The array of strings and values.
902
+ def inject_values(str, values)
903
+ return [str.gsub('{{', '{')] if values.empty?
904
+ # Add an extra { so that we process the tail end of the string
905
+ result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
906
+ [pre, esc ? '{' : '', n ? values[n.to_i] : '']
907
+ end.flatten(1)
908
+ result[-2] = '' # Get rid of the extra {
909
+ merge_adjacent_strings(result).reject {|s| s == ''}
910
+ end
911
+
912
+ # Allows modifications to be performed on the string form
913
+ # of an array containing both strings and non-strings.
914
+ #
915
+ # @param arr [Array] The array from which values are extracted.
916
+ # @yield [str] A block in which string manipulation can be done to the array.
917
+ # @yieldparam str [String] The string form of `arr`.
918
+ # @yieldreturn [String] The modified string.
919
+ # @return [Array] The modified, interpolated array.
920
+ def with_extracted_values(arr)
921
+ str, vals = extract_values(arr)
922
+ str = yield str
923
+ inject_values(str, vals)
924
+ end
925
+
926
+ # Builds a sourcemap file name given the generated CSS file name.
927
+ #
928
+ # @param css [String] The generated CSS file name.
929
+ # @return [String] The source map file name.
930
+ def sourcemap_name(css)
931
+ css + ".map"
932
+ end
933
+
934
+ # Escapes certain characters so that the result can be used
935
+ # as the JSON string value. Returns the original string if
936
+ # no escaping is necessary.
937
+ #
938
+ # @param s [String] The string to be escaped
939
+ # @return [String] The escaped string
940
+ def json_escape_string(s)
941
+ return s if s !~ /["\\\b\f\n\r\t]/
942
+
943
+ result = ""
944
+ s.split("").each do |c|
945
+ case c
946
+ when '"', "\\"
947
+ result << "\\" << c
948
+ when "\n" then result << "\\n"
949
+ when "\t" then result << "\\t"
950
+ when "\r" then result << "\\r"
951
+ when "\f" then result << "\\f"
952
+ when "\b" then result << "\\b"
953
+ else
954
+ result << c
955
+ end
956
+ end
957
+ result
958
+ end
959
+
960
+ # Converts the argument into a valid JSON value.
961
+ #
962
+ # @param v [Integer, String, Array, Boolean, nil]
963
+ # @return [String]
964
+ def json_value_of(v)
965
+ case v
966
+ when Integer
967
+ v.to_s
968
+ when String
969
+ "\"" + json_escape_string(v) + "\""
970
+ when Array
971
+ "[" + v.map {|x| json_value_of(x)}.join(",") + "]"
972
+ when NilClass
973
+ "null"
974
+ when TrueClass
975
+ "true"
976
+ when FalseClass
977
+ "false"
978
+ else
979
+ raise ArgumentError.new("Unknown type: #{v.class.name}")
980
+ end
981
+ end
982
+
983
+ VLQ_BASE_SHIFT = 5
984
+ VLQ_BASE = 1 << VLQ_BASE_SHIFT
985
+ VLQ_BASE_MASK = VLQ_BASE - 1
986
+ VLQ_CONTINUATION_BIT = VLQ_BASE
987
+
988
+ BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
989
+ BASE64_DIGIT_MAP = begin
990
+ map = {}
991
+ BASE64_DIGITS.each_with_index.map do |digit, i|
992
+ map[digit] = i
993
+ end
994
+ map
995
+ end
996
+
997
+ # Encodes `value` as VLQ (http://en.wikipedia.org/wiki/VLQ).
998
+ #
999
+ # @param value [Integer]
1000
+ # @return [String] The encoded value
1001
+ def encode_vlq(value)
1002
+ if value < 0
1003
+ value = ((-value) << 1) | 1
1004
+ else
1005
+ value <<= 1
1006
+ end
1007
+
1008
+ result = ''
1009
+ begin
1010
+ digit = value & VLQ_BASE_MASK
1011
+ value >>= VLQ_BASE_SHIFT
1012
+ if value > 0
1013
+ digit |= VLQ_CONTINUATION_BIT
1014
+ end
1015
+ result << BASE64_DIGITS[digit]
1016
+ end while value > 0
1017
+ result
1018
+ end
1019
+
1020
+ ## Static Method Stuff
1021
+
1022
+ # The context in which the ERB for \{#def\_static\_method} will be run.
1023
+ class StaticConditionalContext
1024
+ # @param set [#include?] The set of variables that are defined for this context.
1025
+ def initialize(set)
1026
+ @set = set
1027
+ end
1028
+
1029
+ # Checks whether or not a variable is defined for this context.
1030
+ #
1031
+ # @param name [Symbol] The name of the variable
1032
+ # @return [Boolean]
1033
+ def method_missing(name, *args)
1034
+ super unless args.empty? && !block_given?
1035
+ @set.include?(name)
1036
+ end
1037
+ end
1038
+
1039
+ # @private
1040
+ ATOMIC_WRITE_MUTEX = Mutex.new
1041
+
1042
+ # This creates a temp file and yields it for writing. When the
1043
+ # write is complete, the file is moved into the desired location.
1044
+ # The atomicity of this operation is provided by the filesystem's
1045
+ # rename operation.
1046
+ #
1047
+ # @param filename [String] The file to write to.
1048
+ # @param perms [Integer] The permissions used for creating this file.
1049
+ # Will be masked by the process umask. Defaults to readable/writeable
1050
+ # by all users however the umask usually changes this to only be writable
1051
+ # by the process's user.
1052
+ # @yieldparam tmpfile [Tempfile] The temp file that can be written to.
1053
+ # @return The value returned by the block.
1054
+ def atomic_create_and_write_file(filename, perms = 0666)
1055
+ require 'tempfile'
1056
+ tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
1057
+ tmpfile.binmode if tmpfile.respond_to?(:binmode)
1058
+ result = yield tmpfile
1059
+ tmpfile.close
1060
+ ATOMIC_WRITE_MUTEX.synchronize do
1061
+ begin
1062
+ File.chmod(perms & ~File.umask, tmpfile.path)
1063
+ rescue Errno::EPERM
1064
+ # If we don't have permissions to chmod the file, don't let that crash
1065
+ # the compilation. See issue 1215.
1066
+ end
1067
+ File.rename tmpfile.path, filename
1068
+ end
1069
+ result
1070
+ ensure
1071
+ # close and remove the tempfile if it still exists,
1072
+ # presumably due to an error during write
1073
+ tmpfile.close if tmpfile
1074
+ tmpfile.unlink if tmpfile
1075
+ end
1076
+
1077
+ private
1078
+
1079
+ def find_encoding_error(str)
1080
+ encoding = str.encoding
1081
+ cr = Regexp.quote("\r".encode(encoding).force_encoding('BINARY'))
1082
+ lf = Regexp.quote("\n".encode(encoding).force_encoding('BINARY'))
1083
+ ff = Regexp.quote("\f".encode(encoding).force_encoding('BINARY'))
1084
+ line_break = /#{cr}#{lf}?|#{ff}|#{lf}/
1085
+
1086
+ str.force_encoding("binary").split(line_break).each_with_index do |line, i|
1087
+ begin
1088
+ line.encode(encoding)
1089
+ rescue Encoding::UndefinedConversionError => e
1090
+ raise Sass::SyntaxError.new(
1091
+ "Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}",
1092
+ :line => i + 1)
1093
+ end
1094
+ end
1095
+
1096
+ # We shouldn't get here, but it's possible some weird encoding stuff causes it.
1097
+ return str, str.encoding
1098
+ end
1099
+
1100
+ # Calculates the memoization table for the Least Common Subsequence algorithm.
1101
+ # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
1102
+ def lcs_table(x, y)
1103
+ # This method does not take a block as an explicit parameter for performance reasons.
1104
+ c = Array.new(x.size) {[]}
1105
+ x.size.times {|i| c[i][0] = 0}
1106
+ y.size.times {|j| c[0][j] = 0}
1107
+ (1...x.size).each do |i|
1108
+ (1...y.size).each do |j|
1109
+ c[i][j] =
1110
+ if yield x[i], y[j]
1111
+ c[i - 1][j - 1] + 1
1112
+ else
1113
+ [c[i][j - 1], c[i - 1][j]].max
1114
+ end
1115
+ end
1116
+ end
1117
+ c
1118
+ end
1119
+
1120
+ # Computes a single longest common subsequence for arrays x and y.
1121
+ # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
1122
+ def lcs_backtrace(c, x, y, i, j, &block)
1123
+ return [] if i == 0 || j == 0
1124
+ if (v = yield(x[i], y[j]))
1125
+ return lcs_backtrace(c, x, y, i - 1, j - 1, &block) << v
1126
+ end
1127
+
1128
+ return lcs_backtrace(c, x, y, i, j - 1, &block) if c[i][j - 1] > c[i - 1][j]
1129
+ lcs_backtrace(c, x, y, i - 1, j, &block)
1130
+ end
1131
+
1132
+ singleton_methods.each {|method| module_function method}
1133
+ end
1134
+ end
1135
+
1136
+ require 'sass/util/multibyte_string_scanner'
1137
+ require 'sass/util/normalized_map'