sass 3.4.0 → 3.4.25

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 (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
@@ -9,7 +9,7 @@ module Sass
9
9
  # selectors.
10
10
  #
11
11
  # @return [Set<String>]
12
- ACTUALLY_ELEMENTS = %w[after before first-line first-letter].to_set
12
+ ACTUALLY_ELEMENTS = %w(after before first-line first-letter).to_set
13
13
 
14
14
  # Like \{#type}, but returns the type of selector this looks like, rather
15
15
  # than the type it is semantically. This only differs from type for
@@ -51,40 +51,50 @@ module Sass
51
51
  # Returns a copy of this with \{#selector} set to \{#new\_selector}.
52
52
  #
53
53
  # @param new_selector [CommaSequence]
54
- # @return [CommaSequence]
54
+ # @return [Array<Simple>]
55
55
  def with_selector(new_selector)
56
- Pseudo.new(syntactic_type, name, arg, CommaSequence.new(new_selector.members.map do |seq|
57
- next seq unless seq.members.length == 1
58
- sseq = seq.members.first
59
- next seq unless sseq.is_a?(SimpleSequence) && sseq.members.length == 1
60
- sel = sseq.members.first
61
- next seq unless sel.is_a?(Pseudo) && sel.selector
56
+ result = Pseudo.new(syntactic_type, name, arg,
57
+ CommaSequence.new(new_selector.members.map do |seq|
58
+ next seq unless seq.members.length == 1
59
+ sseq = seq.members.first
60
+ next seq unless sseq.is_a?(SimpleSequence) && sseq.members.length == 1
61
+ sel = sseq.members.first
62
+ next seq unless sel.is_a?(Pseudo) && sel.selector
62
63
 
63
- case normalized_name
64
- when 'not'
65
- # In theory, if there's a nested :not its contents should be
66
- # unified with the return value. For example, if :not(.foo)
67
- # extends .bar, :not(.bar) should become .foo:not(.bar). However,
68
- # this is a narrow edge case and supporting it properly would make
69
- # this code and the code calling it a lot more complicated, so
70
- # it's not supported for now.
71
- next [] unless sel.normalized_name == 'matches'
72
- sel.selector.members
73
- when 'matches', 'any', 'current', 'nth-child', 'nth-last-child'
74
- # As above, we could theoretically support :not within :matches, but
75
- # doing so would require this method and its callers to handle much
76
- # more complex cases that likely aren't worth the pain.
77
- next [] unless sel.name == name && sel.arg == arg
78
- sel.selector.members
79
- when 'has', 'host', 'host-context'
80
- # We can't expand nested selectors here, because each layer adds an
81
- # additional layer of semantics. For example, `:has(:has(img))`
82
- # doesn't match `<div><img></div>` but `:has(img)` does.
83
- sel
84
- else
85
- []
86
- end
87
- end.flatten))
64
+ case normalized_name
65
+ when 'not'
66
+ # In theory, if there's a nested :not its contents should be
67
+ # unified with the return value. For example, if :not(.foo)
68
+ # extends .bar, :not(.bar) should become .foo:not(.bar). However,
69
+ # this is a narrow edge case and supporting it properly would make
70
+ # this code and the code calling it a lot more complicated, so
71
+ # it's not supported for now.
72
+ next [] unless sel.normalized_name == 'matches'
73
+ sel.selector.members
74
+ when 'matches', 'any', 'current', 'nth-child', 'nth-last-child'
75
+ # As above, we could theoretically support :not within :matches, but
76
+ # doing so would require this method and its callers to handle much
77
+ # more complex cases that likely aren't worth the pain.
78
+ next [] unless sel.name == name && sel.arg == arg
79
+ sel.selector.members
80
+ when 'has', 'host', 'host-context'
81
+ # We can't expand nested selectors here, because each layer adds an
82
+ # additional layer of semantics. For example, `:has(:has(img))`
83
+ # doesn't match `<div><img></div>` but `:has(img)` does.
84
+ sel
85
+ else
86
+ []
87
+ end
88
+ end.flatten))
89
+
90
+ # Older browsers support :not but only with a single complex selector.
91
+ # In order to support those browsers, we break up the contents of a :not
92
+ # unless it originally contained a selector list.
93
+ return [result] unless normalized_name == 'not'
94
+ return [result] if selector.members.length > 1
95
+ result.selector.members.map do |seq|
96
+ Pseudo.new(syntactic_type, name, arg, CommaSequence.new([seq]))
97
+ end
88
98
  end
89
99
 
90
100
  # The type of the selector. `:class` if this is a pseudoclass selector,
@@ -103,13 +113,13 @@ module Sass
103
113
  end
104
114
 
105
115
  # @see Selector#to_s
106
- def to_s
116
+ def to_s(opts = {})
107
117
  res = (syntactic_type == :class ? ":" : "::") + @name
108
118
  if @arg || @selector
109
119
  res << "("
110
120
  res << @arg.strip if @arg
111
121
  res << " " if @arg && @selector
112
- res << @selector.to_s if @selector
122
+ res << @selector.to_s(opts) if @selector
113
123
  res << ")"
114
124
  end
115
125
  res
@@ -6,11 +6,11 @@ module Sass
6
6
  # Sets the line of the Sass template on which this selector was declared.
7
7
  # This also sets the line for all child selectors.
8
8
  #
9
- # @param line [Fixnum]
10
- # @return [Fixnum]
9
+ # @param line [Integer]
10
+ # @return [Integer]
11
11
  def line=(line)
12
12
  members.each {|m| m.line = line if m.is_a?(SimpleSequence)}
13
- line
13
+ @line = line
14
14
  end
15
15
 
16
16
  # Sets the name of the file in which this selector was declared,
@@ -52,9 +52,7 @@ module Sass
52
52
  def resolve_parent_refs(super_cseq, implicit_parent)
53
53
  members = @members.dup
54
54
  nl = (members.first == "\n" && members.shift)
55
- contains_parent_ref = members.any? do |seq_or_op|
56
- seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
57
- end
55
+ contains_parent_ref = contains_parent_ref?
58
56
  return CommaSequence.new([self]) if !implicit_parent && !contains_parent_ref
59
57
 
60
58
  unless contains_parent_ref
@@ -75,6 +73,19 @@ module Sass
75
73
  end)
76
74
  end
77
75
 
76
+ # Returns whether there's a {Parent} selector anywhere in this sequence.
77
+ #
78
+ # @return [Boolean]
79
+ def contains_parent_ref?
80
+ members.any? do |sseq_or_op|
81
+ next false unless sseq_or_op.is_a?(SimpleSequence)
82
+ next true if sseq_or_op.members.first.is_a?(Parent)
83
+ sseq_or_op.members.any? do |sel|
84
+ sel.is_a?(Pseudo) && sel.selector && sel.selector.contains_parent_ref?
85
+ end
86
+ end
87
+ end
88
+
78
89
  # Non-destructively extends this selector with the extensions specified in a hash
79
90
  # (which should come from {Sass::Tree::Visitors::Cssize}).
80
91
  #
@@ -146,8 +157,8 @@ module Sass
146
157
  end
147
158
 
148
159
  # @see AbstractSequence#to_s
149
- def to_s
150
- @members.join(" ").gsub(/ ?\n ?/, "\n")
160
+ def to_s(opts = {})
161
+ @members.map {|m| m.is_a?(String) ? m : m.to_s(opts)}.join(" ").gsub(/ ?\n ?/, "\n")
151
162
  end
152
163
 
153
164
  # Returns a string representation of the sequence.
@@ -217,11 +228,11 @@ module Sass
217
228
  next if current.empty?
218
229
  current = current.dup
219
230
  last_current = [current.pop]
220
- prefixes = Sass::Util.flatten(prefixes.map do |prefix|
231
+ prefixes = prefixes.map do |prefix|
221
232
  sub = subweave(prefix, current)
222
233
  next [] unless sub
223
234
  sub.map {|seqs| seqs + last_current}
224
- end, 1)
235
+ end.flatten(1)
225
236
  end
226
237
  prefixes
227
238
  end
@@ -249,10 +260,22 @@ module Sass
249
260
  return [seq1] if seq2.empty?
250
261
 
251
262
  seq1, seq2 = seq1.dup, seq2.dup
252
- init = merge_initial_ops(seq1, seq2)
253
- return unless init
254
- fin = merge_final_ops(seq1, seq2)
255
- return unless fin
263
+ return unless (init = merge_initial_ops(seq1, seq2))
264
+ return unless (fin = merge_final_ops(seq1, seq2))
265
+
266
+ # Make sure there's only one root selector in the output.
267
+ root1 = has_root?(seq1.first) && seq1.shift
268
+ root2 = has_root?(seq2.first) && seq2.shift
269
+ if root1 && root2
270
+ return unless (root = root1.unify(root2))
271
+ seq1.unshift root
272
+ seq2.unshift root
273
+ elsif root1
274
+ seq2.unshift root1
275
+ elsif root2
276
+ seq1.unshift root2
277
+ end
278
+
256
279
  seq1 = group_selectors(seq1)
257
280
  seq2 = group_selectors(seq2)
258
281
  lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
@@ -263,6 +286,7 @@ module Sass
263
286
  end
264
287
 
265
288
  diff = [[init]]
289
+
266
290
  until lcs.empty?
267
291
  diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]
268
292
  seq1.shift
@@ -371,10 +395,10 @@ module Sass
371
395
  ([merged, '+'] if merged)
372
396
  ].compact
373
397
  end
374
- elsif op1 == '>' && %w[~ +].include?(op2)
398
+ elsif op1 == '>' && %w(~ +).include?(op2)
375
399
  res.unshift sel2, op2
376
400
  seq1.push sel1, op1
377
- elsif op2 == '>' && %w[~ +].include?(op1)
401
+ elsif op2 == '>' && %w(~ +).include?(op1)
378
402
  res.unshift sel1, op1
379
403
  seq2.push sel2, op2
380
404
  elsif op1 == op2
@@ -479,8 +503,15 @@ module Sass
479
503
 
480
504
  if seq1[1].is_a?(String)
481
505
  return unless seq2[si + 1].is_a?(String)
506
+
482
507
  # .foo ~ .bar is a superselector of .foo + .bar
483
508
  return unless seq1[1] == "~" ? seq2[si + 1] != ">" : seq1[1] == seq2[si + 1]
509
+
510
+ # .foo > .baz is not a superselector of .foo > .bar > .baz or .foo >
511
+ # .bar .baz, despite the fact that .baz is a superselector of .bar >
512
+ # .baz and .bar .baz. Same goes for + and ~.
513
+ return if seq1.length == 3 && seq2.length > 3
514
+
484
515
  return _superselector?(seq1[2..-1], seq2[si + 2..-1])
485
516
  elsif seq2[si + 1].is_a?(String)
486
517
  return unless seq2[si + 1] == ">"
@@ -520,7 +551,7 @@ module Sass
520
551
  def trim(seqses)
521
552
  # Avoid truly horrific quadratic behavior. TODO: I think there
522
553
  # may be a way to get perfect trimming without going quadratic.
523
- return Sass::Util.flatten(seqses, 1) if seqses.size > 100
554
+ return seqses.flatten(1) if seqses.size > 100
524
555
 
525
556
  # Keep the results in a separate array so we can be sure we aren't
526
557
  # comparing against an already-trimmed selector. This ensures that two
@@ -555,7 +586,7 @@ module Sass
555
586
  end
556
587
  end
557
588
  end
558
- Sass::Util.flatten(result, 1)
589
+ result.flatten(1)
559
590
  end
560
591
 
561
592
  def _hash
@@ -595,6 +626,11 @@ module Sass
595
626
  "(#{choices.join ', '})"
596
627
  end.join ' '
597
628
  end
629
+
630
+ def has_root?(sseq)
631
+ sseq.is_a?(SimpleSequence) &&
632
+ sseq.members.any? {|sel| sel.is_a?(Pseudo) && sel.normalized_name == "root"}
633
+ end
598
634
  end
599
635
  end
600
636
  end
@@ -5,7 +5,7 @@ module Sass
5
5
  class Simple
6
6
  # The line of the Sass template on which this selector was declared.
7
7
  #
8
- # @return [Fixnum]
8
+ # @return [Integer]
9
9
  attr_accessor :line
10
10
 
11
11
  # The name of the file in which this selector was declared,
@@ -23,8 +23,10 @@ module Sass
23
23
 
24
24
  # Returns the selector string.
25
25
  #
26
+ # @param opts [Hash] rendering options.
27
+ # @option opts [Symbol] :style The css rendering style.
26
28
  # @return [String]
27
- def to_s
29
+ def to_s(opts = {})
28
30
  Sass::Util.abstract(self)
29
31
  end
30
32
 
@@ -34,7 +36,7 @@ module Sass
34
36
  # so if that contains information irrelevant to the identity of the selector,
35
37
  # this should be overridden.
36
38
  #
37
- # @return [Fixnum]
39
+ # @return [Integer]
38
40
  def hash
39
41
  @_hash ||= equality_key.hash
40
42
  end
@@ -70,14 +72,12 @@ module Sass
70
72
  # by the time extension and unification happen,
71
73
  # this exception will only ever be raised as a result of programmer error
72
74
  def unify(sels)
75
+ return sels.first.unify([self]) if sels.length == 1 && sels.first.is_a?(Universal)
73
76
  return sels if sels.any? {|sel2| eql?(sel2)}
74
77
  sels_with_ix = Sass::Util.enum_with_index(sels)
75
- _, i =
76
- if is_a?(Pseudo)
77
- sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.type == :element)}
78
- else
79
- sels_with_ix.find {|sel, _| sel.is_a?(Pseudo)}
80
- end
78
+ if !is_a?(Pseudo) || (sels.last.is_a?(Pseudo) && sels.last.type == :element)
79
+ _, i = sels_with_ix.find {|sel, _| sel.is_a?(Pseudo)}
80
+ end
81
81
  return sels + [self] unless i
82
82
  sels[0...i] + [self] + sels[i..-1]
83
83
  end
@@ -107,10 +107,10 @@ module Sass
107
107
  # could be found at all.
108
108
  # If the second value is `false`, the first should be ignored.
109
109
  def unify_namespaces(ns1, ns2)
110
- return nil, false unless ns1 == ns2 || ns1.nil? || ns1 == '*' || ns2.nil? || ns2 == '*'
111
110
  return ns2, true if ns1 == '*'
112
111
  return ns1, true if ns2 == '*'
113
- [ns1 || ns2, true]
112
+ return nil, false unless ns1 == ns2
113
+ [ns1, true]
114
114
  end
115
115
  end
116
116
  end
@@ -86,9 +86,14 @@ module Sass
86
86
  # @return [CommaSequence] This selector, with parent references resolved
87
87
  # @raise [Sass::SyntaxError] If a parent selector is invalid
88
88
  def resolve_parent_refs(super_cseq)
89
+ resolved_members = @members.map do |sel|
90
+ next sel unless sel.is_a?(Pseudo) && sel.selector
91
+ sel.with_selector(sel.selector.resolve_parent_refs(super_cseq, false))
92
+ end.flatten
93
+
89
94
  # Parent selector only appears as the first selector in the sequence
90
- unless (parent = @members.first).is_a?(Parent)
91
- return CommaSequence.new([Sequence.new([self])])
95
+ unless (parent = resolved_members.first).is_a?(Parent)
96
+ return CommaSequence.new([Sequence.new([SimpleSequence.new(resolved_members, subject?)])])
92
97
  end
93
98
 
94
99
  return super_cseq if @members.size == 1 && parent.suffix.nil?
@@ -128,9 +133,9 @@ module Sass
128
133
  end
129
134
 
130
135
  Sequence.new(members[0...-1] +
131
- [SimpleSequence.new(parent_sub + @members[1..-1], subject?)] +
136
+ [SimpleSequence.new(parent_sub + resolved_members[1..-1], subject?)] +
132
137
  [newline].compact)
133
- end)
138
+ end)
134
139
  end
135
140
 
136
141
  # Non-destructively extends this selector with the extensions specified in a hash
@@ -153,17 +158,31 @@ module Sass
153
158
  seen_with_pseudo_selectors = seen.dup
154
159
 
155
160
  modified_original = false
156
- members = Sass::Util.enum_with_index(self.members).map do |sel, i|
161
+ members = self.members.map do |sel|
157
162
  next sel unless sel.is_a?(Pseudo) && sel.selector
158
163
  next sel if seen.include?([sel])
159
- extended = sel.selector.do_extend(extends, parent_directives, replace, seen, !:original)
164
+ extended = sel.selector.do_extend(extends, parent_directives, replace, seen, false)
160
165
  next sel if extended == sel.selector
161
166
  extended.members.reject! {|seq| seq.has_placeholder?}
167
+
168
+ # For `:not()`, we usually want to get rid of any complex
169
+ # selectors because that will cause the selector to fail to
170
+ # parse on all browsers at time of writing. We can keep them
171
+ # if either the original selector had a complex selector, or
172
+ # the result of extending has only complex selectors,
173
+ # because either way we aren't breaking anything that isn't
174
+ # already broken.
175
+ if sel.normalized_name == 'not' &&
176
+ (sel.selector.members.none? {|seq| seq.members.length > 1} &&
177
+ extended.members.any? {|seq| seq.members.length == 1})
178
+ extended.members.reject! {|seq| seq.members.length > 1}
179
+ end
180
+
162
181
  modified_original = true
163
182
  result = sel.with_selector(extended)
164
- seen_with_pseudo_selectors << [result]
183
+ result.each {|new_sel| seen_with_pseudo_selectors << [new_sel]}
165
184
  result
166
- end
185
+ end.flatten
167
186
 
168
187
  groups = Sass::Util.group_by_to_a(extends[members.to_set]) {|ex| ex.extender}
169
188
  groups.map! do |seq, group|
@@ -185,7 +204,7 @@ module Sass
185
204
  groups.map! do |sels, seq|
186
205
  next [] if seen.include?(sels)
187
206
  seq.do_extend(
188
- extends, parent_directives, !:replace, seen_with_pseudo_selectors + [sels], !:original)
207
+ extends, parent_directives, false, seen_with_pseudo_selectors + [sels], false)
189
208
  end
190
209
  groups.flatten!
191
210
 
@@ -241,7 +260,7 @@ module Sass
241
260
 
242
261
  # Some psuedo-selectors can be subselectors of non-pseudo selectors.
243
262
  # Pull those out here so we can efficiently check against them below.
244
- their_subselector_pseudos = %w[matches any nth-child nth-last-child].
263
+ their_subselector_pseudos = %w(matches any nth-child nth-last-child).
245
264
  map {|name| their_spcs[name] || []}.flatten
246
265
 
247
266
  # If `self`'s non-pseudo simple selectors aren't a subset of `their_sseq`'s,
@@ -260,14 +279,14 @@ module Sass
260
279
  end
261
280
  end
262
281
 
263
- our_spcs.all? do |name, pseudos|
282
+ our_spcs.all? do |_name, pseudos|
264
283
  pseudos.all? {|pseudo| pseudo.superselector?(their_sseq, parents)}
265
284
  end
266
285
  end
267
286
 
268
287
  # @see Simple#to_s
269
- def to_s
270
- res = @members.join
288
+ def to_s(opts = {})
289
+ res = @members.map {|m| m.to_s(opts)}.join
271
290
  res << '!' if subject?
272
291
  res
273
292
  end
@@ -313,12 +332,12 @@ MESSAGE
313
332
  end
314
333
 
315
334
  def _hash
316
- [base, Sass::Util.set_hash(rest)].hash
335
+ [base, rest.hash].hash
317
336
  end
318
337
 
319
338
  def _eql?(other)
320
339
  other.base.eql?(base) && other.pseudo_elements == pseudo_elements &&
321
- Sass::Util.set_eql?(other.rest, rest) && other.subject? == subject?
340
+ other.rest.eql?(rest) && other.subject? == subject?
322
341
  end
323
342
  end
324
343
  end
data/lib/sass/selector.rb CHANGED
@@ -39,7 +39,7 @@ module Sass
39
39
  end
40
40
 
41
41
  # @see Selector#to_s
42
- def to_s
42
+ def to_s(opts = {})
43
43
  "&" + (@suffix || '')
44
44
  end
45
45
 
@@ -65,7 +65,7 @@ module Sass
65
65
  end
66
66
 
67
67
  # @see Selector#to_s
68
- def to_s
68
+ def to_s(opts = {})
69
69
  "." + @name
70
70
  end
71
71
 
@@ -88,7 +88,7 @@ module Sass
88
88
  end
89
89
 
90
90
  # @see Selector#to_s
91
- def to_s
91
+ def to_s(opts = {})
92
92
  "#" + @name
93
93
  end
94
94
 
@@ -123,7 +123,7 @@ module Sass
123
123
  end
124
124
 
125
125
  # @see Selector#to_s
126
- def to_s
126
+ def to_s(opts = {})
127
127
  "%" + @name
128
128
  end
129
129
 
@@ -147,7 +147,7 @@ module Sass
147
147
  end
148
148
 
149
149
  # @see Selector#to_s
150
- def to_s
150
+ def to_s(opts = {})
151
151
  @namespace ? "#{@namespace}|*" : "*"
152
152
  end
153
153
 
@@ -219,7 +219,7 @@ module Sass
219
219
  end
220
220
 
221
221
  # @see Selector#to_s
222
- def to_s
222
+ def to_s(opts = {})
223
223
  @namespace ? "#{@namespace}|#{@name}" : @name
224
224
  end
225
225
 
@@ -296,10 +296,7 @@ module Sass
296
296
  # @param operator [String] The matching operator, e.g. `"="` or `"^="`
297
297
  # @param value [String] See \{#value}
298
298
  # @param flags [String] See \{#flags}
299
- # @comment
300
- # rubocop:disable ParameterLists
301
299
  def initialize(name, namespace, operator, value, flags)
302
- # rubocop:enable ParameterLists
303
300
  @name = name
304
301
  @namespace = namespace
305
302
  @operator = operator
@@ -308,7 +305,7 @@ module Sass
308
305
  end
309
306
 
310
307
  # @see Selector#to_s
311
- def to_s
308
+ def to_s(opts = {})
312
309
  res = "["
313
310
  res << @namespace << "|" if @namespace
314
311
  res << @name
data/lib/sass/shared.rb CHANGED
@@ -31,7 +31,7 @@ module Sass
31
31
  # A `Fixnum` in 1.8, a `String` in 1.9
32
32
  # @param finish [Character] The character closing the balanced pair.
33
33
  # A `Fixnum` in 1.8, a `String` in 1.9
34
- # @param count [Fixnum] The number of opening characters matched
34
+ # @param count [Integer] The number of opening characters matched
35
35
  # before calling this method
36
36
  # @return [(String, String)] The string matched within the balanced pair
37
37
  # and the rest of the string.
@@ -37,7 +37,7 @@ module Sass::Source
37
37
 
38
38
  # Shifts all output source ranges forward one or more lines.
39
39
  #
40
- # @param delta [Fixnum] The number of lines to shift the ranges forward.
40
+ # @param delta [Integer] The number of lines to shift the ranges forward.
41
41
  def shift_output_lines(delta)
42
42
  return if delta == 0
43
43
  @data.each do |m|
@@ -49,7 +49,7 @@ module Sass::Source
49
49
  # Shifts any output source ranges that lie on the first line forward one or
50
50
  # more characters on that line.
51
51
  #
52
- # @param delta [Fixnum] The number of characters to shift the ranges
52
+ # @param delta [Integer] The number of characters to shift the ranges
53
53
  # forward.
54
54
  def shift_output_offsets(delta)
55
55
  return if delta == 0
@@ -95,7 +95,8 @@ module Sass::Source
95
95
  end
96
96
  css_path &&= Sass::Util.pathname(Sass::Util.absolute_path(css_path))
97
97
  sourcemap_path &&= Sass::Util.pathname(Sass::Util.absolute_path(sourcemap_path))
98
- css_uri ||= Sass::Util.file_uri_from_path(css_path.relative_path_from(sourcemap_path.dirname))
98
+ css_uri ||= Sass::Util.file_uri_from_path(
99
+ Sass::Util.relative_path_from(css_path, sourcemap_path.dirname))
99
100
 
100
101
  result = "{\n"
101
102
  write_json_field(result, "version", 3, true)
@@ -117,12 +118,14 @@ module Sass::Source
117
118
  @data.each do |m|
118
119
  file, importer = m.input.file, m.input.importer
119
120
 
121
+ next unless importer
122
+
120
123
  if options[:type] == :inline
121
124
  source_uri = file
122
125
  else
123
126
  sourcemap_dir = sourcemap_path && sourcemap_path.dirname.to_s
124
127
  sourcemap_dir = nil if options[:type] == :file
125
- source_uri = importer && importer.public_url(file, sourcemap_dir)
128
+ source_uri = importer.public_url(file, sourcemap_dir)
126
129
  next unless source_uri
127
130
  end
128
131
 
@@ -2,17 +2,17 @@ module Sass::Source
2
2
  class Position
3
3
  # The one-based line of the document associated with the position.
4
4
  #
5
- # @return [Fixnum]
5
+ # @return [Integer]
6
6
  attr_accessor :line
7
7
 
8
8
  # The one-based offset in the line of the document associated with the
9
9
  # position.
10
10
  #
11
- # @return [Fixnum]
11
+ # @return [Integer]
12
12
  attr_accessor :offset
13
13
 
14
- # @param line [Fixnum] The source line
15
- # @param offset [Fixnum] The source offset
14
+ # @param line [Integer] The source line
15
+ # @param offset [Integer] The source offset
16
16
  def initialize(line, offset)
17
17
  @line = line
18
18
  @offset = offset
data/lib/sass/stack.rb CHANGED
@@ -101,8 +101,8 @@ module Sass
101
101
  def to_s
102
102
  Sass::Util.enum_with_index(Sass::Util.enum_cons(frames.reverse + [nil], 2)).
103
103
  map do |(frame, caller), i|
104
- "#{i == 0 ? "on" : "from"} line #{frame.line}" +
105
- " of #{frame.filename || "an unknown file"}" +
104
+ "#{i == 0 ? 'on' : 'from'} line #{frame.line}" +
105
+ " of #{frame.filename || 'an unknown file'}" +
106
106
  (caller && caller.name ? ", in `#{caller.name}'" : "")
107
107
  end.join("\n")
108
108
  end
data/lib/sass/supports.rb CHANGED
@@ -58,11 +58,11 @@ module Sass::Supports
58
58
  end
59
59
 
60
60
  def to_css
61
- "#{left_parens @left.to_css} #{op} #{right_parens @right.to_css}"
61
+ "#{parens @left, @left.to_css} #{op} #{parens @right, @right.to_css}"
62
62
  end
63
63
 
64
64
  def to_src(options)
65
- "#{left_parens @left.to_src(options)} #{op} #{right_parens @right.to_src(options)}"
65
+ "#{parens @left, @left.to_src(options)} #{op} #{parens @right, @right.to_src(options)}"
66
66
  end
67
67
 
68
68
  def deep_copy
@@ -79,14 +79,12 @@ module Sass::Supports
79
79
 
80
80
  private
81
81
 
82
- def left_parens(str)
83
- return "(#{str})" if @left.is_a?(Negation)
84
- str
85
- end
86
-
87
- def right_parens(str)
88
- return "(#{str})" if @right.is_a?(Negation) || @right.is_a?(Operator)
89
- str
82
+ def parens(condition, str)
83
+ if condition.is_a?(Negation) || (condition.is_a?(Operator) && condition.op != op)
84
+ return "(#{str})"
85
+ else
86
+ return str
87
+ end
90
88
  end
91
89
  end
92
90
 
@@ -59,7 +59,7 @@ module Sass::Tree
59
59
 
60
60
  # Returns the number of lines in the comment.
61
61
  #
62
- # @return [Fixnum]
62
+ # @return [Integer]
63
63
  def lines
64
64
  @value.inject(0) do |s, e|
65
65
  next s + e.count("\n") if e.is_a?(String)