sass 3.4.0 → 3.4.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +26 -20
  7. data/Rakefile +103 -20
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/extra/sass-spec-ref.sh +32 -0
  11. data/extra/update_watch.rb +1 -1
  12. data/lib/sass/cache_stores/filesystem.rb +7 -7
  13. data/lib/sass/cache_stores/memory.rb +4 -5
  14. data/lib/sass/callbacks.rb +2 -2
  15. data/lib/sass/css.rb +11 -10
  16. data/lib/sass/deprecation.rb +55 -0
  17. data/lib/sass/engine.rb +83 -38
  18. data/lib/sass/environment.rb +26 -2
  19. data/lib/sass/error.rb +12 -12
  20. data/lib/sass/exec/base.rb +15 -3
  21. data/lib/sass/exec/sass_convert.rb +34 -15
  22. data/lib/sass/exec/sass_scss.rb +23 -7
  23. data/lib/sass/features.rb +2 -2
  24. data/lib/sass/importers/base.rb +1 -1
  25. data/lib/sass/importers/deprecated_path.rb +51 -0
  26. data/lib/sass/importers/filesystem.rb +24 -16
  27. data/lib/sass/importers.rb +1 -0
  28. data/lib/sass/logger/base.rb +8 -2
  29. data/lib/sass/logger/delayed.rb +50 -0
  30. data/lib/sass/logger.rb +8 -3
  31. data/lib/sass/plugin/compiler.rb +42 -25
  32. data/lib/sass/plugin/configuration.rb +38 -22
  33. data/lib/sass/plugin/merb.rb +2 -2
  34. data/lib/sass/plugin/rack.rb +3 -3
  35. data/lib/sass/plugin/rails.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +3 -3
  37. data/lib/sass/plugin.rb +3 -2
  38. data/lib/sass/script/css_parser.rb +2 -3
  39. data/lib/sass/script/css_variable_warning.rb +52 -0
  40. data/lib/sass/script/functions.rb +140 -73
  41. data/lib/sass/script/lexer.rb +37 -22
  42. data/lib/sass/script/parser.rb +235 -40
  43. data/lib/sass/script/tree/funcall.rb +12 -5
  44. data/lib/sass/script/tree/interpolation.rb +109 -4
  45. data/lib/sass/script/tree/list_literal.rb +31 -4
  46. data/lib/sass/script/tree/literal.rb +4 -0
  47. data/lib/sass/script/tree/node.rb +21 -3
  48. data/lib/sass/script/tree/operation.rb +54 -1
  49. data/lib/sass/script/tree/string_interpolation.rb +58 -37
  50. data/lib/sass/script/tree/variable.rb +1 -1
  51. data/lib/sass/script/value/base.rb +10 -9
  52. data/lib/sass/script/value/color.rb +42 -24
  53. data/lib/sass/script/value/helpers.rb +16 -6
  54. data/lib/sass/script/value/map.rb +1 -1
  55. data/lib/sass/script/value/number.rb +52 -19
  56. data/lib/sass/script/value/string.rb +46 -5
  57. data/lib/sass/script.rb +3 -3
  58. data/lib/sass/scss/css_parser.rb +16 -2
  59. data/lib/sass/scss/parser.rb +120 -75
  60. data/lib/sass/scss/rx.rb +9 -10
  61. data/lib/sass/scss/static_parser.rb +19 -14
  62. data/lib/sass/scss.rb +0 -2
  63. data/lib/sass/selector/abstract_sequence.rb +8 -6
  64. data/lib/sass/selector/comma_sequence.rb +25 -9
  65. data/lib/sass/selector/pseudo.rb +45 -35
  66. data/lib/sass/selector/sequence.rb +54 -18
  67. data/lib/sass/selector/simple.rb +11 -11
  68. data/lib/sass/selector/simple_sequence.rb +34 -15
  69. data/lib/sass/selector.rb +7 -10
  70. data/lib/sass/shared.rb +1 -1
  71. data/lib/sass/source/map.rb +7 -4
  72. data/lib/sass/source/position.rb +4 -4
  73. data/lib/sass/stack.rb +2 -2
  74. data/lib/sass/supports.rb +8 -10
  75. data/lib/sass/tree/comment_node.rb +1 -1
  76. data/lib/sass/tree/css_import_node.rb +9 -1
  77. data/lib/sass/tree/function_node.rb +8 -3
  78. data/lib/sass/tree/import_node.rb +6 -5
  79. data/lib/sass/tree/node.rb +5 -3
  80. data/lib/sass/tree/prop_node.rb +5 -6
  81. data/lib/sass/tree/rule_node.rb +14 -4
  82. data/lib/sass/tree/visitors/check_nesting.rb +18 -22
  83. data/lib/sass/tree/visitors/convert.rb +43 -26
  84. data/lib/sass/tree/visitors/cssize.rb +5 -1
  85. data/lib/sass/tree/visitors/deep_copy.rb +1 -1
  86. data/lib/sass/tree/visitors/extend.rb +15 -13
  87. data/lib/sass/tree/visitors/perform.rb +42 -17
  88. data/lib/sass/tree/visitors/set_options.rb +1 -1
  89. data/lib/sass/tree/visitors/to_css.rb +58 -30
  90. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  91. data/lib/sass/util/normalized_map.rb +0 -1
  92. data/lib/sass/util/subset_map.rb +1 -2
  93. data/lib/sass/util.rb +125 -68
  94. data/lib/sass/version.rb +2 -2
  95. data/lib/sass.rb +10 -3
  96. data/test/sass/compiler_test.rb +6 -2
  97. data/test/sass/conversion_test.rb +187 -53
  98. data/test/sass/css2sass_test.rb +50 -1
  99. data/test/sass/css_variable_test.rb +132 -0
  100. data/test/sass/engine_test.rb +207 -61
  101. data/test/sass/exec_test.rb +10 -0
  102. data/test/sass/extend_test.rb +101 -29
  103. data/test/sass/functions_test.rb +60 -9
  104. data/test/sass/importer_test.rb +9 -0
  105. data/test/sass/more_templates/more1.sass +10 -10
  106. data/test/sass/more_templates/more_import.sass +2 -2
  107. data/test/sass/plugin_test.rb +10 -8
  108. data/test/sass/results/script.css +3 -3
  109. data/test/sass/script_conversion_test.rb +58 -29
  110. data/test/sass/script_test.rb +430 -53
  111. data/test/sass/scss/css_test.rb +73 -7
  112. data/test/sass/scss/rx_test.rb +4 -0
  113. data/test/sass/scss/scss_test.rb +309 -4
  114. data/test/sass/source_map_test.rb +152 -74
  115. data/test/sass/superselector_test.rb +19 -0
  116. data/test/sass/templates/_partial.sass +1 -1
  117. data/test/sass/templates/basic.sass +10 -10
  118. data/test/sass/templates/bork1.sass +1 -1
  119. data/test/sass/templates/bork5.sass +1 -1
  120. data/test/sass/templates/compact.sass +10 -10
  121. data/test/sass/templates/complex.sass +187 -187
  122. data/test/sass/templates/compressed.sass +10 -10
  123. data/test/sass/templates/expanded.sass +10 -10
  124. data/test/sass/templates/import.sass +2 -2
  125. data/test/sass/templates/importee.sass +3 -3
  126. data/test/sass/templates/mixins.sass +22 -22
  127. data/test/sass/templates/multiline.sass +4 -4
  128. data/test/sass/templates/nested.sass +13 -13
  129. data/test/sass/templates/parent_ref.sass +12 -12
  130. data/test/sass/templates/script.sass +70 -70
  131. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  132. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  133. data/test/sass/templates/subdir/subdir.sass +3 -3
  134. data/test/sass/templates/units.sass +10 -10
  135. data/test/sass/util/multibyte_string_scanner_test.rb +10 -2
  136. data/test/sass/util_test.rb +15 -44
  137. data/test/sass-spec.yml +3 -0
  138. data/test/test_helper.rb +5 -4
  139. metadata +302 -295
  140. data/CONTRIBUTING +0 -3
  141. data/lib/sass/scss/script_lexer.rb +0 -15
  142. data/lib/sass/scss/script_parser.rb +0 -25
@@ -12,8 +12,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
12
12
  @tabs = 0
13
13
  @line = 1
14
14
  @offset = 1
15
- @result = ""
16
- @source_mapping = Sass::Source::Map.new if build_source_mapping
15
+ @result = String.new("")
16
+ @source_mapping = build_source_mapping ? Sass::Source::Map.new : nil
17
+ @lstrip = nil
18
+ @in_directive = false
17
19
  end
18
20
 
19
21
  # Runs the visitor on `node`.
@@ -51,6 +53,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
51
53
  @source_mapping.add(source_range, target_range)
52
54
  end
53
55
 
56
+ def trailing_semicolon?
57
+ @result.end_with?(";") && !@result.end_with?('\;')
58
+ end
59
+
54
60
  # Move the output cursor back `chars` characters.
55
61
  def erase!(chars)
56
62
  return if chars == 0
@@ -98,7 +104,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
98
104
  @lstrip = true
99
105
  yield
100
106
  ensure
101
- @lstrip = @lstrip && old_lstrip
107
+ @lstrip &&= old_lstrip
102
108
  end
103
109
 
104
110
  # Prepend `prefix` to the output string.
@@ -116,14 +122,15 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
116
122
  node.children.each do |child|
117
123
  next if child.invisible?
118
124
  visit(child)
119
- unless node.style == :compressed
120
- output "\n"
121
- if child.is_a?(Sass::Tree::DirectiveNode) && child.has_children && !child.bubbles?
122
- output "\n"
123
- end
124
- end
125
+ next if node.style == :compressed
126
+ output "\n"
127
+ next unless child.is_a?(Sass::Tree::DirectiveNode) && child.has_children && !child.bubbles?
128
+ output "\n"
125
129
  end
126
130
  rstrip!
131
+ if node.style == :compressed && trailing_semicolon?
132
+ erase! 1
133
+ end
127
134
  return "" if @result.empty?
128
135
 
129
136
  output "\n"
@@ -152,13 +159,14 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
152
159
  def visit_comment(node)
153
160
  return if node.invisible?
154
161
  spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max)
162
+ output(spaces)
155
163
 
156
- content = node.resolved_value.gsub(/^/, spaces)
164
+ content = node.resolved_value.split("\n").join("\n" + spaces)
157
165
  if node.type == :silent
158
- content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"}
166
+ content.gsub!(%r{^(\s*)//(.*)$}) {"#{$1}/*#{$2} */"}
159
167
  end
160
168
  if (node.style == :compact || node.style == :compressed) && node.type != :loud
161
- content.gsub!(/\n +(\* *(?!\/))?/, ' ')
169
+ content.gsub!(%r{\n +(\* *(?!/))?}, ' ')
162
170
  end
163
171
  for_node(node) {output(content)}
164
172
  end
@@ -171,28 +179,32 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
171
179
  if !node.has_children || node.children.empty?
172
180
  output(tab_str)
173
181
  for_node(node) {output(node.resolved_value)}
174
- output(!node.has_children ? ";" : " {}")
182
+ if node.has_children
183
+ output("#{' ' unless node.style == :compressed}{}")
184
+ elsif node.children.empty?
185
+ output(";")
186
+ end
175
187
  return
176
188
  end
177
189
 
178
- @in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
190
+ @in_directive ||= !node.is_a?(Sass::Tree::MediaNode)
179
191
  output(tab_str) if node.style != :compressed
180
192
  for_node(node) {output(node.resolved_value)}
181
193
  output(node.style == :compressed ? "{" : " {")
182
194
  output(node.style == :compact ? ' ' : "\n") if node.style != :compressed
183
195
 
184
- was_prop = false
196
+ had_children = true
185
197
  first = true
186
198
  node.children.each do |child|
187
199
  next if child.invisible?
188
200
  if node.style == :compact
189
201
  if child.is_a?(Sass::Tree::PropNode)
190
- with_tabs(first || was_prop ? 0 : @tabs + 1) do
202
+ with_tabs(first || !had_children ? 0 : @tabs + 1) do
191
203
  visit(child)
192
204
  output(' ')
193
205
  end
194
206
  else
195
- if was_prop
207
+ unless had_children
196
208
  erase! 1
197
209
  output "\n"
198
210
  end
@@ -206,18 +218,23 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
206
218
  rstrip!
207
219
  output "\n"
208
220
  end
209
- was_prop = child.is_a?(Sass::Tree::PropNode)
221
+ had_children = child.has_children
210
222
  first = false
211
223
  elsif node.style == :compressed
212
- output(was_prop ? ";" : "")
224
+ unless had_children
225
+ output(";") unless trailing_semicolon?
226
+ end
213
227
  with_tabs(0) {visit(child)}
214
- was_prop = child.is_a?(Sass::Tree::PropNode)
228
+ had_children = child.has_children
215
229
  else
216
230
  with_tabs(@tabs + 1) {visit(child)}
217
231
  output "\n"
218
232
  end
219
233
  end
220
234
  rstrip!
235
+ if node.style == :compressed && trailing_semicolon?
236
+ erase! 1
237
+ end
221
238
  if node.style == :expanded
222
239
  output("\n#{tab_str}")
223
240
  elsif node.style != :compressed
@@ -278,10 +295,13 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
278
295
 
279
296
  joined_rules = node.resolved_rules.members.map do |seq|
280
297
  next if seq.has_placeholder?
281
- rule_part = seq.to_s
298
+ rule_part = seq.to_s(:style => node.style)
282
299
  if node.style == :compressed
283
300
  rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
284
- rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
301
+ rule_part.gsub!(/\s*([+>])\s*/m, '\1')
302
+ rule_part.gsub!(/nth([^( ]*)\(([^)]*)\)/m) do |match|
303
+ match.tr(" \t\n", "")
304
+ end
285
305
  rule_part.strip!
286
306
  end
287
307
  rule_part
@@ -306,8 +326,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
306
326
  relative_filename =
307
327
  if node.options[:css_filename]
308
328
  begin
309
- Sass::Util.pathname(node.filename).relative_path_from(
310
- Sass::Util.pathname(File.dirname(node.options[:css_filename]))).to_s
329
+ Sass::Util.relative_path_from(
330
+ node.filename, File.dirname(node.options[:css_filename])).to_s
311
331
  rescue ArgumentError
312
332
  nil
313
333
  end
@@ -320,7 +340,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
320
340
  end
321
341
  end
322
342
 
323
- end_props, trailer, tabs = '', '', 0
343
+ end_props, trailer, tabs = '', '', 0
324
344
  if node.style == :compact
325
345
  separator, end_props, bracket = ' ', ' ', ' { '
326
346
  trailer = "\n" if node.group_end
@@ -338,10 +358,18 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
338
358
 
339
359
  with_tabs(tabs) do
340
360
  node.children.each_with_index do |child, i|
341
- output(separator) if i > 0
361
+ if i > 0
362
+ if separator.start_with?(";") && trailing_semicolon?
363
+ erase! 1
364
+ end
365
+ output(separator)
366
+ end
342
367
  visit(child)
343
368
  end
344
369
  end
370
+ if node.style == :compressed && trailing_semicolon?
371
+ erase! 1
372
+ end
345
373
 
346
374
  output(end_props)
347
375
  output("}" + trailer)
@@ -362,10 +390,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
362
390
  rule = Sass::Tree::RuleNode.new([""])
363
391
  rule.resolved_rules = Sass::Selector::CommaSequence.new(
364
392
  [Sass::Selector::Sequence.new(
365
- [Sass::Selector::SimpleSequence.new(
366
- [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)],
367
- false)
368
- ])
393
+ [Sass::Selector::SimpleSequence.new(
394
+ [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)],
395
+ false)
396
+ ])
369
397
  ])
370
398
  prop = Sass::Tree::PropNode.new([""], Sass::Script::Value::String.new(''), :new)
371
399
  prop.resolved_name = "font-family"
@@ -1,9 +1,7 @@
1
1
  require 'strscan'
2
2
 
3
3
  if Sass::Util.ruby1_8?
4
- # rubocop:disable ConstantName
5
4
  Sass::Util::MultibyteStringScanner = StringScanner
6
- # rubocop:enable ConstantName
7
5
  else
8
6
  if Sass::Util.rbx?
9
7
  # Rubinius's StringScanner class implements some of its methods in terms of
@@ -1,5 +1,4 @@
1
1
  require 'delegate'
2
- require 'sass/util'
3
2
 
4
3
  module Sass
5
4
  module Util
@@ -78,8 +78,7 @@ module Sass
78
78
  next unless subset.subset?(set)
79
79
  [index, subenum]
80
80
  end
81
- end
82
- res = Sass::Util.flatten(res, 1)
81
+ end.flatten(1)
83
82
  res.compact!
84
83
  res.uniq!
85
84
  res.sort!
data/lib/sass/util.rb CHANGED
@@ -13,6 +13,8 @@ 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
16
18
  module Util
17
19
  extend self
18
20
 
@@ -138,6 +140,18 @@ module Sass
138
140
  [[value, range.first].max, range.last].min
139
141
  end
140
142
 
143
+ # Like [Fixnum.round], but leaves rooms for slight floating-point
144
+ # differences.
145
+ #
146
+ # @param value [Numeric]
147
+ # @return [Numeric]
148
+ def round(value)
149
+ # If the number is within epsilon of X.5, round up (or down for negative
150
+ # numbers).
151
+ return value.round if (value % 1) - 0.5 <= -1 * Script::Value::Number.epsilon
152
+ value > 0 ? value.ceil : value.floor
153
+ end
154
+
141
155
  # Concatenates all strings that are adjacent in an array,
142
156
  # while leaving other elements as they are.
143
157
  #
@@ -265,7 +279,7 @@ module Sass
265
279
  # # [2, 4, 5]]
266
280
  def paths(arrs)
267
281
  arrs.inject([[]]) do |paths, arr|
268
- flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
282
+ arr.map {|e| paths.map {|path| path + [e]}}.flatten(1)
269
283
  end
270
284
  end
271
285
 
@@ -298,7 +312,7 @@ module Sass
298
312
  # @return [Array]
299
313
  def hash_to_a(hash)
300
314
  return hash.to_a unless ruby1_8? || defined?(Test::Unit)
301
- hash.sort_by {|k, v| k}
315
+ hash.sort_by {|k, _v| k}
302
316
  end
303
317
 
304
318
  # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
@@ -325,6 +339,18 @@ module Sass
325
339
  arr
326
340
  end
327
341
 
342
+ # Like `String.upcase`, but only ever upcases ASCII letters.
343
+ def upcase(string)
344
+ return string.upcase unless ruby2_4?
345
+ string.upcase(:ascii)
346
+ end
347
+
348
+ # Like `String.downcase`, but only ever downcases ASCII letters.
349
+ def downcase(string)
350
+ return string.downcase unless ruby2_4?
351
+ string.downcase(:ascii)
352
+ end
353
+
328
354
  # Returns a sub-array of `minuend` containing only elements that are also in
329
355
  # `subtrahend`. Ensures that the return value has the same order as
330
356
  # `minuend`, even on Rubinius where that's not guaranteed by `Array#-`.
@@ -404,13 +430,13 @@ module Sass
404
430
  # Returns information about the caller of the previous method.
405
431
  #
406
432
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
407
- # @return [[String, Fixnum, (String, nil)]]
433
+ # @return [[String, Integer, (String, nil)]]
408
434
  # An array containing the filename, line, and method name of the caller.
409
435
  # The method name may be nil
410
436
  def caller_info(entry = nil)
411
437
  # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
412
438
  entry ||= caller[1]
413
- info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
439
+ info = entry.scan(/^((?:[A-Za-z]:)?.*?):(-?.*?)(?::.*`(.+)')?$/).first
414
440
  info[1] = info[1].to_i
415
441
  # This is added by Rubinius to designate a block, but we don't care about it.
416
442
  info[2].sub!(/ \{\}\Z/, '') if info[2]
@@ -498,7 +524,7 @@ module Sass
498
524
  #
499
525
  # @param msg [String]
500
526
  def sass_warn(msg)
501
- msg = msg + "\n" unless ruby1?
527
+ msg += "\n" unless ruby1?
502
528
  Sass.logger.warn(msg)
503
529
  end
504
530
 
@@ -557,9 +583,12 @@ module Sass
557
583
  #
558
584
  # @return [Boolean]
559
585
  def listen_geq_2?
560
- return @listen_geq_2 unless @listen_geq_2.nil?
586
+ return @listen_geq_2 if defined?(@listen_geq_2)
561
587
  @listen_geq_2 =
562
588
  begin
589
+ # Make sure we're loading listen/version from the same place that
590
+ # we're loading listen itself.
591
+ load_listen!
563
592
  require 'listen/version'
564
593
  version_geq(::Listen::VERSION, '2.0.0')
565
594
  rescue LoadError
@@ -619,7 +648,7 @@ module Sass
619
648
 
620
649
  # Returns an array of ints representing the JRuby version number.
621
650
  #
622
- # @return [Array<Fixnum>]
651
+ # @return [Array<Integer>]
623
652
  def jruby_version
624
653
  @jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
625
654
  end
@@ -628,7 +657,7 @@ module Sass
628
657
  #
629
658
  # @param path [String]
630
659
  def glob(path)
631
- path = path.gsub('\\', '/') if windows?
660
+ path = path.tr('\\', '/') if windows?
632
661
  if block_given?
633
662
  Dir.glob(path) {|f| yield(f)}
634
663
  else
@@ -661,19 +690,77 @@ module Sass
661
690
  pathname(path.cleanpath.to_s)
662
691
  end
663
692
 
693
+ # Returns `path` with all symlinks resolved.
694
+ #
695
+ # @param path [String, Pathname]
696
+ # @return [Pathname]
697
+ def realpath(path)
698
+ path = Pathname.new(path) unless path.is_a?(Pathname)
699
+
700
+ # Explicitly DON'T run #pathname here. We don't want to convert
701
+ # to Windows directory separators because we're comparing these
702
+ # against the paths returned by Listen, which use forward
703
+ # slashes everywhere.
704
+ begin
705
+ path.realpath
706
+ rescue SystemCallError
707
+ # If [path] doesn't actually exist, don't bail, just
708
+ # return the original.
709
+ path
710
+ end
711
+ end
712
+
713
+ # Returns `path` relative to `from`.
714
+ #
715
+ # This is like `Pathname#relative_path_from` except it accepts both strings
716
+ # and pathnames, it handles Windows path separators correctly, and it throws
717
+ # an error rather than crashing if the paths use different encodings
718
+ # (https://github.com/ruby/ruby/pull/713).
719
+ #
720
+ # @param path [String, Pathname]
721
+ # @param from [String, Pathname]
722
+ # @return [Pathname?]
723
+ def relative_path_from(path, from)
724
+ pathname(path.to_s).relative_path_from(pathname(from.to_s))
725
+ rescue NoMethodError => e
726
+ raise e unless e.name == :zero?
727
+
728
+ # Work around https://github.com/ruby/ruby/pull/713.
729
+ path = path.to_s
730
+ from = from.to_s
731
+ raise ArgumentError("Incompatible path encodings: #{path.inspect} is #{path.encoding}, " +
732
+ "#{from.inspect} is #{from.encoding}")
733
+ end
734
+
664
735
  # Converts `path` to a "file:" URI. This handles Windows paths correctly.
665
736
  #
666
737
  # @param path [String, Pathname]
667
738
  # @return [String]
668
739
  def file_uri_from_path(path)
669
740
  path = path.to_s if path.is_a?(Pathname)
741
+ path = path.tr('\\', '/') if windows?
670
742
  path = Sass::Util.escape_uri(path)
671
743
  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 =~ /\\\\[^\\]+\\[^\\\/]+/
744
+ return "file:///" + path.tr("\\", "/") if path =~ %r{^[a-zA-Z]:[/\\]}
745
+ return "file:" + path.tr("\\", "/") if path =~ %r{\\\\[^\\]+\\[^\\/]+}
674
746
  path.tr("\\", "/")
675
747
  end
676
748
 
749
+ # Retries a filesystem operation if it fails on Windows. Windows
750
+ # has weird and flaky locking rules that can cause operations to fail.
751
+ #
752
+ # @yield [] The filesystem operation.
753
+ def retry_on_windows
754
+ return yield unless windows?
755
+
756
+ begin
757
+ yield
758
+ rescue SystemCallError
759
+ sleep 0.1
760
+ yield
761
+ end
762
+ end
763
+
677
764
  # Prepare a value for a destructuring assignment (e.g. `a, b =
678
765
  # val`). This works around a performance bug when using
679
766
  # ActiveSupport, and only needs to be called when `val` is likely
@@ -711,15 +798,6 @@ module Sass
711
798
  (RUBY_VERSION_COMPONENTS[0] == 1 && RUBY_VERSION_COMPONENTS[1] < 9)
712
799
  end
713
800
 
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
801
  # Whether or not this is running under Ruby 1.9.2 exactly.
724
802
  #
725
803
  # @return [Boolean]
@@ -728,6 +806,19 @@ module Sass
728
806
  @ruby1_9_2 = RUBY_VERSION_COMPONENTS == [1, 9, 2]
729
807
  end
730
808
 
809
+ # Whether or not this is running under Ruby 2.4 or higher.
810
+ #
811
+ # @return [Boolean]
812
+ def ruby2_4?
813
+ return @ruby2_4 if defined?(@ruby2_4)
814
+ @ruby2_4 =
815
+ if RUBY_VERSION_COMPONENTS[0] == 2
816
+ RUBY_VERSION_COMPONENTS[1] >= 4
817
+ else
818
+ RUBY_VERSION_COMPONENTS[0] > 2
819
+ end
820
+ end
821
+
731
822
  # Wehter or not this is running under JRuby 1.6 or lower.
732
823
  def jruby1_6?
733
824
  return @jruby1_6 if defined?(@jruby1_6)
@@ -769,14 +860,15 @@ module Sass
769
860
  end
770
861
 
771
862
  return Hash[pairs_or_hash] unless ruby1_8?
772
- (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
863
+ (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*pairs_or_hash.flatten(1)]
773
864
  end
774
865
 
775
866
  unless ruby1_8?
776
867
  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')
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')
780
872
  end
781
873
 
782
874
  # Like {\#check\_encoding}, but also checks for a `@charset` declaration
@@ -874,7 +966,7 @@ module Sass
874
966
  # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
875
967
  #
876
968
  # @param enum [Enumerable] The enumerable to get the enumerator for
877
- # @param n [Fixnum] The size of each cons
969
+ # @param n [Integer] The size of each cons
878
970
  # @return [Enumerator] The consed enumerator
879
971
  def enum_cons(enum, n)
880
972
  ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
@@ -883,7 +975,7 @@ module Sass
883
975
  # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
884
976
  #
885
977
  # @param enum [Enumerable] The enumerable to get the enumerator for
886
- # @param n [Fixnum] The size of each slice
978
+ # @param n [Integer] The size of each slice
887
979
  # @return [Enumerator] The consed enumerator
888
980
  def enum_slice(enum, n)
889
981
  ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
@@ -910,22 +1002,11 @@ module Sass
910
1002
  # Returns the ASCII code of the given character.
911
1003
  #
912
1004
  # @param c [String] All characters but the first are ignored.
913
- # @return [Fixnum] The ASCII code of `c`.
1005
+ # @return [Integer] The ASCII code of `c`.
914
1006
  def ord(c)
915
1007
  ruby1_8? ? c[0] : c.ord
916
1008
  end
917
1009
 
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
1010
  # Flattens the first level of nested arrays in `arrs`. Unlike
930
1011
  # `Array#flatten`, this orders the result by taking the first
931
1012
  # values from each array in order, then the second, and so on.
@@ -944,27 +1025,6 @@ module Sass
944
1025
  result
945
1026
  end
946
1027
 
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
1028
  # Like `Object#inspect`, but preserves non-ASCII characters rather than
969
1029
  # escaping them under Ruby 1.9.2. This is necessary so that the
970
1030
  # precompiled Haml template can be `#encode`d into `@options[:encoding]`
@@ -1067,11 +1127,11 @@ module Sass
1067
1127
 
1068
1128
  # Converts the argument into a valid JSON value.
1069
1129
  #
1070
- # @param v [Fixnum, String, Array, Boolean, nil]
1130
+ # @param v [Integer, String, Array, Boolean, nil]
1071
1131
  # @return [String]
1072
1132
  def json_value_of(v)
1073
1133
  case v
1074
- when Fixnum
1134
+ when Integer
1075
1135
  v.to_s
1076
1136
  when String
1077
1137
  "\"" + json_escape_string(v) + "\""
@@ -1093,7 +1153,7 @@ module Sass
1093
1153
  VLQ_BASE_MASK = VLQ_BASE - 1
1094
1154
  VLQ_CONTINUATION_BIT = VLQ_BASE
1095
1155
 
1096
- BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1156
+ BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1097
1157
  BASE64_DIGIT_MAP = begin
1098
1158
  map = {}
1099
1159
  Sass::Util.enum_with_index(BASE64_DIGITS).map do |digit, i|
@@ -1104,7 +1164,7 @@ module Sass
1104
1164
 
1105
1165
  # Encodes `value` as VLQ (http://en.wikipedia.org/wiki/VLQ).
1106
1166
  #
1107
- # @param value [Fixnum]
1167
+ # @param value [Integer]
1108
1168
  # @return [String] The encoded value
1109
1169
  def encode_vlq(value)
1110
1170
  if value < 0
@@ -1223,7 +1283,7 @@ module Sass
1223
1283
  require 'listen'
1224
1284
  rescue LoadError => e
1225
1285
  if version_geq(RUBY_VERSION, "1.9.3")
1226
- version_constraint = "~> 2.7"
1286
+ version_constraint = "~> 3.0"
1227
1287
  else
1228
1288
  version_constraint = "~> 1.1"
1229
1289
  end
@@ -1272,13 +1332,10 @@ module Sass
1272
1332
  return str, str.encoding
1273
1333
  end
1274
1334
 
1275
- # rubocop:disable LineLength
1276
-
1277
1335
  # Calculates the memoization table for the Least Common Subsequence algorithm.
1278
1336
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
1279
1337
  def lcs_table(x, y)
1280
1338
  # This method does not take a block as an explicit parameter for performance reasons.
1281
- # rubocop:enable LineLength
1282
1339
  c = Array.new(x.size) {[]}
1283
1340
  x.size.times {|i| c[i][0] = 0}
1284
1341
  y.size.times {|j| c[0][j] = 0}
@@ -1294,12 +1351,12 @@ module Sass
1294
1351
  end
1295
1352
  c
1296
1353
  end
1297
- # rubocop:disable ParameterLists, LineLength
1354
+ # rubocop:disable ParameterLists
1298
1355
 
1299
1356
  # Computes a single longest common subsequence for arrays x and y.
1300
1357
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
1301
1358
  def lcs_backtrace(c, x, y, i, j, &block)
1302
- # rubocop:enable ParameterList, LineLengths
1359
+ # rubocop:enable ParameterList
1303
1360
  return [] if i == 0 || j == 0
1304
1361
  if (v = yield(x[i], y[j]))
1305
1362
  return lcs_backtrace(c, x, y, i - 1, j - 1, &block) << v
data/lib/sass/version.rb CHANGED
@@ -8,7 +8,7 @@ module Sass
8
8
  # if it was installed from Git.
9
9
  module Version
10
10
  # Returns a hash representing the version of Sass.
11
- # The `:major`, `:minor`, and `:teeny` keys have their respective numbers as Fixnums.
11
+ # The `:major`, `:minor`, and `:teeny` keys have their respective numbers as Integers.
12
12
  # The `:name` key has the name of the version.
13
13
  # The `:string` key contains a human-readable string representation of the version.
14
14
  # The `:number` key is the major, minor, and teeny keys separated by periods.
@@ -41,7 +41,7 @@ module Sass
41
41
  # :prerelease_number => 1
42
42
  # }
43
43
  #
44
- # @return [{Symbol => String/Fixnum}] The version hash
44
+ # @return [{Symbol => String/Integer}] The version hash
45
45
  # @comment
46
46
  # rubocop:disable ClassVars
47
47
  def version
data/lib/sass.rb CHANGED
@@ -47,7 +47,7 @@ module Sass
47
47
  #
48
48
  # @param contents [String] The contents of the Sass file.
49
49
  # @param options [{Symbol => Object}] An options hash;
50
- # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
50
+ # see {file:SASS_REFERENCE.md#Options the Sass options documentation}
51
51
  # @raise [Sass::SyntaxError] if there's an error in the document
52
52
  # @raise [Encoding::UndefinedConversionError] if the source encoding
53
53
  # cannot be converted to UTF-8
@@ -69,7 +69,7 @@ module Sass
69
69
  #
70
70
  # @param filename [String] The path to the Sass, SCSS, or CSS file on disk.
71
71
  # @param options [{Symbol => Object}] An options hash;
72
- # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
72
+ # see {file:SASS_REFERENCE.md#Options the Sass options documentation}
73
73
  # @return [String] The compiled CSS.
74
74
  #
75
75
  # @overload compile_file(filename, css_filename, options = {})
@@ -77,7 +77,7 @@ module Sass
77
77
  #
78
78
  # @param filename [String] The path to the Sass, SCSS, or CSS file on disk.
79
79
  # @param options [{Symbol => Object}] An options hash;
80
- # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
80
+ # see {file:SASS_REFERENCE.md#Options the Sass options documentation}
81
81
  # @param css_filename [String] The location to which to write the compiled CSS.
82
82
  def self.compile_file(filename, *args)
83
83
  options = args.last.is_a?(Hash) ? args.pop : {}
@@ -100,3 +100,10 @@ require 'sass/engine'
100
100
  require 'sass/plugin' if defined?(Merb::Plugins)
101
101
  require 'sass/railtie'
102
102
  require 'sass/features'
103
+
104
+ if Sass::Util.ruby1?
105
+ Sass::Util.sass_warn(
106
+ "DEPRECATION WARNING:\n" +
107
+ "Sass 3.5 will no longer support Ruby #{RUBY_VERSION}.\n" +
108
+ "Please upgrade to Ruby 2.0.0 or greater as soon as possible.\n")
109
+ end