oreorenasass 3.4.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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
@@ -0,0 +1,110 @@
1
+ require 'set'
2
+
3
+ module Sass
4
+ module Util
5
+ # A map from sets to values.
6
+ # A value is \{#\[]= set} by providing a set (the "set-set") and a value,
7
+ # which is then recorded as corresponding to that set.
8
+ # Values are \{#\[] accessed} by providing a set (the "get-set")
9
+ # and returning all values that correspond to set-sets
10
+ # that are subsets of the get-set.
11
+ #
12
+ # SubsetMap preserves the order of values as they're inserted.
13
+ #
14
+ # @example
15
+ # ssm = SubsetMap.new
16
+ # ssm[Set[1, 2]] = "Foo"
17
+ # ssm[Set[2, 3]] = "Bar"
18
+ # ssm[Set[1, 2, 3]] = "Baz"
19
+ #
20
+ # ssm[Set[1, 2, 3]] #=> ["Foo", "Bar", "Baz"]
21
+ class SubsetMap
22
+ # Creates a new, empty SubsetMap.
23
+ def initialize
24
+ @hash = {}
25
+ @vals = []
26
+ end
27
+
28
+ # Whether or not this SubsetMap has any key-value pairs.
29
+ #
30
+ # @return [Boolean]
31
+ def empty?
32
+ @hash.empty?
33
+ end
34
+
35
+ # Associates a value with a set.
36
+ # When `set` or any of its supersets is accessed,
37
+ # `value` will be among the values returned.
38
+ #
39
+ # Note that if the same `set` is passed to this method multiple times,
40
+ # all given `value`s will be associated with that `set`.
41
+ #
42
+ # This runs in `O(n)` time, where `n` is the size of `set`.
43
+ #
44
+ # @param set [#to_set] The set to use as the map key. May not be empty.
45
+ # @param value [Object] The value to associate with `set`.
46
+ # @raise [ArgumentError] If `set` is empty.
47
+ def []=(set, value)
48
+ raise ArgumentError.new("SubsetMap keys may not be empty.") if set.empty?
49
+
50
+ index = @vals.size
51
+ @vals << value
52
+ set.each do |k|
53
+ @hash[k] ||= []
54
+ @hash[k] << [set, set.to_set, index]
55
+ end
56
+ end
57
+
58
+ # Returns all values associated with subsets of `set`.
59
+ #
60
+ # In the worst case, this runs in `O(m*max(n, log m))` time,
61
+ # where `n` is the size of `set`
62
+ # and `m` is the number of associations in the map.
63
+ # However, unless many keys in the map overlap with `set`,
64
+ # `m` will typically be much smaller.
65
+ #
66
+ # @param set [Set] The set to use as the map key.
67
+ # @return [Array<(Object, #to_set)>] An array of pairs,
68
+ # where the first value is the value associated with a subset of `set`,
69
+ # and the second value is that subset of `set`
70
+ # (or whatever `#to_set` object was used to set the value)
71
+ # This array is in insertion order.
72
+ # @see #[]
73
+ def get(set)
74
+ res = set.map do |k|
75
+ subsets = @hash[k]
76
+ next unless subsets
77
+ subsets.map do |subenum, subset, index|
78
+ next unless subset.subset?(set)
79
+ [index, subenum]
80
+ end
81
+ end
82
+ res = Sass::Util.flatten(res, 1)
83
+ res.compact!
84
+ res.uniq!
85
+ res.sort!
86
+ res.map! {|i, s| [@vals[i], s]}
87
+ res
88
+ end
89
+
90
+ # Same as \{#get}, but doesn't return the subsets of the argument
91
+ # for which values were found.
92
+ #
93
+ # @param set [Set] The set to use as the map key.
94
+ # @return [Array] The array of all values
95
+ # associated with subsets of `set`, in insertion order.
96
+ # @see #get
97
+ def [](set)
98
+ get(set).map {|v, _| v}
99
+ end
100
+
101
+ # Iterates over each value in the subset map. Ignores keys completely. If
102
+ # multiple keys have the same value, this will return them multiple times.
103
+ #
104
+ # @yield [Object] Each value in the map.
105
+ def each_value
106
+ @vals.each {|v| yield v}
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,9 @@
1
+ module Sass
2
+ module Util
3
+ module Test
4
+ def skip(msg = nil, bt = caller)
5
+ super if defined?(super)
6
+ end
7
+ end
8
+ end
9
+ end
data/lib/sass/util.rb ADDED
@@ -0,0 +1,1318 @@
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
+ # Converts an array of `[key, value]` pairs to a hash.
37
+ #
38
+ # @example
39
+ # to_hash([[:foo, "bar"], [:baz, "bang"]])
40
+ # #=> {:foo => "bar", :baz => "bang"}
41
+ # @param arr [Array<(Object, Object)>] An array of pairs
42
+ # @return [Hash] A hash
43
+ def to_hash(arr)
44
+ ordered_hash(*arr.compact)
45
+ end
46
+
47
+ # Maps the keys in a hash according to a block.
48
+ #
49
+ # @example
50
+ # map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
51
+ # #=> {"foo" => "bar", "baz" => "bang"}
52
+ # @param hash [Hash] The hash to map
53
+ # @yield [key] A block in which the keys are transformed
54
+ # @yieldparam key [Object] The key that should be mapped
55
+ # @yieldreturn [Object] The new value for the key
56
+ # @return [Hash] The mapped hash
57
+ # @see #map_vals
58
+ # @see #map_hash
59
+ def map_keys(hash)
60
+ map_hash(hash) {|k, v| [yield(k), v]}
61
+ end
62
+
63
+ # Maps the values in a hash according to a block.
64
+ #
65
+ # @example
66
+ # map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
67
+ # #=> {:foo => :bar, :baz => :bang}
68
+ # @param hash [Hash] The hash to map
69
+ # @yield [value] A block in which the values are transformed
70
+ # @yieldparam value [Object] The value that should be mapped
71
+ # @yieldreturn [Object] The new value for the value
72
+ # @return [Hash] The mapped hash
73
+ # @see #map_keys
74
+ # @see #map_hash
75
+ def map_vals(hash)
76
+ # We don't delegate to map_hash for performance here
77
+ # because map_hash does more than is necessary.
78
+ rv = hash.class.new
79
+ hash = hash.as_stored if hash.is_a?(NormalizedMap)
80
+ hash.each do |k, v|
81
+ rv[k] = yield(v)
82
+ end
83
+ rv
84
+ end
85
+
86
+ # Maps the key-value pairs of a hash according to a block.
87
+ #
88
+ # @example
89
+ # map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
90
+ # #=> {"foo" => :bar, "baz" => :bang}
91
+ # @param hash [Hash] The hash to map
92
+ # @yield [key, value] A block in which the key-value pairs are transformed
93
+ # @yieldparam [key] The hash key
94
+ # @yieldparam [value] The hash value
95
+ # @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
96
+ # @return [Hash] The mapped hash
97
+ # @see #map_keys
98
+ # @see #map_vals
99
+ def map_hash(hash)
100
+ # Copy and modify is more performant than mapping to an array and using
101
+ # to_hash on the result.
102
+ rv = hash.class.new
103
+ hash.each do |k, v|
104
+ new_key, new_value = yield(k, v)
105
+ new_key = hash.denormalize(new_key) if hash.is_a?(NormalizedMap) && new_key == k
106
+ rv[new_key] = new_value
107
+ end
108
+ rv
109
+ end
110
+
111
+ # Computes the powerset of the given array.
112
+ # This is the set of all subsets of the array.
113
+ #
114
+ # @example
115
+ # powerset([1, 2, 3]) #=>
116
+ # Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
117
+ # @param arr [Enumerable]
118
+ # @return [Set<Set>] The subsets of `arr`
119
+ def powerset(arr)
120
+ arr.inject([Set.new].to_set) do |powerset, el|
121
+ new_powerset = Set.new
122
+ powerset.each do |subset|
123
+ new_powerset << subset
124
+ new_powerset << subset + [el]
125
+ end
126
+ new_powerset
127
+ end
128
+ end
129
+
130
+ # Restricts a number to falling within a given range.
131
+ # Returns the number if it falls within the range,
132
+ # or the closest value in the range if it doesn't.
133
+ #
134
+ # @param value [Numeric]
135
+ # @param range [Range<Numeric>]
136
+ # @return [Numeric]
137
+ def restrict(value, range)
138
+ [[value, range.first].max, range.last].min
139
+ end
140
+
141
+ # Concatenates all strings that are adjacent in an array,
142
+ # while leaving other elements as they are.
143
+ #
144
+ # @example
145
+ # merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
146
+ # #=> [1, "foobar", 2, "baz"]
147
+ # @param arr [Array]
148
+ # @return [Array] The enumerable with strings merged
149
+ def merge_adjacent_strings(arr)
150
+ # Optimize for the common case of one element
151
+ return arr if arr.size < 2
152
+ arr.inject([]) do |a, e|
153
+ if e.is_a?(String)
154
+ if a.last.is_a?(String)
155
+ a.last << e
156
+ else
157
+ a << e.dup
158
+ end
159
+ else
160
+ a << e
161
+ end
162
+ a
163
+ end
164
+ end
165
+
166
+ # Non-destructively replaces all occurrences of a subsequence in an array
167
+ # with another subsequence.
168
+ #
169
+ # @example
170
+ # replace_subseq([1, 2, 3, 4, 5], [2, 3], [:a, :b])
171
+ # #=> [1, :a, :b, 4, 5]
172
+ #
173
+ # @param arr [Array] The array whose subsequences will be replaced.
174
+ # @param subseq [Array] The subsequence to find and replace.
175
+ # @param replacement [Array] The sequence that `subseq` will be replaced with.
176
+ # @return [Array] `arr` with `subseq` replaced with `replacement`.
177
+ def replace_subseq(arr, subseq, replacement)
178
+ new = []
179
+ matched = []
180
+ i = 0
181
+ arr.each do |elem|
182
+ if elem != subseq[i]
183
+ new.push(*matched)
184
+ matched = []
185
+ i = 0
186
+ new << elem
187
+ next
188
+ end
189
+
190
+ if i == subseq.length - 1
191
+ matched = []
192
+ i = 0
193
+ new.push(*replacement)
194
+ else
195
+ matched << elem
196
+ i += 1
197
+ end
198
+ end
199
+ new.push(*matched)
200
+ new
201
+ end
202
+
203
+ # Intersperses a value in an enumerable, as would be done with `Array#join`
204
+ # but without concatenating the array together afterwards.
205
+ #
206
+ # @param enum [Enumerable]
207
+ # @param val
208
+ # @return [Array]
209
+ def intersperse(enum, val)
210
+ enum.inject([]) {|a, e| a << e << val}[0...-1]
211
+ end
212
+
213
+ def slice_by(enum)
214
+ results = []
215
+ enum.each do |value|
216
+ key = yield(value)
217
+ if !results.empty? && results.last.first == key
218
+ results.last.last << value
219
+ else
220
+ results << [key, [value]]
221
+ end
222
+ end
223
+ results
224
+ end
225
+
226
+ # Substitutes a sub-array of one array with another sub-array.
227
+ #
228
+ # @param ary [Array] The array in which to make the substitution
229
+ # @param from [Array] The sequence of elements to replace with `to`
230
+ # @param to [Array] The sequence of elements to replace `from` with
231
+ def substitute(ary, from, to)
232
+ res = ary.dup
233
+ i = 0
234
+ while i < res.size
235
+ if res[i...i + from.size] == from
236
+ res[i...i + from.size] = to
237
+ end
238
+ i += 1
239
+ end
240
+ res
241
+ end
242
+
243
+ # Destructively strips whitespace from the beginning and end
244
+ # of the first and last elements, respectively,
245
+ # in the array (if those elements are strings).
246
+ #
247
+ # @param arr [Array]
248
+ # @return [Array] `arr`
249
+ def strip_string_array(arr)
250
+ arr.first.lstrip! if arr.first.is_a?(String)
251
+ arr.last.rstrip! if arr.last.is_a?(String)
252
+ arr
253
+ end
254
+
255
+ # Return an array of all possible paths through the given arrays.
256
+ #
257
+ # @param arrs [Array<Array>]
258
+ # @return [Array<Arrays>]
259
+ #
260
+ # @example
261
+ # paths([[1, 2], [3, 4], [5]]) #=>
262
+ # # [[1, 3, 5],
263
+ # # [2, 3, 5],
264
+ # # [1, 4, 5],
265
+ # # [2, 4, 5]]
266
+ def paths(arrs)
267
+ arrs.inject([[]]) do |paths, arr|
268
+ flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
269
+ end
270
+ end
271
+
272
+ # Computes a single longest common subsequence for `x` and `y`.
273
+ # If there are more than one longest common subsequences,
274
+ # the one returned is that which starts first in `x`.
275
+ #
276
+ # @param x [Array]
277
+ # @param y [Array]
278
+ # @yield [a, b] An optional block to use in place of a check for equality
279
+ # between elements of `x` and `y`.
280
+ # @yieldreturn [Object, nil] If the two values register as equal,
281
+ # this will return the value to use in the LCS array.
282
+ # @return [Array] The LCS
283
+ def lcs(x, y, &block)
284
+ x = [nil, *x]
285
+ y = [nil, *y]
286
+ block ||= proc {|a, b| a == b && a}
287
+ lcs_backtrace(lcs_table(x, y, &block), x, y, x.size - 1, y.size - 1, &block)
288
+ end
289
+
290
+ # Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
291
+ # with the following exceptions:
292
+ #
293
+ # * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
294
+ # * In Ruby 1.9 when running tests, this is ordered in the same way it would
295
+ # be under Ruby 1.8 (sorted key order rather than insertion order).
296
+ #
297
+ # @param hash [Hash]
298
+ # @return [Array]
299
+ def hash_to_a(hash)
300
+ return hash.to_a unless ruby1_8? || defined?(Test::Unit)
301
+ hash.sort_by {|k, v| k}
302
+ end
303
+
304
+ # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
305
+ # order. Unlike {Util#hash_to_a}, the resulting order isn't sorted key order;
306
+ # instead, it's the same order as `#group_by` has under Ruby 1.9 (key
307
+ # appearance order).
308
+ #
309
+ # @param enum [Enumerable]
310
+ # @return [Array<[Object, Array]>] An array of pairs.
311
+ def group_by_to_a(enum)
312
+ return enum.group_by {|e| yield(e)}.to_a unless ruby1_8?
313
+ order = {}
314
+ arr = []
315
+ groups = enum.group_by do |e|
316
+ res = yield(e)
317
+ unless order.include?(res)
318
+ order[res] = order.size
319
+ end
320
+ res
321
+ end
322
+ groups.each do |key, vals|
323
+ arr[order[key]] = [key, vals]
324
+ end
325
+ arr
326
+ end
327
+
328
+ # Returns a sub-array of `minuend` containing only elements that are also in
329
+ # `subtrahend`. Ensures that the return value has the same order as
330
+ # `minuend`, even on Rubinius where that's not guaranteed by `Array#-`.
331
+ #
332
+ # @param minuend [Array]
333
+ # @param subtrahend [Array]
334
+ # @return [Array]
335
+ def array_minus(minuend, subtrahend)
336
+ return minuend - subtrahend unless rbx?
337
+ set = Set.new(minuend) - subtrahend
338
+ minuend.select {|e| set.include?(e)}
339
+ end
340
+
341
+ # Returns the maximum of `val1` and `val2`. We use this over \{Array.max} to
342
+ # avoid unnecessary garbage collection.
343
+ def max(val1, val2)
344
+ val1 > val2 ? val1 : val2
345
+ end
346
+
347
+ # Returns the minimum of `val1` and `val2`. We use this over \{Array.min} to
348
+ # avoid unnecessary garbage collection.
349
+ def min(val1, val2)
350
+ val1 <= val2 ? val1 : val2
351
+ end
352
+
353
+ # Returns a string description of the character that caused an
354
+ # `Encoding::UndefinedConversionError`.
355
+ #
356
+ # @param e [Encoding::UndefinedConversionError]
357
+ # @return [String]
358
+ def undefined_conversion_error_char(e)
359
+ # Rubinius (as of 2.0.0.rc1) pre-quotes the error character.
360
+ return e.error_char if rbx?
361
+ # JRuby (as of 1.7.2) doesn't have an error_char field on
362
+ # Encoding::UndefinedConversionError.
363
+ return e.error_char.dump unless jruby?
364
+ e.message[/^"[^"]+"/] # "
365
+ end
366
+
367
+ # Asserts that `value` falls within `range` (inclusive), leaving
368
+ # room for slight floating-point errors.
369
+ #
370
+ # @param name [String] The name of the value. Used in the error message.
371
+ # @param range [Range] The allowed range of values.
372
+ # @param value [Numeric, Sass::Script::Value::Number] The value to check.
373
+ # @param unit [String] The unit of the value. Used in error reporting.
374
+ # @return [Numeric] `value` adjusted to fall within range, if it
375
+ # was outside by a floating-point margin.
376
+ def check_range(name, range, value, unit = '')
377
+ grace = (-0.00001..0.00001)
378
+ str = value.to_s
379
+ value = value.value if value.is_a?(Sass::Script::Value::Number)
380
+ return value if range.include?(value)
381
+ return range.first if grace.include?(value - range.first)
382
+ return range.last if grace.include?(value - range.last)
383
+ raise ArgumentError.new(
384
+ "#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
385
+ end
386
+
387
+ # Returns whether or not `seq1` is a subsequence of `seq2`. That is, whether
388
+ # or not `seq2` contains every element in `seq1` in the same order (and
389
+ # possibly more elements besides).
390
+ #
391
+ # @param seq1 [Array]
392
+ # @param seq2 [Array]
393
+ # @return [Boolean]
394
+ def subsequence?(seq1, seq2)
395
+ i = j = 0
396
+ loop do
397
+ return true if i == seq1.size
398
+ return false if j == seq2.size
399
+ i += 1 if seq1[i] == seq2[j]
400
+ j += 1
401
+ end
402
+ end
403
+
404
+ # Returns information about the caller of the previous method.
405
+ #
406
+ # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
407
+ # @return [[String, Fixnum, (String, nil)]]
408
+ # An array containing the filename, line, and method name of the caller.
409
+ # The method name may be nil
410
+ def caller_info(entry = nil)
411
+ # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
412
+ entry ||= caller[1]
413
+ info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
414
+ info[1] = info[1].to_i
415
+ # This is added by Rubinius to designate a block, but we don't care about it.
416
+ info[2].sub!(/ \{\}\Z/, '') if info[2]
417
+ info
418
+ end
419
+
420
+ # Returns whether one version string represents a more recent version than another.
421
+ #
422
+ # @param v1 [String] A version string.
423
+ # @param v2 [String] Another version string.
424
+ # @return [Boolean]
425
+ def version_gt(v1, v2)
426
+ # Construct an array to make sure the shorter version is padded with nil
427
+ Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
428
+ p1 ||= "0"
429
+ p2 ||= "0"
430
+ release1 = p1 =~ /^[0-9]+$/
431
+ release2 = p2 =~ /^[0-9]+$/
432
+ if release1 && release2
433
+ # Integer comparison if both are full releases
434
+ p1, p2 = p1.to_i, p2.to_i
435
+ next if p1 == p2
436
+ return p1 > p2
437
+ elsif !release1 && !release2
438
+ # String comparison if both are prereleases
439
+ next if p1 == p2
440
+ return p1 > p2
441
+ else
442
+ # If only one is a release, that one is newer
443
+ return release1
444
+ end
445
+ end
446
+ end
447
+
448
+ # Returns whether one version string represents the same or a more
449
+ # 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_geq(v1, v2)
455
+ version_gt(v1, v2) || !version_gt(v2, v1)
456
+ end
457
+
458
+ # Throws a NotImplementedError for an abstract method.
459
+ #
460
+ # @param obj [Object] `self`
461
+ # @raise [NotImplementedError]
462
+ def abstract(obj)
463
+ raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
464
+ end
465
+
466
+ # Prints a deprecation warning for the caller method.
467
+ #
468
+ # @param obj [Object] `self`
469
+ # @param message [String] A message describing what to do instead.
470
+ def deprecated(obj, message = nil)
471
+ obj_class = obj.is_a?(Class) ? "#{obj}." : "#{obj.class}#"
472
+ full_message = "DEPRECATION WARNING: #{obj_class}#{caller_info[2]} " +
473
+ "will be removed in a future version of Sass.#{("\n" + message) if message}"
474
+ Sass::Util.sass_warn full_message
475
+ end
476
+
477
+ # Silence all output to STDERR within a block.
478
+ #
479
+ # @yield A block in which no output will be printed to STDERR
480
+ def silence_warnings
481
+ the_real_stderr, $stderr = $stderr, StringIO.new
482
+ yield
483
+ ensure
484
+ $stderr = the_real_stderr
485
+ end
486
+
487
+ # Silences all Sass warnings within a block.
488
+ #
489
+ # @yield A block in which no Sass warnings will be printed
490
+ def silence_sass_warnings
491
+ old_level, Sass.logger.log_level = Sass.logger.log_level, :error
492
+ yield
493
+ ensure
494
+ Sass.logger.log_level = old_level
495
+ end
496
+
497
+ # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
498
+ #
499
+ # @param msg [String]
500
+ def sass_warn(msg)
501
+ msg = msg + "\n" unless ruby1?
502
+ Sass.logger.warn(msg)
503
+ end
504
+
505
+ ## Cross Rails Version Compatibility
506
+
507
+ # Returns the root of the Rails application,
508
+ # if this is running in a Rails context.
509
+ # Returns `nil` if no such root is defined.
510
+ #
511
+ # @return [String, nil]
512
+ def rails_root
513
+ if defined?(::Rails.root)
514
+ return ::Rails.root.to_s if ::Rails.root
515
+ raise "ERROR: Rails.root is nil!"
516
+ end
517
+ return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
518
+ nil
519
+ end
520
+
521
+ # Returns the environment of the Rails application,
522
+ # if this is running in a Rails context.
523
+ # Returns `nil` if no such environment is defined.
524
+ #
525
+ # @return [String, nil]
526
+ def rails_env
527
+ return ::Rails.env.to_s if defined?(::Rails.env)
528
+ return RAILS_ENV.to_s if defined?(RAILS_ENV)
529
+ nil
530
+ end
531
+
532
+ # Returns whether this environment is using ActionPack
533
+ # version 3.0.0 or greater.
534
+ #
535
+ # @return [Boolean]
536
+ def ap_geq_3?
537
+ ap_geq?("3.0.0.beta1")
538
+ end
539
+
540
+ # Returns whether this environment is using ActionPack
541
+ # of a version greater than or equal to that specified.
542
+ #
543
+ # @param version [String] The string version number to check against.
544
+ # Should be greater than or equal to Rails 3,
545
+ # because otherwise ActionPack::VERSION isn't autoloaded
546
+ # @return [Boolean]
547
+ def ap_geq?(version)
548
+ # The ActionPack module is always loaded automatically in Rails >= 3
549
+ return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
550
+ defined?(ActionPack::VERSION::STRING)
551
+
552
+ version_geq(ActionPack::VERSION::STRING, version)
553
+ end
554
+
555
+ # Returns whether this environment is using Listen
556
+ # version 2.0.0 or greater.
557
+ #
558
+ # @return [Boolean]
559
+ def listen_geq_2?
560
+ return @listen_geq_2 unless @listen_geq_2.nil?
561
+ @listen_geq_2 =
562
+ begin
563
+ require 'listen/version'
564
+ version_geq(::Listen::VERSION, '2.0.0')
565
+ rescue LoadError
566
+ false
567
+ end
568
+ end
569
+
570
+ # Returns an ActionView::Template* class.
571
+ # In pre-3.0 versions of Rails, most of these classes
572
+ # were of the form `ActionView::TemplateFoo`,
573
+ # while afterwards they were of the form `ActionView;:Template::Foo`.
574
+ #
575
+ # @param name [#to_s] The name of the class to get.
576
+ # For example, `:Error` will return `ActionView::TemplateError`
577
+ # or `ActionView::Template::Error`.
578
+ def av_template_class(name)
579
+ return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
580
+ ActionView::Template.const_get(name.to_s)
581
+ end
582
+
583
+ ## Cross-OS Compatibility
584
+ #
585
+ # These methods are cached because some of them are called quite frequently
586
+ # and even basic checks like String#== are too costly to be called repeatedly.
587
+
588
+ # Whether or not this is running on Windows.
589
+ #
590
+ # @return [Boolean]
591
+ def windows?
592
+ return @windows if defined?(@windows)
593
+ @windows = (RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i)
594
+ end
595
+
596
+ # Whether or not this is running on IronRuby.
597
+ #
598
+ # @return [Boolean]
599
+ def ironruby?
600
+ return @ironruby if defined?(@ironruby)
601
+ @ironruby = RUBY_ENGINE == "ironruby"
602
+ end
603
+
604
+ # Whether or not this is running on Rubinius.
605
+ #
606
+ # @return [Boolean]
607
+ def rbx?
608
+ return @rbx if defined?(@rbx)
609
+ @rbx = RUBY_ENGINE == "rbx"
610
+ end
611
+
612
+ # Whether or not this is running on JRuby.
613
+ #
614
+ # @return [Boolean]
615
+ def jruby?
616
+ return @jruby if defined?(@jruby)
617
+ @jruby = RUBY_PLATFORM =~ /java/
618
+ end
619
+
620
+ # Returns an array of ints representing the JRuby version number.
621
+ #
622
+ # @return [Array<Fixnum>]
623
+ def jruby_version
624
+ @jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
625
+ end
626
+
627
+ # Like `Dir.glob`, but works with backslash-separated paths on Windows.
628
+ #
629
+ # @param path [String]
630
+ def glob(path)
631
+ path = path.gsub('\\', '/') if windows?
632
+ if block_given?
633
+ Dir.glob(path) {|f| yield(f)}
634
+ else
635
+ Dir.glob(path)
636
+ end
637
+ end
638
+
639
+ # Like `Pathname.new`, but normalizes Windows paths to always use backslash
640
+ # separators.
641
+ #
642
+ # `Pathname#relative_path_from` can break if the two pathnames aren't
643
+ # consistent in their slash style.
644
+ #
645
+ # @param path [String]
646
+ # @return [Pathname]
647
+ def pathname(path)
648
+ path = path.tr("/", "\\") if windows?
649
+ Pathname.new(path)
650
+ end
651
+
652
+ # Like `Pathname#cleanpath`, but normalizes Windows paths to always use
653
+ # backslash separators. Normally, `Pathname#cleanpath` actually does the
654
+ # reverse -- it will convert backslashes to forward slashes, which can break
655
+ # `Pathname#relative_path_from`.
656
+ #
657
+ # @param path [String, Pathname]
658
+ # @return [Pathname]
659
+ def cleanpath(path)
660
+ path = Pathname.new(path) unless path.is_a?(Pathname)
661
+ pathname(path.cleanpath.to_s)
662
+ end
663
+
664
+ # Converts `path` to a "file:" URI. This handles Windows paths correctly.
665
+ #
666
+ # @param path [String, Pathname]
667
+ # @return [String]
668
+ def file_uri_from_path(path)
669
+ path = path.to_s if path.is_a?(Pathname)
670
+ path = Sass::Util.escape_uri(path)
671
+ return path.start_with?('/') ? "file://" + path : path unless windows?
672
+ return "file:///" + path.tr("\\", "/") if path =~ /^[a-zA-Z]:[\/\\]/
673
+ return "file://" + path.tr("\\", "/") if path =~ /\\\\[^\\]+\\[^\\\/]+/
674
+ path.tr("\\", "/")
675
+ end
676
+
677
+ # Prepare a value for a destructuring assignment (e.g. `a, b =
678
+ # val`). This works around a performance bug when using
679
+ # ActiveSupport, and only needs to be called when `val` is likely
680
+ # to be `nil` reasonably often.
681
+ #
682
+ # See [this bug report](http://redmine.ruby-lang.org/issues/4917).
683
+ #
684
+ # @param val [Object]
685
+ # @return [Object]
686
+ def destructure(val)
687
+ val || []
688
+ end
689
+
690
+ ## Cross-Ruby-Version Compatibility
691
+
692
+ # Whether or not this is running under a Ruby version under 2.0.
693
+ #
694
+ # @return [Boolean]
695
+ def ruby1?
696
+ return @ruby1 if defined?(@ruby1)
697
+ @ruby1 = RUBY_VERSION_COMPONENTS[0] <= 1
698
+ end
699
+
700
+ # Whether or not this is running under Ruby 1.8 or lower.
701
+ #
702
+ # Note that IronRuby counts as Ruby 1.8,
703
+ # because it doesn't support the Ruby 1.9 encoding API.
704
+ #
705
+ # @return [Boolean]
706
+ def ruby1_8?
707
+ # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
708
+ # We have to fall back to 1.8 behavior.
709
+ return @ruby1_8 if defined?(@ruby1_8)
710
+ @ruby1_8 = ironruby? ||
711
+ (RUBY_VERSION_COMPONENTS[0] == 1 && RUBY_VERSION_COMPONENTS[1] < 9)
712
+ end
713
+
714
+ # Whether or not this is running under Ruby 1.8.6 or lower.
715
+ # Note that lower versions are not officially supported.
716
+ #
717
+ # @return [Boolean]
718
+ def ruby1_8_6?
719
+ return @ruby1_8_6 if defined?(@ruby1_8_6)
720
+ @ruby1_8_6 = ruby1_8? && RUBY_VERSION_COMPONENTS[2] < 7
721
+ end
722
+
723
+ # Whether or not this is running under Ruby 1.9.2 exactly.
724
+ #
725
+ # @return [Boolean]
726
+ def ruby1_9_2?
727
+ return @ruby1_9_2 if defined?(@ruby1_9_2)
728
+ @ruby1_9_2 = RUBY_VERSION_COMPONENTS == [1, 9, 2]
729
+ end
730
+
731
+ # Wehter or not this is running under JRuby 1.6 or lower.
732
+ def jruby1_6?
733
+ return @jruby1_6 if defined?(@jruby1_6)
734
+ @jruby1_6 = jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
735
+ end
736
+
737
+ # Whether or not this is running under MacRuby.
738
+ #
739
+ # @return [Boolean]
740
+ def macruby?
741
+ return @macruby if defined?(@macruby)
742
+ @macruby = RUBY_ENGINE == 'macruby'
743
+ end
744
+
745
+ require 'sass/util/ordered_hash' if ruby1_8?
746
+
747
+ # Converts a hash or a list of pairs into an order-preserving hash.
748
+ #
749
+ # On Ruby 1.8.7, this uses the orderedhash gem to simulate an
750
+ # order-preserving hash. On Ruby 1.9 and up, it just uses the native Hash
751
+ # class, since that preserves the order itself.
752
+ #
753
+ # @overload ordered_hash(hash)
754
+ # @param hash [Hash] a normal hash to convert to an ordered hash
755
+ # @return [Hash]
756
+ # @overload ordered_hash(*pairs)
757
+ # @example
758
+ # ordered_hash([:foo, "bar"], [:baz, "bang"])
759
+ # #=> {:foo => "bar", :baz => "bang"}
760
+ # ordered_hash #=> {}
761
+ # @param pairs [Array<(Object, Object)>] the list of key/value pairs for
762
+ # the hash.
763
+ # @return [Hash]
764
+ def ordered_hash(*pairs_or_hash)
765
+ if pairs_or_hash.length == 1 && pairs_or_hash.first.is_a?(Hash)
766
+ hash = pairs_or_hash.first
767
+ return hash unless ruby1_8?
768
+ return OrderedHash.new.merge hash
769
+ end
770
+
771
+ return Hash[pairs_or_hash] unless ruby1_8?
772
+ (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
773
+ end
774
+
775
+ unless ruby1_8?
776
+ CHARSET_REGEXP = /\A@charset "([^"]+)"/
777
+ UTF_8_BOM = "\xEF\xBB\xBF".force_encoding('BINARY')
778
+ UTF_16BE_BOM = "\xFE\xFF".force_encoding('BINARY')
779
+ UTF_16LE_BOM = "\xFF\xFE".force_encoding('BINARY')
780
+ end
781
+
782
+ # Like {\#check\_encoding}, but also checks for a `@charset` declaration
783
+ # at the beginning of the file and uses that encoding if it exists.
784
+ #
785
+ # Sass follows CSS's decoding rules.
786
+ #
787
+ # @param str [String] The string of which to check the encoding
788
+ # @return [(String, Encoding)] The original string encoded as UTF-8,
789
+ # and the source encoding of the string (or `nil` under Ruby 1.8)
790
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
791
+ # cannot be converted to UTF-8
792
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
793
+ # @raise [Sass::SyntaxError] If the document declares an encoding that
794
+ # doesn't match its contents, or it doesn't declare an encoding and its
795
+ # contents are invalid in the native encoding.
796
+ def check_sass_encoding(str)
797
+ # On Ruby 1.8 we can't do anything complicated with encodings.
798
+ # Instead, we just strip out a UTF-8 BOM if it exists and
799
+ # sanitize according to Section 3.3 of CSS Syntax Level 3. We
800
+ # don't sanitize null characters since they might be components
801
+ # of other characters.
802
+ if ruby1_8?
803
+ return str.gsub(/\A\xEF\xBB\xBF/, '').gsub(/\r\n?|\f/, "\n"), nil
804
+ end
805
+
806
+ # Determine the fallback encoding following section 3.2 of CSS Syntax Level 3 and Encodings:
807
+ # http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#determine-the-fallback-encoding
808
+ # http://encoding.spec.whatwg.org/#decode
809
+ binary = str.dup.force_encoding("BINARY")
810
+ if binary.start_with?(UTF_8_BOM)
811
+ binary.slice! 0, UTF_8_BOM.length
812
+ str = binary.force_encoding('UTF-8')
813
+ elsif binary.start_with?(UTF_16BE_BOM)
814
+ binary.slice! 0, UTF_16BE_BOM.length
815
+ str = binary.force_encoding('UTF-16BE')
816
+ elsif binary.start_with?(UTF_16LE_BOM)
817
+ binary.slice! 0, UTF_16LE_BOM.length
818
+ str = binary.force_encoding('UTF-16LE')
819
+ elsif binary =~ CHARSET_REGEXP
820
+ charset = $1.force_encoding('US-ASCII')
821
+ # Ruby 1.9.2 doesn't recognize a UTF-16 encoding without an endian marker.
822
+ if ruby1_9_2? && charset.downcase == 'utf-16'
823
+ encoding = Encoding.find('UTF-8')
824
+ else
825
+ encoding = Encoding.find(charset)
826
+ if encoding.name == 'UTF-16' || encoding.name == 'UTF-16BE'
827
+ encoding = Encoding.find('UTF-8')
828
+ end
829
+ end
830
+ str = binary.force_encoding(encoding)
831
+ elsif str.encoding.name == "ASCII-8BIT"
832
+ # Normally we want to fall back on believing the Ruby string
833
+ # encoding, but if that's just binary we want to make sure
834
+ # it's valid UTF-8.
835
+ str = str.force_encoding('utf-8')
836
+ end
837
+
838
+ find_encoding_error(str) unless str.valid_encoding?
839
+
840
+ begin
841
+ # If the string is valid, preprocess it according to section 3.3 of CSS Syntax Level 3.
842
+ return str.encode("UTF-8").gsub(/\r\n?|\f/, "\n").tr("\u0000", "�"), str.encoding
843
+ rescue EncodingError
844
+ find_encoding_error(str)
845
+ end
846
+ end
847
+
848
+ # Checks to see if a class has a given method.
849
+ # For example:
850
+ #
851
+ # Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
852
+ #
853
+ # Method collections like `Class#instance_methods`
854
+ # return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
855
+ # so this handles checking for them in a compatible way.
856
+ #
857
+ # @param attr [#to_s] The (singular) name of the method-collection method
858
+ # (e.g. `:instance_methods`, `:private_methods`)
859
+ # @param klass [Module] The class to check the methods of which to check
860
+ # @param method [String, Symbol] The name of the method do check for
861
+ # @return [Boolean] Whether or not the given collection has the given method
862
+ def has?(attr, klass, method)
863
+ klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
864
+ end
865
+
866
+ # A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
867
+ #
868
+ # @param enum [Enumerable] The enumerable to get the enumerator for
869
+ # @return [Enumerator] The with-index enumerator
870
+ def enum_with_index(enum)
871
+ ruby1_8? ? enum.enum_with_index : enum.each_with_index
872
+ end
873
+
874
+ # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
875
+ #
876
+ # @param enum [Enumerable] The enumerable to get the enumerator for
877
+ # @param n [Fixnum] The size of each cons
878
+ # @return [Enumerator] The consed enumerator
879
+ def enum_cons(enum, n)
880
+ ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
881
+ end
882
+
883
+ # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
884
+ #
885
+ # @param enum [Enumerable] The enumerable to get the enumerator for
886
+ # @param n [Fixnum] The size of each slice
887
+ # @return [Enumerator] The consed enumerator
888
+ def enum_slice(enum, n)
889
+ ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
890
+ end
891
+
892
+ # Destructively removes all elements from an array that match a block, and
893
+ # returns the removed elements.
894
+ #
895
+ # @param array [Array] The array from which to remove elements.
896
+ # @yield [el] Called for each element.
897
+ # @yieldparam el [*] The element to test.
898
+ # @yieldreturn [Boolean] Whether or not to extract the element.
899
+ # @return [Array] The extracted elements.
900
+ def extract!(array)
901
+ out = []
902
+ array.reject! do |e|
903
+ next false unless yield e
904
+ out << e
905
+ true
906
+ end
907
+ out
908
+ end
909
+
910
+ # Returns the ASCII code of the given character.
911
+ #
912
+ # @param c [String] All characters but the first are ignored.
913
+ # @return [Fixnum] The ASCII code of `c`.
914
+ def ord(c)
915
+ ruby1_8? ? c[0] : c.ord
916
+ end
917
+
918
+ # Flattens the first `n` nested arrays in a cross-version manner.
919
+ #
920
+ # @param arr [Array] The array to flatten
921
+ # @param n [Fixnum] The number of levels to flatten
922
+ # @return [Array] The flattened array
923
+ def flatten(arr, n)
924
+ return arr.flatten(n) unless ruby1_8_6?
925
+ return arr if n == 0
926
+ arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
927
+ end
928
+
929
+ # Flattens the first level of nested arrays in `arrs`. Unlike
930
+ # `Array#flatten`, this orders the result by taking the first
931
+ # values from each array in order, then the second, and so on.
932
+ #
933
+ # @param arrs [Array] The array to flatten.
934
+ # @return [Array] The flattened array.
935
+ def flatten_vertically(arrs)
936
+ result = []
937
+ arrs = arrs.map {|sub| sub.is_a?(Array) ? sub.dup : Array(sub)}
938
+ until arrs.empty?
939
+ arrs.reject! do |arr|
940
+ result << arr.shift
941
+ arr.empty?
942
+ end
943
+ end
944
+ result
945
+ end
946
+
947
+ # Returns the hash code for a set in a cross-version manner.
948
+ # Aggravatingly, this is order-dependent in Ruby 1.8.6.
949
+ #
950
+ # @param set [Set]
951
+ # @return [Fixnum] The order-independent hashcode of `set`
952
+ def set_hash(set)
953
+ return set.hash unless ruby1_8_6?
954
+ set.map {|e| e.hash}.uniq.sort.hash
955
+ end
956
+
957
+ # Tests the hash-equality of two sets in a cross-version manner.
958
+ # Aggravatingly, this is order-dependent in Ruby 1.8.6.
959
+ #
960
+ # @param set1 [Set]
961
+ # @param set2 [Set]
962
+ # @return [Boolean] Whether or not the sets are hashcode equal
963
+ def set_eql?(set1, set2)
964
+ return set1.eql?(set2) unless ruby1_8_6?
965
+ set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
966
+ end
967
+
968
+ # Like `Object#inspect`, but preserves non-ASCII characters rather than
969
+ # escaping them under Ruby 1.9.2. This is necessary so that the
970
+ # precompiled Haml template can be `#encode`d into `@options[:encoding]`
971
+ # before being evaluated.
972
+ #
973
+ # @param obj {Object}
974
+ # @return {String}
975
+ def inspect_obj(obj)
976
+ return obj.inspect unless version_geq(RUBY_VERSION, "1.9.2")
977
+ return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
978
+ return obj.inspect unless obj.is_a?(String)
979
+ '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
980
+ end
981
+
982
+ # Extracts the non-string vlaues from an array containing both strings and non-strings.
983
+ # These values are replaced with escape sequences.
984
+ # This can be undone using \{#inject\_values}.
985
+ #
986
+ # This is useful e.g. when we want to do string manipulation
987
+ # on an interpolated string.
988
+ #
989
+ # The precise format of the resulting string is not guaranteed.
990
+ # However, it is guaranteed that newlines and whitespace won't be affected.
991
+ #
992
+ # @param arr [Array] The array from which values are extracted.
993
+ # @return [(String, Array)] The resulting string, and an array of extracted values.
994
+ def extract_values(arr)
995
+ values = []
996
+ mapped = arr.map do |e|
997
+ next e.gsub('{', '{{') if e.is_a?(String)
998
+ values << e
999
+ next "{#{values.count - 1}}"
1000
+ end
1001
+ return mapped.join, values
1002
+ end
1003
+
1004
+ # Undoes \{#extract\_values} by transforming a string with escape sequences
1005
+ # into an array of strings and non-string values.
1006
+ #
1007
+ # @param str [String] The string with escape sequences.
1008
+ # @param values [Array] The array of values to inject.
1009
+ # @return [Array] The array of strings and values.
1010
+ def inject_values(str, values)
1011
+ return [str.gsub('{{', '{')] if values.empty?
1012
+ # Add an extra { so that we process the tail end of the string
1013
+ result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
1014
+ [pre, esc ? '{' : '', n ? values[n.to_i] : '']
1015
+ end.flatten(1)
1016
+ result[-2] = '' # Get rid of the extra {
1017
+ merge_adjacent_strings(result).reject {|s| s == ''}
1018
+ end
1019
+
1020
+ # Allows modifications to be performed on the string form
1021
+ # of an array containing both strings and non-strings.
1022
+ #
1023
+ # @param arr [Array] The array from which values are extracted.
1024
+ # @yield [str] A block in which string manipulation can be done to the array.
1025
+ # @yieldparam str [String] The string form of `arr`.
1026
+ # @yieldreturn [String] The modified string.
1027
+ # @return [Array] The modified, interpolated array.
1028
+ def with_extracted_values(arr)
1029
+ str, vals = extract_values(arr)
1030
+ str = yield str
1031
+ inject_values(str, vals)
1032
+ end
1033
+
1034
+ # Builds a sourcemap file name given the generated CSS file name.
1035
+ #
1036
+ # @param css [String] The generated CSS file name.
1037
+ # @return [String] The source map file name.
1038
+ def sourcemap_name(css)
1039
+ css + ".map"
1040
+ end
1041
+
1042
+ # Escapes certain characters so that the result can be used
1043
+ # as the JSON string value. Returns the original string if
1044
+ # no escaping is necessary.
1045
+ #
1046
+ # @param s [String] The string to be escaped
1047
+ # @return [String] The escaped string
1048
+ def json_escape_string(s)
1049
+ return s if s !~ /["\\\b\f\n\r\t]/
1050
+
1051
+ result = ""
1052
+ s.split("").each do |c|
1053
+ case c
1054
+ when '"', "\\"
1055
+ result << "\\" << c
1056
+ when "\n" then result << "\\n"
1057
+ when "\t" then result << "\\t"
1058
+ when "\r" then result << "\\r"
1059
+ when "\f" then result << "\\f"
1060
+ when "\b" then result << "\\b"
1061
+ else
1062
+ result << c
1063
+ end
1064
+ end
1065
+ result
1066
+ end
1067
+
1068
+ # Converts the argument into a valid JSON value.
1069
+ #
1070
+ # @param v [Fixnum, String, Array, Boolean, nil]
1071
+ # @return [String]
1072
+ def json_value_of(v)
1073
+ case v
1074
+ when Fixnum
1075
+ v.to_s
1076
+ when String
1077
+ "\"" + json_escape_string(v) + "\""
1078
+ when Array
1079
+ "[" + v.map {|x| json_value_of(x)}.join(",") + "]"
1080
+ when NilClass
1081
+ "null"
1082
+ when TrueClass
1083
+ "true"
1084
+ when FalseClass
1085
+ "false"
1086
+ else
1087
+ raise ArgumentError.new("Unknown type: #{v.class.name}")
1088
+ end
1089
+ end
1090
+
1091
+ VLQ_BASE_SHIFT = 5
1092
+ VLQ_BASE = 1 << VLQ_BASE_SHIFT
1093
+ VLQ_BASE_MASK = VLQ_BASE - 1
1094
+ VLQ_CONTINUATION_BIT = VLQ_BASE
1095
+
1096
+ BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1097
+ BASE64_DIGIT_MAP = begin
1098
+ map = {}
1099
+ Sass::Util.enum_with_index(BASE64_DIGITS).map do |digit, i|
1100
+ map[digit] = i
1101
+ end
1102
+ map
1103
+ end
1104
+
1105
+ # Encodes `value` as VLQ (http://en.wikipedia.org/wiki/VLQ).
1106
+ #
1107
+ # @param value [Fixnum]
1108
+ # @return [String] The encoded value
1109
+ def encode_vlq(value)
1110
+ if value < 0
1111
+ value = ((-value) << 1) | 1
1112
+ else
1113
+ value <<= 1
1114
+ end
1115
+
1116
+ result = ''
1117
+ begin
1118
+ digit = value & VLQ_BASE_MASK
1119
+ value >>= VLQ_BASE_SHIFT
1120
+ if value > 0
1121
+ digit |= VLQ_CONTINUATION_BIT
1122
+ end
1123
+ result << BASE64_DIGITS[digit]
1124
+ end while value > 0
1125
+ result
1126
+ end
1127
+
1128
+ # This is a hack around the fact that you can't instantiate a URI parser on
1129
+ # 1.8, so we have to have this hacky stuff to work around it. When 1.8
1130
+ # support is dropped, we can remove this method.
1131
+ #
1132
+ # @private
1133
+ URI_ESCAPE = URI.const_defined?("DEFAULT_PARSER") ? URI::DEFAULT_PARSER : URI
1134
+
1135
+ # URI-escape `string`.
1136
+ #
1137
+ # @param string [String]
1138
+ # @return [String]
1139
+ def escape_uri(string)
1140
+ URI_ESCAPE.escape string
1141
+ end
1142
+
1143
+ # A cross-platform implementation of `File.absolute_path`.
1144
+ #
1145
+ # @param path [String]
1146
+ # @param dir_string [String] The directory to consider [path] relative to.
1147
+ # @return [String] The absolute version of `path`.
1148
+ def absolute_path(path, dir_string = nil)
1149
+ # Ruby 1.8 doesn't support File.absolute_path.
1150
+ return File.absolute_path(path, dir_string) unless ruby1_8?
1151
+
1152
+ # File.expand_path expands "~", which we don't want.
1153
+ return File.expand_path(path, dir_string) unless path[0] == ?~
1154
+ File.expand_path(File.join(".", path), dir_string)
1155
+ end
1156
+
1157
+ ## Static Method Stuff
1158
+
1159
+ # The context in which the ERB for \{#def\_static\_method} will be run.
1160
+ class StaticConditionalContext
1161
+ # @param set [#include?] The set of variables that are defined for this context.
1162
+ def initialize(set)
1163
+ @set = set
1164
+ end
1165
+
1166
+ # Checks whether or not a variable is defined for this context.
1167
+ #
1168
+ # @param name [Symbol] The name of the variable
1169
+ # @return [Boolean]
1170
+ def method_missing(name, *args)
1171
+ super unless args.empty? && !block_given?
1172
+ @set.include?(name)
1173
+ end
1174
+ end
1175
+
1176
+ # @private
1177
+ ATOMIC_WRITE_MUTEX = Mutex.new
1178
+
1179
+ # This creates a temp file and yields it for writing. When the
1180
+ # write is complete, the file is moved into the desired location.
1181
+ # The atomicity of this operation is provided by the filesystem's
1182
+ # rename operation.
1183
+ #
1184
+ # @param filename [String] The file to write to.
1185
+ # @param perms [Integer] The permissions used for creating this file.
1186
+ # Will be masked by the process umask. Defaults to readable/writeable
1187
+ # by all users however the umask usually changes this to only be writable
1188
+ # by the process's user.
1189
+ # @yieldparam tmpfile [Tempfile] The temp file that can be written to.
1190
+ # @return The value returned by the block.
1191
+ def atomic_create_and_write_file(filename, perms = 0666)
1192
+ require 'tempfile'
1193
+ tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
1194
+ tmpfile.binmode if tmpfile.respond_to?(:binmode)
1195
+ result = yield tmpfile
1196
+ tmpfile.close
1197
+ ATOMIC_WRITE_MUTEX.synchronize do
1198
+ begin
1199
+ File.chmod(perms & ~File.umask, tmpfile.path)
1200
+ rescue Errno::EPERM
1201
+ # If we don't have permissions to chmod the file, don't let that crash
1202
+ # the compilation. See issue 1215.
1203
+ end
1204
+ File.rename tmpfile.path, filename
1205
+ end
1206
+ result
1207
+ ensure
1208
+ # close and remove the tempfile if it still exists,
1209
+ # presumably due to an error during write
1210
+ tmpfile.close if tmpfile
1211
+ tmpfile.unlink if tmpfile
1212
+ end
1213
+
1214
+ def load_listen!
1215
+ if defined?(gem)
1216
+ begin
1217
+ gem 'listen', '>= 1.1.0', '< 3.0.0'
1218
+ require 'listen'
1219
+ rescue Gem::LoadError
1220
+ dir = scope("vendor/listen/lib")
1221
+ $LOAD_PATH.unshift dir
1222
+ begin
1223
+ require 'listen'
1224
+ rescue LoadError => e
1225
+ if version_geq(RUBY_VERSION, "1.9.3")
1226
+ version_constraint = "~> 2.7"
1227
+ else
1228
+ version_constraint = "~> 1.1"
1229
+ end
1230
+ e.message << "\n" <<
1231
+ "Run \"gem install listen --version '#{version_constraint}'\" to get it."
1232
+ raise e
1233
+ end
1234
+ end
1235
+ else
1236
+ begin
1237
+ require 'listen'
1238
+ rescue LoadError => e
1239
+ dir = scope("vendor/listen/lib")
1240
+ if $LOAD_PATH.include?(dir)
1241
+ raise e unless File.exist?(scope(".git"))
1242
+ e.message << "\n" <<
1243
+ 'Run "git submodule update --init" to get the bundled version.'
1244
+ else
1245
+ $LOAD_PATH.unshift dir
1246
+ retry
1247
+ end
1248
+ end
1249
+ end
1250
+ end
1251
+
1252
+ private
1253
+
1254
+ def find_encoding_error(str)
1255
+ encoding = str.encoding
1256
+ cr = Regexp.quote("\r".encode(encoding).force_encoding('BINARY'))
1257
+ lf = Regexp.quote("\n".encode(encoding).force_encoding('BINARY'))
1258
+ ff = Regexp.quote("\f".encode(encoding).force_encoding('BINARY'))
1259
+ line_break = /#{cr}#{lf}?|#{ff}|#{lf}/
1260
+
1261
+ str.force_encoding("binary").split(line_break).each_with_index do |line, i|
1262
+ begin
1263
+ line.encode(encoding)
1264
+ rescue Encoding::UndefinedConversionError => e
1265
+ raise Sass::SyntaxError.new(
1266
+ "Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}",
1267
+ :line => i + 1)
1268
+ end
1269
+ end
1270
+
1271
+ # We shouldn't get here, but it's possible some weird encoding stuff causes it.
1272
+ return str, str.encoding
1273
+ end
1274
+
1275
+ # rubocop:disable LineLength
1276
+
1277
+ # Calculates the memoization table for the Least Common Subsequence algorithm.
1278
+ # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
1279
+ def lcs_table(x, y)
1280
+ # This method does not take a block as an explicit parameter for performance reasons.
1281
+ # rubocop:enable LineLength
1282
+ c = Array.new(x.size) {[]}
1283
+ x.size.times {|i| c[i][0] = 0}
1284
+ y.size.times {|j| c[0][j] = 0}
1285
+ (1...x.size).each do |i|
1286
+ (1...y.size).each do |j|
1287
+ c[i][j] =
1288
+ if yield x[i], y[j]
1289
+ c[i - 1][j - 1] + 1
1290
+ else
1291
+ [c[i][j - 1], c[i - 1][j]].max
1292
+ end
1293
+ end
1294
+ end
1295
+ c
1296
+ end
1297
+ # rubocop:disable ParameterLists, LineLength
1298
+
1299
+ # Computes a single longest common subsequence for arrays x and y.
1300
+ # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
1301
+ def lcs_backtrace(c, x, y, i, j, &block)
1302
+ # rubocop:enable ParameterList, LineLengths
1303
+ return [] if i == 0 || j == 0
1304
+ if (v = yield(x[i], y[j]))
1305
+ return lcs_backtrace(c, x, y, i - 1, j - 1, &block) << v
1306
+ end
1307
+
1308
+ return lcs_backtrace(c, x, y, i, j - 1, &block) if c[i][j - 1] > c[i - 1][j]
1309
+ lcs_backtrace(c, x, y, i - 1, j, &block)
1310
+ end
1311
+
1312
+ singleton_methods.each {|method| module_function method}
1313
+ end
1314
+ end
1315
+
1316
+ require 'sass/util/multibyte_string_scanner'
1317
+ require 'sass/util/normalized_map'
1318
+ require 'sass/util/cross_platform_random'