sass 3.4.25 → 3.7.4

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 (237) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +1 -1
  3. data/CONTRIBUTING.md +3 -3
  4. data/README.md +17 -9
  5. data/VERSION +1 -1
  6. data/VERSION_DATE +1 -1
  7. data/VERSION_NAME +1 -1
  8. data/extra/sass-spec-ref.sh +9 -1
  9. data/lib/sass.rb +0 -7
  10. data/lib/sass/cache_stores/filesystem.rb +1 -1
  11. data/lib/sass/css.rb +1 -2
  12. data/lib/sass/engine.rb +39 -29
  13. data/lib/sass/environment.rb +26 -5
  14. data/lib/sass/error.rb +2 -2
  15. data/lib/sass/exec/base.rb +2 -13
  16. data/lib/sass/exec/sass_scss.rb +1 -5
  17. data/lib/sass/features.rb +1 -0
  18. data/lib/sass/importers/filesystem.rb +6 -4
  19. data/lib/sass/logger/base.rb +11 -0
  20. data/lib/sass/plugin/compiler.rb +20 -50
  21. data/lib/sass/plugin/configuration.rb +2 -2
  22. data/lib/sass/railtie.rb +1 -1
  23. data/lib/sass/script/css_parser.rb +4 -1
  24. data/lib/sass/script/functions.rb +308 -81
  25. data/lib/sass/script/lexer.rb +63 -9
  26. data/lib/sass/script/parser.rb +287 -118
  27. data/lib/sass/script/tree/funcall.rb +35 -34
  28. data/lib/sass/script/tree/interpolation.rb +0 -3
  29. data/lib/sass/script/tree/list_literal.rb +23 -8
  30. data/lib/sass/script/tree/map_literal.rb +2 -2
  31. data/lib/sass/script/tree/node.rb +0 -8
  32. data/lib/sass/script/tree/operation.rb +1 -8
  33. data/lib/sass/script/value.rb +2 -0
  34. data/lib/sass/script/value/arg_list.rb +1 -1
  35. data/lib/sass/script/value/base.rb +17 -0
  36. data/lib/sass/script/value/callable.rb +25 -0
  37. data/lib/sass/script/value/color.rb +8 -2
  38. data/lib/sass/script/value/function.rb +19 -0
  39. data/lib/sass/script/value/helpers.rb +37 -11
  40. data/lib/sass/script/value/list.rb +35 -14
  41. data/lib/sass/script/value/map.rb +2 -2
  42. data/lib/sass/script/value/number.rb +3 -2
  43. data/lib/sass/scss/css_parser.rb +6 -1
  44. data/lib/sass/scss/parser.rb +145 -56
  45. data/lib/sass/scss/rx.rb +5 -11
  46. data/lib/sass/scss/static_parser.rb +20 -42
  47. data/lib/sass/selector.rb +4 -0
  48. data/lib/sass/selector/abstract_sequence.rb +7 -6
  49. data/lib/sass/selector/comma_sequence.rb +9 -5
  50. data/lib/sass/selector/pseudo.rb +20 -3
  51. data/lib/sass/selector/sequence.rb +35 -10
  52. data/lib/sass/selector/simple.rb +9 -2
  53. data/lib/sass/selector/simple_sequence.rb +8 -4
  54. data/lib/sass/source/map.rb +2 -6
  55. data/lib/sass/stack.rb +21 -1
  56. data/lib/sass/tree/charset_node.rb +1 -1
  57. data/lib/sass/tree/prop_node.rb +45 -53
  58. data/lib/sass/tree/rule_node.rb +6 -8
  59. data/lib/sass/tree/visitors/check_nesting.rb +1 -1
  60. data/lib/sass/tree/visitors/convert.rb +2 -3
  61. data/lib/sass/tree/visitors/cssize.rb +4 -15
  62. data/lib/sass/tree/visitors/deep_copy.rb +1 -1
  63. data/lib/sass/tree/visitors/extend.rb +2 -8
  64. data/lib/sass/tree/visitors/perform.rb +23 -15
  65. data/lib/sass/tree/visitors/set_options.rb +1 -1
  66. data/lib/sass/tree/visitors/to_css.rb +49 -22
  67. data/lib/sass/util.rb +72 -310
  68. data/lib/sass/util/multibyte_string_scanner.rb +127 -131
  69. data/lib/sass/util/normalized_map.rb +1 -8
  70. data/lib/sass/version.rb +0 -4
  71. metadata +55 -202
  72. data/Rakefile +0 -453
  73. data/lib/sass/script/css_variable_warning.rb +0 -52
  74. data/lib/sass/util/cross_platform_random.rb +0 -19
  75. data/lib/sass/util/ordered_hash.rb +0 -192
  76. data/test/sass-spec.yml +0 -3
  77. data/test/sass/cache_test.rb +0 -131
  78. data/test/sass/callbacks_test.rb +0 -61
  79. data/test/sass/compiler_test.rb +0 -236
  80. data/test/sass/conversion_test.rb +0 -2188
  81. data/test/sass/css2sass_test.rb +0 -526
  82. data/test/sass/css_variable_test.rb +0 -132
  83. data/test/sass/data/hsl-rgb.txt +0 -319
  84. data/test/sass/encoding_test.rb +0 -219
  85. data/test/sass/engine_test.rb +0 -3447
  86. data/test/sass/exec_test.rb +0 -96
  87. data/test/sass/extend_test.rb +0 -1733
  88. data/test/sass/fixtures/test_staleness_check_across_importers.css +0 -1
  89. data/test/sass/fixtures/test_staleness_check_across_importers.scss +0 -1
  90. data/test/sass/functions_test.rb +0 -1977
  91. data/test/sass/importer_test.rb +0 -421
  92. data/test/sass/logger_test.rb +0 -58
  93. data/test/sass/mock_importer.rb +0 -49
  94. data/test/sass/more_results/more1.css +0 -9
  95. data/test/sass/more_results/more1_with_line_comments.css +0 -26
  96. data/test/sass/more_results/more_import.css +0 -29
  97. data/test/sass/more_templates/_more_partial.sass +0 -2
  98. data/test/sass/more_templates/more1.sass +0 -23
  99. data/test/sass/more_templates/more_import.sass +0 -11
  100. data/test/sass/plugin_test.rb +0 -556
  101. data/test/sass/results/alt.css +0 -4
  102. data/test/sass/results/basic.css +0 -9
  103. data/test/sass/results/cached_import_option.css +0 -3
  104. data/test/sass/results/compact.css +0 -5
  105. data/test/sass/results/complex.css +0 -86
  106. data/test/sass/results/compressed.css +0 -1
  107. data/test/sass/results/expanded.css +0 -19
  108. data/test/sass/results/filename_fn.css +0 -3
  109. data/test/sass/results/if.css +0 -3
  110. data/test/sass/results/import.css +0 -31
  111. data/test/sass/results/import_charset.css +0 -5
  112. data/test/sass/results/import_charset_1_8.css +0 -5
  113. data/test/sass/results/import_charset_ibm866.css +0 -5
  114. data/test/sass/results/import_content.css +0 -1
  115. data/test/sass/results/line_numbers.css +0 -49
  116. data/test/sass/results/mixins.css +0 -95
  117. data/test/sass/results/multiline.css +0 -24
  118. data/test/sass/results/nested.css +0 -22
  119. data/test/sass/results/options.css +0 -1
  120. data/test/sass/results/parent_ref.css +0 -13
  121. data/test/sass/results/script.css +0 -16
  122. data/test/sass/results/scss_import.css +0 -31
  123. data/test/sass/results/scss_importee.css +0 -2
  124. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +0 -1
  125. data/test/sass/results/subdir/subdir.css +0 -3
  126. data/test/sass/results/units.css +0 -11
  127. data/test/sass/results/warn.css +0 -0
  128. data/test/sass/results/warn_imported.css +0 -0
  129. data/test/sass/script_conversion_test.rb +0 -357
  130. data/test/sass/script_test.rb +0 -1431
  131. data/test/sass/scss/css_test.rb +0 -1281
  132. data/test/sass/scss/rx_test.rb +0 -160
  133. data/test/sass/scss/scss_test.rb +0 -4205
  134. data/test/sass/scss/test_helper.rb +0 -37
  135. data/test/sass/source_map_test.rb +0 -1055
  136. data/test/sass/superselector_test.rb +0 -210
  137. data/test/sass/templates/_cached_import_option_partial.scss +0 -1
  138. data/test/sass/templates/_double_import_loop2.sass +0 -1
  139. data/test/sass/templates/_filename_fn_import.scss +0 -11
  140. data/test/sass/templates/_imported_charset_ibm866.sass +0 -4
  141. data/test/sass/templates/_imported_charset_utf8.sass +0 -4
  142. data/test/sass/templates/_imported_content.sass +0 -3
  143. data/test/sass/templates/_partial.sass +0 -2
  144. data/test/sass/templates/_same_name_different_partiality.scss +0 -1
  145. data/test/sass/templates/alt.sass +0 -16
  146. data/test/sass/templates/basic.sass +0 -23
  147. data/test/sass/templates/bork1.sass +0 -2
  148. data/test/sass/templates/bork2.sass +0 -2
  149. data/test/sass/templates/bork3.sass +0 -2
  150. data/test/sass/templates/bork4.sass +0 -2
  151. data/test/sass/templates/bork5.sass +0 -3
  152. data/test/sass/templates/cached_import_option.scss +0 -3
  153. data/test/sass/templates/compact.sass +0 -17
  154. data/test/sass/templates/complex.sass +0 -305
  155. data/test/sass/templates/compressed.sass +0 -15
  156. data/test/sass/templates/double_import_loop1.sass +0 -1
  157. data/test/sass/templates/expanded.sass +0 -17
  158. data/test/sass/templates/filename_fn.scss +0 -18
  159. data/test/sass/templates/if.sass +0 -11
  160. data/test/sass/templates/import.sass +0 -12
  161. data/test/sass/templates/import_charset.sass +0 -9
  162. data/test/sass/templates/import_charset_1_8.sass +0 -6
  163. data/test/sass/templates/import_charset_ibm866.sass +0 -11
  164. data/test/sass/templates/import_content.sass +0 -4
  165. data/test/sass/templates/importee.less +0 -2
  166. data/test/sass/templates/importee.sass +0 -19
  167. data/test/sass/templates/line_numbers.sass +0 -13
  168. data/test/sass/templates/mixin_bork.sass +0 -5
  169. data/test/sass/templates/mixins.sass +0 -76
  170. data/test/sass/templates/multiline.sass +0 -20
  171. data/test/sass/templates/nested.sass +0 -25
  172. data/test/sass/templates/nested_bork1.sass +0 -2
  173. data/test/sass/templates/nested_bork2.sass +0 -2
  174. data/test/sass/templates/nested_bork3.sass +0 -2
  175. data/test/sass/templates/nested_bork4.sass +0 -2
  176. data/test/sass/templates/nested_import.sass +0 -2
  177. data/test/sass/templates/nested_mixin_bork.sass +0 -6
  178. data/test/sass/templates/options.sass +0 -2
  179. data/test/sass/templates/parent_ref.sass +0 -25
  180. data/test/sass/templates/same_name_different_ext.sass +0 -2
  181. data/test/sass/templates/same_name_different_ext.scss +0 -1
  182. data/test/sass/templates/same_name_different_partiality.scss +0 -1
  183. data/test/sass/templates/script.sass +0 -101
  184. data/test/sass/templates/scss_import.scss +0 -12
  185. data/test/sass/templates/scss_importee.scss +0 -1
  186. data/test/sass/templates/single_import_loop.sass +0 -1
  187. data/test/sass/templates/subdir/import_up1.scss +0 -1
  188. data/test/sass/templates/subdir/import_up2.scss +0 -1
  189. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +0 -2
  190. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +0 -3
  191. data/test/sass/templates/subdir/subdir.sass +0 -6
  192. data/test/sass/templates/units.sass +0 -11
  193. data/test/sass/templates/warn.sass +0 -3
  194. data/test/sass/templates/warn_imported.sass +0 -4
  195. data/test/sass/test_helper.rb +0 -8
  196. data/test/sass/util/multibyte_string_scanner_test.rb +0 -155
  197. data/test/sass/util/normalized_map_test.rb +0 -51
  198. data/test/sass/util/subset_map_test.rb +0 -91
  199. data/test/sass/util_test.rb +0 -438
  200. data/test/sass/value_helpers_test.rb +0 -179
  201. data/test/test_helper.rb +0 -110
  202. data/vendor/listen/CHANGELOG.md +0 -1
  203. data/vendor/listen/CONTRIBUTING.md +0 -38
  204. data/vendor/listen/Gemfile +0 -20
  205. data/vendor/listen/Guardfile +0 -8
  206. data/vendor/listen/LICENSE +0 -20
  207. data/vendor/listen/README.md +0 -349
  208. data/vendor/listen/Rakefile +0 -5
  209. data/vendor/listen/Vagrantfile +0 -96
  210. data/vendor/listen/lib/listen.rb +0 -54
  211. data/vendor/listen/lib/listen/adapter.rb +0 -327
  212. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -75
  213. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -48
  214. data/vendor/listen/lib/listen/adapters/linux.rb +0 -81
  215. data/vendor/listen/lib/listen/adapters/polling.rb +0 -58
  216. data/vendor/listen/lib/listen/adapters/windows.rb +0 -91
  217. data/vendor/listen/lib/listen/directory_record.rb +0 -406
  218. data/vendor/listen/lib/listen/listener.rb +0 -323
  219. data/vendor/listen/lib/listen/turnstile.rb +0 -32
  220. data/vendor/listen/lib/listen/version.rb +0 -3
  221. data/vendor/listen/listen.gemspec +0 -28
  222. data/vendor/listen/spec/listen/adapter_spec.rb +0 -149
  223. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  224. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  225. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  226. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  227. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  228. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1250
  229. data/vendor/listen/spec/listen/listener_spec.rb +0 -258
  230. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  231. data/vendor/listen/spec/listen_spec.rb +0 -67
  232. data/vendor/listen/spec/spec_helper.rb +0 -25
  233. data/vendor/listen/spec/support/adapter_helper.rb +0 -666
  234. data/vendor/listen/spec/support/directory_record_helper.rb +0 -57
  235. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  236. data/vendor/listen/spec/support/listeners_helper.rb +0 -179
  237. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -88,7 +88,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
88
88
 
89
89
  def visit_prop(node)
90
90
  node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
91
- node.value.options = @options
91
+ node.value.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
92
92
  yield
93
93
  end
94
94
 
@@ -135,7 +135,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
135
135
 
136
136
  output "\n"
137
137
 
138
- unless Sass::Util.ruby1_8? || @result.ascii_only?
138
+ unless @result.ascii_only?
139
139
  if node.style == :compressed
140
140
  # A byte order mark is sufficient to tell browsers that this
141
141
  # file is UTF-8 encoded, and will override any other detection
@@ -171,8 +171,6 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
171
171
  for_node(node) {output(content)}
172
172
  end
173
173
 
174
- # @comment
175
- # rubocop:disable MethodLength
176
174
  def visit_directive(node)
177
175
  was_in_directive = @in_directive
178
176
  tab_str = ' ' * @tabs
@@ -244,8 +242,6 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
244
242
  ensure
245
243
  @in_directive = was_in_directive
246
244
  end
247
- # @comment
248
- # rubocop:enable MethodLength
249
245
 
250
246
  def visit_media(node)
251
247
  with_tabs(@tabs + node.tabs) {visit_directive(node)}
@@ -261,22 +257,22 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
261
257
  end
262
258
 
263
259
  def visit_prop(node)
264
- return if node.resolved_value.empty?
260
+ return if node.resolved_value.empty? && !node.custom_property?
265
261
  tab_str = ' ' * (@tabs + node.tabs)
266
262
  output(tab_str)
267
263
  for_node(node, :name) {output(node.resolved_name)}
268
- if node.style == :compressed
269
- output(":")
270
- for_node(node, :value) {output(node.resolved_value)}
271
- else
272
- output(": ")
273
- for_node(node, :value) {output(node.resolved_value)}
274
- output(";")
264
+ output(":")
265
+ output(" ") unless node.style == :compressed || node.custom_property?
266
+ for_node(node, :value) do
267
+ output(if node.custom_property?
268
+ format_custom_property_value(node)
269
+ else
270
+ node.resolved_value
271
+ end)
275
272
  end
273
+ output(";") unless node.style == :compressed
276
274
  end
277
275
 
278
- # @comment
279
- # rubocop:disable MethodLength
280
276
  def visit_rule(node)
281
277
  with_tabs(@tabs + node.tabs) do
282
278
  rule_separator = node.style == :compressed ? ',' : ', '
@@ -294,15 +290,15 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
294
290
  end
295
291
 
296
292
  joined_rules = node.resolved_rules.members.map do |seq|
297
- next if seq.has_placeholder?
298
- rule_part = seq.to_s(:style => node.style)
293
+ next if seq.invisible?
294
+ rule_part = seq.to_s(style: node.style, placeholder: false)
299
295
  if node.style == :compressed
300
296
  rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
301
297
  rule_part.gsub!(/\s*([+>])\s*/m, '\1')
302
298
  rule_part.gsub!(/nth([^( ]*)\(([^)]*)\)/m) do |match|
303
299
  match.tr(" \t\n", "")
304
300
  end
305
- rule_part.strip!
301
+ rule_part = Sass::Util.strip_except_escapes(rule_part)
306
302
  end
307
303
  rule_part
308
304
  end.compact.join(rule_separator)
@@ -375,8 +371,6 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
375
371
  output("}" + trailer)
376
372
  end
377
373
  end
378
- # @comment
379
- # rubocop:enable MethodLength
380
374
 
381
375
  def visit_keyframerule(node)
382
376
  visit_directive(node)
@@ -384,9 +378,42 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
384
378
 
385
379
  private
386
380
 
381
+ # Reformats the value of `node` so that it's nicely indented, preserving its
382
+ # existing relative indentation.
383
+ #
384
+ # @param node [Sass::Script::Tree::PropNode] A custom property node.
385
+ # @return [String]
386
+ def format_custom_property_value(node)
387
+ value = node.resolved_value.sub(/\n[ \t\r\f\n]*\Z/, ' ')
388
+ if node.style == :compact || node.style == :compressed || !value.include?("\n")
389
+ # Folding not involving newlines was done in the parser. We can safely
390
+ # fold newlines here because tokens like strings can't contain literal
391
+ # newlines, so we know any adjacent whitespace is tokenized as whitespace.
392
+ return node.resolved_value.gsub(/[ \t\r\f]*\n[ \t\r\f\n]*/, ' ')
393
+ end
394
+
395
+ # Find the smallest amount of indentation in the custom property and use
396
+ # that as the base indentation level.
397
+ lines = value.split("\n")
398
+ indented_lines = lines[1..-1]
399
+ min_indentation = indented_lines.
400
+ map {|line| line[/^[ \t]*/]}.
401
+ reject {|line| line.empty?}.
402
+ min_by {|line| line.length}
403
+
404
+ # Limit the base indentation to the same indentation level as the node name
405
+ # so that if *every* line is indented relative to the property name that's
406
+ # preserved.
407
+ if node.name_source_range
408
+ base_indentation = min_indentation[0...node.name_source_range.start_pos.offset - 1]
409
+ end
410
+
411
+ lines.first + "\n" + indented_lines.join("\n").gsub(/^#{base_indentation}/, ' ' * @tabs)
412
+ end
413
+
387
414
  def debug_info_rule(debug_info, options)
388
415
  node = Sass::Tree::DirectiveNode.resolved("@media -sass-debug-info")
389
- Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
416
+ debug_info.map {|k, v| [k.to_s, v.to_s]}.to_a.each do |k, v|
390
417
  rule = Sass::Tree::RuleNode.new([""])
391
418
  rule.resolved_rules = Sass::Selector::CommaSequence.new(
392
419
  [Sass::Selector::Sequence.new(
@@ -395,7 +422,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
395
422
  false)
396
423
  ])
397
424
  ])
398
- prop = Sass::Tree::PropNode.new([""], Sass::Script::Value::String.new(''), :new)
425
+ prop = Sass::Tree::PropNode.new([""], [""], :new)
399
426
  prop.resolved_name = "font-family"
400
427
  prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
401
428
  rule << prop
@@ -13,8 +13,6 @@ require 'sass/util/subset_map'
13
13
 
14
14
  module Sass
15
15
  # A module containing various useful functions.
16
- # @comment
17
- # rubocop:disable ModuleLength
18
16
  module Util
19
17
  extend self
20
18
 
@@ -35,17 +33,6 @@ module Sass
35
33
  File.join(Sass::ROOT_DIR, file)
36
34
  end
37
35
 
38
- # Converts an array of `[key, value]` pairs to a hash.
39
- #
40
- # @example
41
- # to_hash([[:foo, "bar"], [:baz, "bang"]])
42
- # #=> {:foo => "bar", :baz => "bang"}
43
- # @param arr [Array<(Object, Object)>] An array of pairs
44
- # @return [Hash] A hash
45
- def to_hash(arr)
46
- ordered_hash(*arr.compact)
47
- end
48
-
49
36
  # Maps the keys in a hash according to a block.
50
37
  #
51
38
  # @example
@@ -148,8 +135,13 @@ module Sass
148
135
  def round(value)
149
136
  # If the number is within epsilon of X.5, round up (or down for negative
150
137
  # numbers).
151
- return value.round if (value % 1) - 0.5 <= -1 * Script::Value::Number.epsilon
152
- value > 0 ? value.ceil : value.floor
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
153
145
  end
154
146
 
155
147
  # Concatenates all strings that are adjacent in an array,
@@ -254,18 +246,67 @@ module Sass
254
246
  res
255
247
  end
256
248
 
257
- # Destructively strips whitespace from the beginning and end
258
- # of the first and last elements, respectively,
259
- # in the array (if those elements are strings).
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.
260
252
  #
261
253
  # @param arr [Array]
262
254
  # @return [Array] `arr`
263
255
  def strip_string_array(arr)
264
256
  arr.first.lstrip! if arr.first.is_a?(String)
265
- arr.last.rstrip! if arr.last.is_a?(String)
257
+ arr[-1] = Sass::Util.rstrip_except_escapes(arr[-1]) if arr.last.is_a?(String)
266
258
  arr
267
259
  end
268
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
+
269
310
  # Return an array of all possible paths through the given arrays.
270
311
  #
271
312
  # @param arrs [Array<Array>]
@@ -301,44 +342,6 @@ module Sass
301
342
  lcs_backtrace(lcs_table(x, y, &block), x, y, x.size - 1, y.size - 1, &block)
302
343
  end
303
344
 
304
- # Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
305
- # with the following exceptions:
306
- #
307
- # * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
308
- # * In Ruby 1.9 when running tests, this is ordered in the same way it would
309
- # be under Ruby 1.8 (sorted key order rather than insertion order).
310
- #
311
- # @param hash [Hash]
312
- # @return [Array]
313
- def hash_to_a(hash)
314
- return hash.to_a unless ruby1_8? || defined?(Test::Unit)
315
- hash.sort_by {|k, _v| k}
316
- end
317
-
318
- # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
319
- # order. Unlike {Util#hash_to_a}, the resulting order isn't sorted key order;
320
- # instead, it's the same order as `#group_by` has under Ruby 1.9 (key
321
- # appearance order).
322
- #
323
- # @param enum [Enumerable]
324
- # @return [Array<[Object, Array]>] An array of pairs.
325
- def group_by_to_a(enum)
326
- return enum.group_by {|e| yield(e)}.to_a unless ruby1_8?
327
- order = {}
328
- arr = []
329
- groups = enum.group_by do |e|
330
- res = yield(e)
331
- unless order.include?(res)
332
- order[res] = order.size
333
- end
334
- res
335
- end
336
- groups.each do |key, vals|
337
- arr[order[key]] = [key, vals]
338
- end
339
- arr
340
- end
341
-
342
345
  # Like `String.upcase`, but only ever upcases ASCII letters.
343
346
  def upcase(string)
344
347
  return string.upcase unless ruby2_4?
@@ -500,16 +503,6 @@ module Sass
500
503
  Sass::Util.sass_warn full_message
501
504
  end
502
505
 
503
- # Silence all output to STDERR within a block.
504
- #
505
- # @yield A block in which no output will be printed to STDERR
506
- def silence_warnings
507
- the_real_stderr, $stderr = $stderr, StringIO.new
508
- yield
509
- ensure
510
- $stderr = the_real_stderr
511
- end
512
-
513
506
  # Silences all Sass warnings within a block.
514
507
  #
515
508
  # @yield A block in which no Sass warnings will be printed
@@ -524,8 +517,7 @@ module Sass
524
517
  #
525
518
  # @param msg [String]
526
519
  def sass_warn(msg)
527
- msg += "\n" unless ruby1?
528
- Sass.logger.warn(msg)
520
+ Sass.logger.warn("#{msg}\n")
529
521
  end
530
522
 
531
523
  ## Cross Rails Version Compatibility
@@ -578,24 +570,6 @@ module Sass
578
570
  version_geq(ActionPack::VERSION::STRING, version)
579
571
  end
580
572
 
581
- # Returns whether this environment is using Listen
582
- # version 2.0.0 or greater.
583
- #
584
- # @return [Boolean]
585
- def listen_geq_2?
586
- return @listen_geq_2 if defined?(@listen_geq_2)
587
- @listen_geq_2 =
588
- begin
589
- # Make sure we're loading listen/version from the same place that
590
- # we're loading listen itself.
591
- load_listen!
592
- require 'listen/version'
593
- version_geq(::Listen::VERSION, '2.0.0')
594
- rescue LoadError
595
- false
596
- end
597
- end
598
-
599
573
  # Returns an ActionView::Template* class.
600
574
  # In pre-3.0 versions of Rails, most of these classes
601
575
  # were of the form `ActionView::TemplateFoo`,
@@ -739,7 +713,7 @@ module Sass
739
713
  def file_uri_from_path(path)
740
714
  path = path.to_s if path.is_a?(Pathname)
741
715
  path = path.tr('\\', '/') if windows?
742
- path = Sass::Util.escape_uri(path)
716
+ path = URI::DEFAULT_PARSER.escape(path)
743
717
  return path.start_with?('/') ? "file://" + path : path unless windows?
744
718
  return "file:///" + path.tr("\\", "/") if path =~ %r{^[a-zA-Z]:[/\\]}
745
719
  return "file:" + path.tr("\\", "/") if path =~ %r{\\\\[^\\]+\\[^\\/]+}
@@ -774,37 +748,13 @@ module Sass
774
748
  val || []
775
749
  end
776
750
 
777
- ## Cross-Ruby-Version Compatibility
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')
778
756
 
779
- # Whether or not this is running under a Ruby version under 2.0.
780
- #
781
- # @return [Boolean]
782
- def ruby1?
783
- return @ruby1 if defined?(@ruby1)
784
- @ruby1 = RUBY_VERSION_COMPONENTS[0] <= 1
785
- end
786
-
787
- # Whether or not this is running under Ruby 1.8 or lower.
788
- #
789
- # Note that IronRuby counts as Ruby 1.8,
790
- # because it doesn't support the Ruby 1.9 encoding API.
791
- #
792
- # @return [Boolean]
793
- def ruby1_8?
794
- # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
795
- # We have to fall back to 1.8 behavior.
796
- return @ruby1_8 if defined?(@ruby1_8)
797
- @ruby1_8 = ironruby? ||
798
- (RUBY_VERSION_COMPONENTS[0] == 1 && RUBY_VERSION_COMPONENTS[1] < 9)
799
- end
800
-
801
- # Whether or not this is running under Ruby 1.9.2 exactly.
802
- #
803
- # @return [Boolean]
804
- def ruby1_9_2?
805
- return @ruby1_9_2 if defined?(@ruby1_9_2)
806
- @ruby1_9_2 = RUBY_VERSION_COMPONENTS == [1, 9, 2]
807
- end
757
+ ## Cross-Ruby-Version Compatibility
808
758
 
809
759
  # Whether or not this is running under Ruby 2.4 or higher.
810
760
  #
@@ -819,58 +769,6 @@ module Sass
819
769
  end
820
770
  end
821
771
 
822
- # Wehter or not this is running under JRuby 1.6 or lower.
823
- def jruby1_6?
824
- return @jruby1_6 if defined?(@jruby1_6)
825
- @jruby1_6 = jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
826
- end
827
-
828
- # Whether or not this is running under MacRuby.
829
- #
830
- # @return [Boolean]
831
- def macruby?
832
- return @macruby if defined?(@macruby)
833
- @macruby = RUBY_ENGINE == 'macruby'
834
- end
835
-
836
- require 'sass/util/ordered_hash' if ruby1_8?
837
-
838
- # Converts a hash or a list of pairs into an order-preserving hash.
839
- #
840
- # On Ruby 1.8.7, this uses the orderedhash gem to simulate an
841
- # order-preserving hash. On Ruby 1.9 and up, it just uses the native Hash
842
- # class, since that preserves the order itself.
843
- #
844
- # @overload ordered_hash(hash)
845
- # @param hash [Hash] a normal hash to convert to an ordered hash
846
- # @return [Hash]
847
- # @overload ordered_hash(*pairs)
848
- # @example
849
- # ordered_hash([:foo, "bar"], [:baz, "bang"])
850
- # #=> {:foo => "bar", :baz => "bang"}
851
- # ordered_hash #=> {}
852
- # @param pairs [Array<(Object, Object)>] the list of key/value pairs for
853
- # the hash.
854
- # @return [Hash]
855
- def ordered_hash(*pairs_or_hash)
856
- if pairs_or_hash.length == 1 && pairs_or_hash.first.is_a?(Hash)
857
- hash = pairs_or_hash.first
858
- return hash unless ruby1_8?
859
- return OrderedHash.new.merge hash
860
- end
861
-
862
- return Hash[pairs_or_hash] unless ruby1_8?
863
- (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*pairs_or_hash.flatten(1)]
864
- end
865
-
866
- unless ruby1_8?
867
- CHARSET_REGEXP = /\A@charset "([^"]+)"/
868
- bom = "\uFEFF"
869
- UTF_8_BOM = bom.encode("UTF-8").force_encoding('BINARY')
870
- UTF_16BE_BOM = bom.encode("UTF-16BE").force_encoding('BINARY')
871
- UTF_16LE_BOM = bom.encode("UTF-16LE").force_encoding('BINARY')
872
- end
873
-
874
772
  # Like {\#check\_encoding}, but also checks for a `@charset` declaration
875
773
  # at the beginning of the file and uses that encoding if it exists.
876
774
  #
@@ -878,7 +776,7 @@ module Sass
878
776
  #
879
777
  # @param str [String] The string of which to check the encoding
880
778
  # @return [(String, Encoding)] The original string encoded as UTF-8,
881
- # and the source encoding of the string (or `nil` under Ruby 1.8)
779
+ # and the source encoding of the string
882
780
  # @raise [Encoding::UndefinedConversionError] if the source encoding
883
781
  # cannot be converted to UTF-8
884
782
  # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
@@ -886,15 +784,6 @@ module Sass
886
784
  # doesn't match its contents, or it doesn't declare an encoding and its
887
785
  # contents are invalid in the native encoding.
888
786
  def check_sass_encoding(str)
889
- # On Ruby 1.8 we can't do anything complicated with encodings.
890
- # Instead, we just strip out a UTF-8 BOM if it exists and
891
- # sanitize according to Section 3.3 of CSS Syntax Level 3. We
892
- # don't sanitize null characters since they might be components
893
- # of other characters.
894
- if ruby1_8?
895
- return str.gsub(/\A\xEF\xBB\xBF/, '').gsub(/\r\n?|\f/, "\n"), nil
896
- end
897
-
898
787
  # Determine the fallback encoding following section 3.2 of CSS Syntax Level 3 and Encodings:
899
788
  # http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#determine-the-fallback-encoding
900
789
  # http://encoding.spec.whatwg.org/#decode
@@ -910,14 +799,9 @@ module Sass
910
799
  str = binary.force_encoding('UTF-16LE')
911
800
  elsif binary =~ CHARSET_REGEXP
912
801
  charset = $1.force_encoding('US-ASCII')
913
- # Ruby 1.9.2 doesn't recognize a UTF-16 encoding without an endian marker.
914
- if ruby1_9_2? && charset.downcase == 'utf-16'
802
+ encoding = Encoding.find(charset)
803
+ if encoding.name == 'UTF-16' || encoding.name == 'UTF-16BE'
915
804
  encoding = Encoding.find('UTF-8')
916
- else
917
- encoding = Encoding.find(charset)
918
- if encoding.name == 'UTF-16' || encoding.name == 'UTF-16BE'
919
- encoding = Encoding.find('UTF-8')
920
- end
921
805
  end
922
806
  str = binary.force_encoding(encoding)
923
807
  elsif str.encoding.name == "ASCII-8BIT"
@@ -937,50 +821,6 @@ module Sass
937
821
  end
938
822
  end
939
823
 
940
- # Checks to see if a class has a given method.
941
- # For example:
942
- #
943
- # Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
944
- #
945
- # Method collections like `Class#instance_methods`
946
- # return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
947
- # so this handles checking for them in a compatible way.
948
- #
949
- # @param attr [#to_s] The (singular) name of the method-collection method
950
- # (e.g. `:instance_methods`, `:private_methods`)
951
- # @param klass [Module] The class to check the methods of which to check
952
- # @param method [String, Symbol] The name of the method do check for
953
- # @return [Boolean] Whether or not the given collection has the given method
954
- def has?(attr, klass, method)
955
- klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
956
- end
957
-
958
- # A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
959
- #
960
- # @param enum [Enumerable] The enumerable to get the enumerator for
961
- # @return [Enumerator] The with-index enumerator
962
- def enum_with_index(enum)
963
- ruby1_8? ? enum.enum_with_index : enum.each_with_index
964
- end
965
-
966
- # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
967
- #
968
- # @param enum [Enumerable] The enumerable to get the enumerator for
969
- # @param n [Integer] The size of each cons
970
- # @return [Enumerator] The consed enumerator
971
- def enum_cons(enum, n)
972
- ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
973
- end
974
-
975
- # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
976
- #
977
- # @param enum [Enumerable] The enumerable to get the enumerator for
978
- # @param n [Integer] The size of each slice
979
- # @return [Enumerator] The consed enumerator
980
- def enum_slice(enum, n)
981
- ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
982
- end
983
-
984
824
  # Destructively removes all elements from an array that match a block, and
985
825
  # returns the removed elements.
986
826
  #
@@ -999,14 +839,6 @@ module Sass
999
839
  out
1000
840
  end
1001
841
 
1002
- # Returns the ASCII code of the given character.
1003
- #
1004
- # @param c [String] All characters but the first are ignored.
1005
- # @return [Integer] The ASCII code of `c`.
1006
- def ord(c)
1007
- ruby1_8? ? c[0] : c.ord
1008
- end
1009
-
1010
842
  # Flattens the first level of nested arrays in `arrs`. Unlike
1011
843
  # `Array#flatten`, this orders the result by taking the first
1012
844
  # values from each array in order, then the second, and so on.
@@ -1156,7 +988,7 @@ module Sass
1156
988
  BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1157
989
  BASE64_DIGIT_MAP = begin
1158
990
  map = {}
1159
- Sass::Util.enum_with_index(BASE64_DIGITS).map do |digit, i|
991
+ BASE64_DIGITS.each_with_index.map do |digit, i|
1160
992
  map[digit] = i
1161
993
  end
1162
994
  map
@@ -1185,35 +1017,6 @@ module Sass
1185
1017
  result
1186
1018
  end
1187
1019
 
1188
- # This is a hack around the fact that you can't instantiate a URI parser on
1189
- # 1.8, so we have to have this hacky stuff to work around it. When 1.8
1190
- # support is dropped, we can remove this method.
1191
- #
1192
- # @private
1193
- URI_ESCAPE = URI.const_defined?("DEFAULT_PARSER") ? URI::DEFAULT_PARSER : URI
1194
-
1195
- # URI-escape `string`.
1196
- #
1197
- # @param string [String]
1198
- # @return [String]
1199
- def escape_uri(string)
1200
- URI_ESCAPE.escape string
1201
- end
1202
-
1203
- # A cross-platform implementation of `File.absolute_path`.
1204
- #
1205
- # @param path [String]
1206
- # @param dir_string [String] The directory to consider [path] relative to.
1207
- # @return [String] The absolute version of `path`.
1208
- def absolute_path(path, dir_string = nil)
1209
- # Ruby 1.8 doesn't support File.absolute_path.
1210
- return File.absolute_path(path, dir_string) unless ruby1_8?
1211
-
1212
- # File.expand_path expands "~", which we don't want.
1213
- return File.expand_path(path, dir_string) unless path[0] == ?~
1214
- File.expand_path(File.join(".", path), dir_string)
1215
- end
1216
-
1217
1020
  ## Static Method Stuff
1218
1021
 
1219
1022
  # The context in which the ERB for \{#def\_static\_method} will be run.
@@ -1271,44 +1074,6 @@ module Sass
1271
1074
  tmpfile.unlink if tmpfile
1272
1075
  end
1273
1076
 
1274
- def load_listen!
1275
- if defined?(gem)
1276
- begin
1277
- gem 'listen', '>= 1.1.0', '< 3.0.0'
1278
- require 'listen'
1279
- rescue Gem::LoadError
1280
- dir = scope("vendor/listen/lib")
1281
- $LOAD_PATH.unshift dir
1282
- begin
1283
- require 'listen'
1284
- rescue LoadError => e
1285
- if version_geq(RUBY_VERSION, "1.9.3")
1286
- version_constraint = "~> 3.0"
1287
- else
1288
- version_constraint = "~> 1.1"
1289
- end
1290
- e.message << "\n" <<
1291
- "Run \"gem install listen --version '#{version_constraint}'\" to get it."
1292
- raise e
1293
- end
1294
- end
1295
- else
1296
- begin
1297
- require 'listen'
1298
- rescue LoadError => e
1299
- dir = scope("vendor/listen/lib")
1300
- if $LOAD_PATH.include?(dir)
1301
- raise e unless File.exist?(scope(".git"))
1302
- e.message << "\n" <<
1303
- 'Run "git submodule update --init" to get the bundled version.'
1304
- else
1305
- $LOAD_PATH.unshift dir
1306
- retry
1307
- end
1308
- end
1309
- end
1310
- end
1311
-
1312
1077
  private
1313
1078
 
1314
1079
  def find_encoding_error(str)
@@ -1351,12 +1116,10 @@ module Sass
1351
1116
  end
1352
1117
  c
1353
1118
  end
1354
- # rubocop:disable ParameterLists
1355
1119
 
1356
1120
  # Computes a single longest common subsequence for arrays x and y.
1357
1121
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
1358
1122
  def lcs_backtrace(c, x, y, i, j, &block)
1359
- # rubocop:enable ParameterList
1360
1123
  return [] if i == 0 || j == 0
1361
1124
  if (v = yield(x[i], y[j]))
1362
1125
  return lcs_backtrace(c, x, y, i - 1, j - 1, &block) << v
@@ -1372,4 +1135,3 @@ end
1372
1135
 
1373
1136
  require 'sass/util/multibyte_string_scanner'
1374
1137
  require 'sass/util/normalized_map'
1375
- require 'sass/util/cross_platform_random'