sass 3.2.7 → 3.3.0.rc.1

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 (184) hide show
  1. data/MIT-LICENSE +2 -2
  2. data/README.md +14 -2
  3. data/Rakefile +25 -1
  4. data/VERSION +1 -1
  5. data/VERSION_DATE +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/lib/sass/cache_stores/base.rb +4 -2
  8. data/lib/sass/cache_stores/chain.rb +2 -1
  9. data/lib/sass/cache_stores/filesystem.rb +2 -6
  10. data/lib/sass/cache_stores/memory.rb +1 -1
  11. data/lib/sass/cache_stores/null.rb +2 -2
  12. data/lib/sass/callbacks.rb +1 -0
  13. data/lib/sass/css.rb +10 -10
  14. data/lib/sass/engine.rb +403 -150
  15. data/lib/sass/environment.rb +136 -57
  16. data/lib/sass/error.rb +7 -7
  17. data/lib/sass/exec.rb +123 -39
  18. data/lib/sass/features.rb +41 -0
  19. data/lib/sass/importers/base.rb +33 -2
  20. data/lib/sass/importers/deprecated_path.rb +45 -0
  21. data/lib/sass/importers/filesystem.rb +25 -14
  22. data/lib/sass/importers.rb +1 -0
  23. data/lib/sass/logger/base.rb +3 -3
  24. data/lib/sass/logger/log_level.rb +4 -6
  25. data/lib/sass/media.rb +19 -19
  26. data/lib/sass/plugin/compiler.rb +141 -101
  27. data/lib/sass/plugin/configuration.rb +18 -22
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/staleness_checker.rb +24 -8
  30. data/lib/sass/plugin.rb +4 -2
  31. data/lib/sass/repl.rb +3 -3
  32. data/lib/sass/script/css_lexer.rb +9 -4
  33. data/lib/sass/script/css_parser.rb +6 -2
  34. data/lib/sass/script/functions.rb +1343 -590
  35. data/lib/sass/script/lexer.rb +84 -52
  36. data/lib/sass/script/parser.rb +217 -97
  37. data/lib/sass/script/tree/funcall.rb +290 -0
  38. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +34 -13
  39. data/lib/sass/script/tree/list_literal.rb +80 -0
  40. data/lib/sass/script/tree/literal.rb +47 -0
  41. data/lib/sass/script/tree/map_literal.rb +64 -0
  42. data/lib/sass/script/{node.rb → tree/node.rb} +22 -12
  43. data/lib/sass/script/{operation.rb → tree/operation.rb} +17 -25
  44. data/lib/sass/script/tree/selector.rb +30 -0
  45. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +5 -4
  46. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +14 -9
  47. data/lib/sass/script/tree/variable.rb +57 -0
  48. data/lib/sass/script/tree.rb +16 -0
  49. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +4 -24
  50. data/lib/sass/script/value/base.rb +248 -0
  51. data/lib/sass/script/value/bool.rb +36 -0
  52. data/lib/sass/script/{color.rb → value/color.rb} +239 -195
  53. data/lib/sass/script/value/helpers.rb +155 -0
  54. data/lib/sass/script/value/list.rb +119 -0
  55. data/lib/sass/script/value/map.rb +70 -0
  56. data/lib/sass/script/value/null.rb +45 -0
  57. data/lib/sass/script/{number.rb → value/number.rb} +91 -65
  58. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  59. data/lib/sass/script/value.rb +11 -0
  60. data/lib/sass/script.rb +35 -8
  61. data/lib/sass/scss/css_parser.rb +2 -1
  62. data/lib/sass/scss/parser.rb +338 -170
  63. data/lib/sass/scss/rx.rb +5 -6
  64. data/lib/sass/scss/script_lexer.rb +1 -0
  65. data/lib/sass/scss/script_parser.rb +1 -0
  66. data/lib/sass/scss/static_parser.rb +23 -6
  67. data/lib/sass/selector/abstract_sequence.rb +2 -2
  68. data/lib/sass/selector/comma_sequence.rb +21 -16
  69. data/lib/sass/selector/sequence.rb +60 -34
  70. data/lib/sass/selector/simple.rb +11 -12
  71. data/lib/sass/selector/simple_sequence.rb +55 -33
  72. data/lib/sass/selector.rb +52 -48
  73. data/lib/sass/source/map.rb +211 -0
  74. data/lib/sass/source/position.rb +39 -0
  75. data/lib/sass/source/range.rb +41 -0
  76. data/lib/sass/stack.rb +120 -0
  77. data/lib/sass/supports.rb +12 -13
  78. data/lib/sass/tree/at_root_node.rb +82 -0
  79. data/lib/sass/tree/comment_node.rb +3 -3
  80. data/lib/sass/tree/css_import_node.rb +11 -11
  81. data/lib/sass/tree/debug_node.rb +2 -2
  82. data/lib/sass/tree/directive_node.rb +13 -2
  83. data/lib/sass/tree/each_node.rb +8 -8
  84. data/lib/sass/tree/extend_node.rb +13 -6
  85. data/lib/sass/tree/for_node.rb +4 -4
  86. data/lib/sass/tree/function_node.rb +5 -4
  87. data/lib/sass/tree/if_node.rb +1 -1
  88. data/lib/sass/tree/import_node.rb +4 -5
  89. data/lib/sass/tree/media_node.rb +4 -14
  90. data/lib/sass/tree/mixin_def_node.rb +4 -4
  91. data/lib/sass/tree/mixin_node.rb +21 -8
  92. data/lib/sass/tree/node.rb +29 -12
  93. data/lib/sass/tree/prop_node.rb +38 -18
  94. data/lib/sass/tree/return_node.rb +3 -2
  95. data/lib/sass/tree/root_node.rb +19 -3
  96. data/lib/sass/tree/rule_node.rb +25 -17
  97. data/lib/sass/tree/supports_node.rb +0 -13
  98. data/lib/sass/tree/trace_node.rb +2 -1
  99. data/lib/sass/tree/variable_node.rb +9 -3
  100. data/lib/sass/tree/visitors/base.rb +6 -6
  101. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  102. data/lib/sass/tree/visitors/convert.rb +63 -38
  103. data/lib/sass/tree/visitors/cssize.rb +63 -23
  104. data/lib/sass/tree/visitors/deep_copy.rb +6 -5
  105. data/lib/sass/tree/visitors/extend.rb +7 -7
  106. data/lib/sass/tree/visitors/perform.rb +256 -151
  107. data/lib/sass/tree/visitors/set_options.rb +6 -6
  108. data/lib/sass/tree/visitors/to_css.rb +231 -81
  109. data/lib/sass/tree/warn_node.rb +2 -2
  110. data/lib/sass/tree/while_node.rb +2 -2
  111. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  112. data/lib/sass/util/normalized_map.rb +65 -0
  113. data/lib/sass/util/ordered_hash.rb +188 -0
  114. data/lib/sass/util/subset_map.rb +3 -2
  115. data/lib/sass/util/test.rb +9 -0
  116. data/lib/sass/util.rb +220 -34
  117. data/lib/sass/version.rb +9 -9
  118. data/lib/sass.rb +14 -7
  119. data/test/sass/compiler_test.rb +213 -0
  120. data/test/sass/conversion_test.rb +235 -9
  121. data/test/sass/engine_test.rb +230 -60
  122. data/test/sass/exec_test.rb +86 -0
  123. data/test/sass/extend_test.rb +215 -147
  124. data/test/sass/functions_test.rb +584 -99
  125. data/test/sass/importer_test.rb +165 -17
  126. data/test/sass/plugin_test.rb +19 -13
  127. data/test/sass/script_conversion_test.rb +40 -0
  128. data/test/sass/script_test.rb +231 -21
  129. data/test/sass/scss/css_test.rb +14 -5
  130. data/test/sass/scss/scss_test.rb +1266 -66
  131. data/test/sass/source_map_test.rb +879 -0
  132. data/test/sass/templates/bork5.sass +3 -0
  133. data/test/sass/util/normalized_map_test.rb +30 -0
  134. data/test/sass/util_test.rb +90 -0
  135. data/test/sass/value_helpers_test.rb +181 -0
  136. data/test/test_helper.rb +7 -2
  137. metadata +316 -291
  138. data/lib/sass/script/bool.rb +0 -18
  139. data/lib/sass/script/funcall.rb +0 -231
  140. data/lib/sass/script/list.rb +0 -84
  141. data/lib/sass/script/literal.rb +0 -239
  142. data/lib/sass/script/null.rb +0 -34
  143. data/lib/sass/script/variable.rb +0 -58
  144. data/test/Gemfile +0 -3
  145. data/vendor/listen/CHANGELOG.md +0 -221
  146. data/vendor/listen/CONTRIBUTING.md +0 -38
  147. data/vendor/listen/Gemfile +0 -30
  148. data/vendor/listen/Guardfile +0 -8
  149. data/vendor/listen/LICENSE +0 -20
  150. data/vendor/listen/README.md +0 -315
  151. data/vendor/listen/Rakefile +0 -47
  152. data/vendor/listen/Vagrantfile +0 -96
  153. data/vendor/listen/lib/listen/adapter.rb +0 -214
  154. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -112
  155. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -85
  156. data/vendor/listen/lib/listen/adapters/linux.rb +0 -113
  157. data/vendor/listen/lib/listen/adapters/polling.rb +0 -67
  158. data/vendor/listen/lib/listen/adapters/windows.rb +0 -87
  159. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  160. data/vendor/listen/lib/listen/directory_record.rb +0 -371
  161. data/vendor/listen/lib/listen/listener.rb +0 -225
  162. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  163. data/vendor/listen/lib/listen/turnstile.rb +0 -28
  164. data/vendor/listen/lib/listen/version.rb +0 -3
  165. data/vendor/listen/lib/listen.rb +0 -40
  166. data/vendor/listen/listen.gemspec +0 -22
  167. data/vendor/listen/spec/listen/adapter_spec.rb +0 -183
  168. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  169. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  170. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  171. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  172. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  173. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  174. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1225
  175. data/vendor/listen/spec/listen/listener_spec.rb +0 -169
  176. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
  177. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  178. data/vendor/listen/spec/listen_spec.rb +0 -73
  179. data/vendor/listen/spec/spec_helper.rb +0 -21
  180. data/vendor/listen/spec/support/adapter_helper.rb +0 -629
  181. data/vendor/listen/spec/support/directory_record_helper.rb +0 -55
  182. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  183. data/vendor/listen/spec/support/listeners_helper.rb +0 -156
  184. data/vendor/listen/spec/support/platform_helper.rb +0 -15
data/lib/sass/scss/rx.rb CHANGED
@@ -22,7 +22,7 @@ module Sass
22
22
  out << escape_char(value.slice!(0...1))
23
23
  end
24
24
  out << value.gsub(/[^a-zA-Z0-9_-]/) {|c| escape_char c}
25
- return out
25
+ out
26
26
  end
27
27
 
28
28
  # Escapes a single character for a CSS identifier.
@@ -32,7 +32,7 @@ module Sass
32
32
  # @private
33
33
  def self.escape_char(c)
34
34
  return "\\%06x" % Sass::Util.ord(c) unless c =~ /[ -\/:-~]/
35
- return "\\#{c}"
35
+ "\\#{c}"
36
36
  end
37
37
 
38
38
  # Creates a Regexp from a plain text string,
@@ -80,8 +80,8 @@ module Sass
80
80
 
81
81
  S = /[ \t\r\n\f]+/
82
82
 
83
- COMMENT = /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\//
84
- SINGLE_LINE_COMMENT = /\/\/.*(\n[ \t]*\/\/.*)*/
83
+ COMMENT = %r{/\*[^*]*\*+(?:[^/][^*]*\*+)*/}
84
+ SINGLE_LINE_COMMENT = %r{//.*(\n[ \t]*//.*)*}
85
85
 
86
86
  CDO = quote("<!--")
87
87
  CDC = quote("-->")
@@ -94,7 +94,6 @@ module Sass
94
94
  HASH = /##{NAME}/
95
95
 
96
96
  IMPORTANT = /!#{W}important/i
97
- DEFAULT = /!#{W}default/i
98
97
 
99
98
  NUMBER = /#{NUM}(?:#{IDENT}|%)?/
100
99
 
@@ -127,7 +126,7 @@ module Sass
127
126
 
128
127
  STATIC_COMPONENT = /#{IDENT}|#{STRING_NOINTERP}|#{HEXCOLOR}|[+-]?#{NUMBER}|\!important/i
129
128
  STATIC_VALUE = /#{STATIC_COMPONENT}(\s*[\s,\/]\s*#{STATIC_COMPONENT})*([;}])/i
130
- STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){0,50}([{])/i
129
+ STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){1,50}([{])/i
131
130
  end
132
131
  end
133
132
  end
@@ -4,6 +4,7 @@ module Sass
4
4
  # that makes them usable by {SCSS::Parser} to parse SassScript.
5
5
  # In particular, the lexer doesn't support `!` for a variable prefix.
6
6
  module ScriptLexer
7
+
7
8
  private
8
9
 
9
10
  def variable
@@ -6,6 +6,7 @@ module Sass
6
6
  # when there's more content in the lexer once lexing is done.
7
7
  # In addition, the parser doesn't support `!` for a variable prefix.
8
8
  module ScriptParser
9
+
9
10
  private
10
11
 
11
12
  # @private
@@ -24,11 +24,28 @@ module Sass
24
24
  seq
25
25
  end
26
26
 
27
+ # Parses a static at-root query.
28
+ #
29
+ # @return [(Symbol, Array<String>)] The type of the query
30
+ # (`:with` or `:without`) and the values that are being filtered.
31
+ # @raise [Sass::SyntaxError] if there's a syntax error in the query,
32
+ # or if it doesn't take up the entire input string.
33
+ def parse_static_at_root_query
34
+ init_scanner!
35
+ tok!(/\(/); ss
36
+ type = tok!(/\b(without|with)\b/).to_sym; ss
37
+ tok!(/:/); ss
38
+ directives = expr!(:at_root_directive_list); ss
39
+ tok!(/\)/)
40
+ expected("@at-root query list") unless @scanner.eos?
41
+ return type, directives
42
+ end
43
+
27
44
  private
28
45
 
29
46
  def moz_document_function
30
- return unless val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) ||
31
- function(!:allow_var)
47
+ val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function(!:allow_var)
48
+ return unless val
32
49
  ss
33
50
  [val]
34
51
  end
@@ -37,12 +54,12 @@ module Sass
37
54
  def script_value; nil; end
38
55
  def interpolation; nil; end
39
56
  def var_expr; nil; end
40
- def interp_string; s = tok(STRING) and [s]; end
41
- def interp_uri; s = tok(URI) and [s]; end
42
- def interp_ident(ident = IDENT); s = tok(ident) and [s]; end
57
+ def interp_string; (s = tok(STRING)) && [s]; end
58
+ def interp_uri; (s = tok(URI)) && [s]; end
59
+ def interp_ident(ident = IDENT); (s = tok(ident)) && [s]; end
43
60
  def use_css_import?; true; end
44
61
 
45
- def special_directive(name)
62
+ def special_directive(name, start_pos)
46
63
  return unless %w[media import charset -moz-document].include?(name)
47
64
  super
48
65
  end
@@ -55,7 +55,7 @@ module Sass
55
55
  # @param other [Object] The object to test equality against
56
56
  # @return [Boolean] Whether or not this is equal to `other`
57
57
  def eql?(other)
58
- other.class == self.class && other.hash == self.hash && _eql?(other)
58
+ other.class == self.class && other.hash == hash && _eql?(other)
59
59
  end
60
60
  alias_method :==, :eql?
61
61
 
@@ -71,7 +71,7 @@ module Sass
71
71
  #
72
72
  # @return [String]
73
73
  def to_s
74
- to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
74
+ to_a.map {|e| e.is_a?(Sass::Script::Tree::Node) ? "\#{#{e.to_sass}}" : e}.join
75
75
  end
76
76
 
77
77
  # Returns the specificity of the selector as an integer. The base is given
@@ -18,23 +18,28 @@ module Sass
18
18
  # handling commas appropriately.
19
19
  #
20
20
  # @param super_cseq [CommaSequence] The parent selector
21
+ # @param implicit_parent [Boolean] Whether the the parent
22
+ # selector should automatically be prepended to the resolved
23
+ # selector if it contains no parent refs.
21
24
  # @return [CommaSequence] This selector, with parent references resolved
22
25
  # @raise [Sass::SyntaxError] If a parent selector is invalid
23
- def resolve_parent_refs(super_cseq)
26
+ def resolve_parent_refs(super_cseq, implicit_parent = true)
24
27
  if super_cseq.nil?
25
28
  if @members.any? do |sel|
26
- sel.members.any? do |sel_or_op|
27
- sel_or_op.is_a?(SimpleSequence) && sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
28
- end
29
- end
30
- raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '&'.")
29
+ sel.members.any? do |sel_or_op|
30
+ sel_or_op.is_a?(SimpleSequence) &&
31
+ sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
32
+ end
33
+ end
34
+ raise Sass::SyntaxError.new(
35
+ "Base-level rules cannot contain the parent-selector-referencing character '&'.")
31
36
  end
32
37
  return self
33
38
  end
34
39
 
35
40
  CommaSequence.new(
36
41
  super_cseq.members.map do |super_seq|
37
- @members.map {|seq| seq.resolve_parent_refs(super_seq)}
42
+ @members.map {|seq| seq.resolve_parent_refs(super_seq, implicit_parent)}
38
43
  end.flatten)
39
44
  end
40
45
 
@@ -53,14 +58,14 @@ module Sass
53
58
  # with extensions made according to `extends`
54
59
  def do_extend(extends, parent_directives)
55
60
  CommaSequence.new(members.map do |seq|
56
- extended = seq.do_extend(extends, parent_directives)
57
- # First Law of Extend: the result of extending a selector should
58
- # always contain the base selector.
59
- #
60
- # See https://github.com/nex3/sass/issues/324.
61
- extended.unshift seq unless seq.has_placeholder? || extended.include?(seq)
62
- extended
63
- end.flatten)
61
+ extended = seq.do_extend(extends, parent_directives)
62
+ # First Law of Extend: the result of extending a selector should
63
+ # always contain the base selector.
64
+ #
65
+ # See https://github.com/nex3/sass/issues/324.
66
+ extended.unshift seq unless seq.has_placeholder? || extended.include?(seq)
67
+ extended
68
+ end.flatten)
64
69
  end
65
70
 
66
71
  # Returns a string representation of the sequence.
@@ -85,7 +90,7 @@ module Sass
85
90
  end
86
91
 
87
92
  def _eql?(other)
88
- other.class == self.class && other.members.eql?(self.members)
93
+ other.class == self.class && other.members.eql?(members)
89
94
  end
90
95
  end
91
96
  end
@@ -33,7 +33,8 @@ module Sass
33
33
  # @return [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>]
34
34
  attr_reader :members
35
35
 
36
- # @param seqs_and_ops [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>] See \{#members}
36
+ # @param seqs_and_ops [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>]
37
+ # See \{#members}
37
38
  def initialize(seqs_and_ops)
38
39
  @members = seqs_and_ops
39
40
  end
@@ -43,14 +44,20 @@ module Sass
43
44
  # handling commas appropriately.
44
45
  #
45
46
  # @param super_seq [Sequence] The parent selector sequence
47
+ # @param implicit_parent [Boolean] Whether the the parent
48
+ # selector should automatically be prepended to the resolved
49
+ # selector if it contains no parent refs.
46
50
  # @return [Sequence] This selector, with parent references resolved
47
51
  # @raise [Sass::SyntaxError] If a parent selector is invalid
48
- def resolve_parent_refs(super_seq)
52
+ def resolve_parent_refs(super_seq, implicit_parent)
49
53
  members = @members.dup
50
54
  nl = (members.first == "\n" && members.shift)
51
- unless members.any? do |seq_or_op|
52
- seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
53
- end
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
58
+ return self if !implicit_parent && !contains_parent_ref
59
+
60
+ unless contains_parent_ref
54
61
  old_members, members = members, []
55
62
  members << nl if nl
56
63
  members << SimpleSequence.new([Parent.new], false)
@@ -105,7 +112,9 @@ module Sass
105
112
 
106
113
  # @see Simple#to_a
107
114
  def to_a
108
- ary = @members.map {|seq_or_op| seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op}
115
+ ary = @members.map do |seq_or_op|
116
+ seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op
117
+ end
109
118
  Sass::Util.intersperse(ary, " ").flatten.compact
110
119
  end
111
120
 
@@ -133,7 +142,8 @@ module Sass
133
142
  # this conceptually expands into `.D .C, .D (.A .B)`,
134
143
  # and this function translates `.D (.A .B)` into `.D .A .B, .A.D .B, .D .A .B`.
135
144
  #
136
- # @param path [Array<Array<SimpleSequence or String>>] A list of parenthesized selector groups.
145
+ # @param path [Array<Array<SimpleSequence or String>>]
146
+ # A list of parenthesized selector groups.
137
147
  # @return [Array<Array<SimpleSequence or String>>] A list of fully-expanded selectors.
138
148
  def weave(path)
139
149
  # This function works by moving through the selector path left-to-right,
@@ -145,12 +155,14 @@ module Sass
145
155
  until afters.empty?
146
156
  current = afters.shift.dup
147
157
  last_current = [current.pop]
148
- befores = Sass::Util.flatten(befores.map do |before|
149
- next [] unless sub = subweave(before, current)
150
- sub.map {|seqs| seqs + last_current}
151
- end, 1)
158
+ befores.map! do |before|
159
+ sub = subweave(before, current)
160
+ next [] unless sub
161
+ sub.map {|seqs| seqs + last_current}
162
+ end
163
+ befores = Sass::Util.flatten(befores, 1)
152
164
  end
153
- return befores
165
+ befores
154
166
  end
155
167
 
156
168
  # This interweaves two lists of selectors,
@@ -176,8 +188,10 @@ module Sass
176
188
  return [seq1] if seq2.empty?
177
189
 
178
190
  seq1, seq2 = seq1.dup, seq2.dup
179
- return unless init = merge_initial_ops(seq1, seq2)
180
- return unless fin = merge_final_ops(seq1, seq2)
191
+ init = merge_initial_ops(seq1, seq2)
192
+ return unless init
193
+ fin = merge_final_ops(seq1, seq2)
194
+ return unless fin
181
195
  seq1 = group_selectors(seq1)
182
196
  seq2 = group_selectors(seq2)
183
197
  lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
@@ -222,7 +236,7 @@ module Sass
222
236
  # merged successfully
223
237
  lcs = Sass::Util.lcs(ops1, ops2)
224
238
  return unless lcs == ops1 || lcs == ops2
225
- return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
239
+ (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
226
240
  end
227
241
 
228
242
  # Extracts final selector combinators (`"+"`, `">"`, `"~"`) and the
@@ -238,6 +252,8 @@ module Sass
238
252
  # be nil. Otherwise, this will contained the merged selector. Array
239
253
  # elements are [Sass::Util#paths]-style options; conceptually, an "or"
240
254
  # of multiple selectors.
255
+ # @comment
256
+ # rubocop:disable MethodLength
241
257
  def merge_final_ops(seq1, seq2, res = [])
242
258
  ops1, ops2 = [], []
243
259
  ops1 << seq1.pop while seq1.last.is_a?(String)
@@ -255,7 +271,7 @@ module Sass
255
271
  # is a supersequence of the other, use that, otherwise give up.
256
272
  lcs = Sass::Util.lcs(ops1, ops2)
257
273
  return unless lcs == ops1 || lcs == ops2
258
- res.unshift *(ops1.size > ops2.size ? ops1 : ops2).reverse
274
+ res.unshift(*(ops1.size > ops2.size ? ops1 : ops2).reverse)
259
275
  return res
260
276
  end
261
277
 
@@ -301,7 +317,8 @@ module Sass
301
317
  res.unshift sel1, op1
302
318
  seq2.push sel2, op2
303
319
  elsif op1 == op2
304
- return unless merged = sel1.unify(sel2.members, sel2.subject?)
320
+ merged = sel1.unify(sel2.members, sel2.subject?)
321
+ return unless merged
305
322
  res.unshift merged, op1
306
323
  else
307
324
  # Unknown selector combinators can't be unified
@@ -318,6 +335,8 @@ module Sass
318
335
  return merge_final_ops(seq1, seq2, res)
319
336
  end
320
337
  end
338
+ # @comment
339
+ # rubocop:enable MethodLength
321
340
 
322
341
  # Takes initial subsequences of `seq1` and `seq2` and returns all
323
342
  # orderings of those subsequences. The initial subsequences are determined
@@ -369,7 +388,7 @@ module Sass
369
388
  end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
370
389
  newseq << head
371
390
  end
372
- return newseq
391
+ newseq
373
392
  end
374
393
 
375
394
  # Given two selector sequences, returns whether `seq1` is a
@@ -398,15 +417,15 @@ module Sass
398
417
  return unless si
399
418
 
400
419
  if seq1[1].is_a?(String)
401
- return unless seq2[si+1].is_a?(String)
420
+ return unless seq2[si + 1].is_a?(String)
402
421
  # .foo ~ .bar is a superselector of .foo + .bar
403
- return unless seq1[1] == "~" ? seq2[si+1] != ">" : seq1[1] == seq2[si+1]
404
- return _superselector?(seq1[2..-1], seq2[si+2..-1])
405
- elsif seq2[si+1].is_a?(String)
406
- return unless seq2[si+1] == ">"
407
- return _superselector?(seq1[1..-1], seq2[si+2..-1])
422
+ return unless seq1[1] == "~" ? seq2[si + 1] != ">" : seq1[1] == seq2[si + 1]
423
+ return _superselector?(seq1[2..-1], seq2[si + 2..-1])
424
+ elsif seq2[si + 1].is_a?(String)
425
+ return unless seq2[si + 1] == ">"
426
+ return _superselector?(seq1[1..-1], seq2[si + 2..-1])
408
427
  else
409
- return _superselector?(seq1[1..-1], seq2[si+1..-1])
428
+ return _superselector?(seq1[1..-1], seq2[si + 1..-1])
410
429
  end
411
430
  end
412
431
 
@@ -421,7 +440,8 @@ module Sass
421
440
  # @param seq2 [Array<SimpleSequence or String>]
422
441
  # @return [Boolean]
423
442
  def parent_superselector?(seq1, seq2)
424
- base = Sass::Selector::SimpleSequence.new([Sass::Selector::Placeholder.new('<temp>')], false)
443
+ base = Sass::Selector::SimpleSequence.new([Sass::Selector::Placeholder.new('<temp>')],
444
+ false)
425
445
  _superselector?(seq1 + [base], seq2 + [base])
426
446
  end
427
447
 
@@ -437,26 +457,32 @@ module Sass
437
457
  # @param seqses [Array<Array<Array<SimpleSequence or String>>>]
438
458
  # @return [Array<Array<Array<SimpleSequence or String>>>]
439
459
  def trim(seqses)
440
- # Avoid truly horrific quadratic behavior. TOOD: I think there
460
+ # Avoid truly horrific quadratic behavior. TODO: I think there
441
461
  # may be a way to get perfect trimming without going quadratic.
442
462
  return seqses if seqses.size > 100
463
+
464
+ # Keep the results in a separate array so we can be sure we aren't
465
+ # comparing against an already-trimmed selector. This ensures that two
466
+ # identical selectors don't mutually trim one another.
467
+ result = seqses.dup
468
+
443
469
  # This is n^2 on the sequences, but only comparing between
444
470
  # separate sequences should limit the quadratic behavior.
445
- seqses.map do |seqs1|
446
- seqs1.reject do |seq1|
447
- min_spec = 0
448
- _sources(seq1).map {|seq| min_spec += seq.specificity}
449
- seqses.any? do |seqs2|
471
+ seqses.each_with_index do |seqs1, i|
472
+ result[i] = seqs1.reject do |seq1|
473
+ max_spec = _sources(seq1).map {|seq| seq.specificity}.max || 0
474
+ result.any? do |seqs2|
450
475
  next if seqs1.equal?(seqs2)
451
476
  # Second Law of Extend: the specificity of a generated selector
452
477
  # should never be less than the specificity of the extending
453
478
  # selector.
454
479
  #
455
480
  # See https://github.com/nex3/sass/issues/324.
456
- seqs2.any? {|seq2| _specificity(seq2) >= min_spec && _superselector?(seq2, seq1)}
481
+ seqs2.any? {|seq2| _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)}
457
482
  end
458
483
  end
459
484
  end
485
+ result
460
486
  end
461
487
 
462
488
  def _hash
@@ -464,7 +490,7 @@ module Sass
464
490
  end
465
491
 
466
492
  def _eql?(other)
467
- other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
493
+ other.members.reject {|m| m == "\n"}.eql?(members.reject {|m| m == "\n"})
468
494
  end
469
495
 
470
496
  private
@@ -14,13 +14,12 @@ module Sass
14
14
  # @return [String, nil]
15
15
  attr_accessor :filename
16
16
 
17
- # Returns a representation of the node
18
- # as an array of strings and potentially {Sass::Script::Node}s
19
- # (if there's interpolation in the selector).
20
- # When the interpolation is resolved and the strings are joined together,
21
- # this will be the string representation of this node.
17
+ # Returns a representation of the node as an array of strings and
18
+ # potentially {Sass::Script::Tree::Node}s (if there's interpolation in the
19
+ # selector). When the interpolation is resolved and the strings are joined
20
+ # together, this will be the string representation of this node.
22
21
  #
23
- # @return [Array<String, Sass::Script::Node>]
22
+ # @return [Array<String, Sass::Script::Tree::Node>]
24
23
  def to_a
25
24
  Sass::Util.abstract(self)
26
25
  end
@@ -30,7 +29,7 @@ module Sass
30
29
  #
31
30
  # @return [String]
32
31
  def inspect
33
- to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
32
+ to_a.map {|e| e.is_a?(Sass::Script::Tree::Node) ? "\#{#{e.to_sass}}" : e}.join
34
33
  end
35
34
 
36
35
  # @see \{#inspect}
@@ -59,7 +58,7 @@ module Sass
59
58
  # @param other [Object] The object to test equality against
60
59
  # @return [Boolean] Whether or not this is equal to `other`
61
60
  def eql?(other)
62
- other.class == self.class && other.hash == self.hash && other.to_a.eql?(to_a)
61
+ other.class == self.class && other.hash == hash && other.to_a.eql?(to_a)
63
62
  end
64
63
  alias_method :==, :eql?
65
64
 
@@ -84,13 +83,13 @@ module Sass
84
83
  return sels if sels.any? {|sel2| eql?(sel2)}
85
84
  sels_with_ix = Sass::Util.enum_with_index(sels)
86
85
  _, i =
87
- if self.is_a?(Pseudo) || self.is_a?(SelectorPseudoClass)
88
- sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.final? || sels.last.type == :element)}
86
+ if is_a?(Pseudo) || is_a?(SelectorPseudoClass)
87
+ sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.type == :element)}
89
88
  else
90
89
  sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
91
90
  end
92
91
  return sels + [self] unless i
93
- return sels[0...i] + [self] + sels[i..-1]
92
+ sels[0...i] + [self] + sels[i..-1]
94
93
  end
95
94
 
96
95
  protected
@@ -112,7 +111,7 @@ module Sass
112
111
  return nil, false unless ns1 == ns2 || ns1.nil? || ns1 == ['*'] || ns2.nil? || ns2 == ['*']
113
112
  return ns2, true if ns1 == ['*']
114
113
  return ns1, true if ns2 == ['*']
115
- return ns1 || ns2, true
114
+ [ns1 || ns2, true]
116
115
  end
117
116
  end
118
117
  end
@@ -21,11 +21,16 @@ module Sass
21
21
  # and the generated selector `c.foo.bar.baz` has `{b.bar, c.baz}` as its
22
22
  # `sources` set.
23
23
  #
24
- # This is populated during the {#do_extend} process.
24
+ # This is populated during the {Sequence#do_extend} process.
25
25
  #
26
26
  # @return {Set<Sequence>}
27
27
  attr_accessor :sources
28
28
 
29
+ # This sequence source range.
30
+ #
31
+ # @return [Sass::Source::Range]
32
+ attr_accessor :source_range
33
+
29
34
  # @see \{#subject?}
30
35
  attr_writer :subject
31
36
 
@@ -37,11 +42,16 @@ module Sass
37
42
  @base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal))
38
43
  end
39
44
 
40
- # Returns the non-base selectors in this sequence.
45
+ def pseudo_elements
46
+ @pseudo_elements ||= (members - [base]).
47
+ select {|sel| sel.is_a?(Pseudo) && sel.type == :element}
48
+ end
49
+
50
+ # Returns the non-base, non-pseudo-class selectors in this sequence.
41
51
  #
42
52
  # @return [Set<Simple>]
43
53
  def rest
44
- @rest ||= Set.new(base ? members[1..-1] : members)
54
+ @rest ||= Set.new(members - [base] - pseudo_elements)
45
55
  end
46
56
 
47
57
  # Whether or not this compound selector is the subject of the parent
@@ -55,11 +65,12 @@ module Sass
55
65
 
56
66
  # @param selectors [Array<Simple>] See \{#members}
57
67
  # @param subject [Boolean] See \{#subject?}
58
- # @param sources [Set<Sequence>]
59
- def initialize(selectors, subject, sources = Set.new)
68
+ # @param source_range [Sass::Source::Range]
69
+ def initialize(selectors, subject, source_range = nil)
60
70
  @members = selectors
61
71
  @subject = subject
62
- @sources = sources
72
+ @sources = Set.new
73
+ @source_range = source_range
63
74
  end
64
75
 
65
76
  # Resolves the {Parent} selectors within this selector
@@ -96,30 +107,37 @@ module Sass
96
107
  # by extending this selector with `extends`.
97
108
  # @see CommaSequence#do_extend
98
109
  def do_extend(extends, parent_directives, seen = Set.new)
99
- Sass::Util.group_by_to_a(extends.get(members.to_set)) {|ex, _| ex.extender}.map do |seq, group|
110
+ groups = Sass::Util.group_by_to_a(extends.get(members.to_set)) {|ex, _| ex.extender}
111
+ groups.map! do |seq, group|
100
112
  sels = group.map {|_, s| s}.flatten
101
113
  # If A {@extend B} and C {...},
102
114
  # seq is A, sels is B, and self is C
103
115
 
104
- self_without_sel = Sass::Util.array_minus(self.members, sels)
116
+ self_without_sel = Sass::Util.array_minus(members, sels)
105
117
  group.each {|e, _| e.result = :failed_to_unify unless e.result == :succeeded}
106
- next unless unified = seq.members.last.unify(self_without_sel, subject?)
118
+ unified = seq.members.last.unify(self_without_sel, subject?)
119
+ next unless unified
107
120
  group.each {|e, _| e.result = :succeeded}
108
- next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?
121
+ group.each {|e, _| check_directives_match!(e, parent_directives)}
109
122
  new_seq = Sequence.new(seq.members[0...-1] + [unified])
110
123
  new_seq.add_sources!(sources + [seq])
111
124
  [sels, new_seq]
112
- end.compact.map do |sels, seq|
125
+ end
126
+ groups.compact!
127
+ groups.map! do |sels, seq|
113
128
  seen.include?(sels) ? [] : seq.do_extend(extends, parent_directives, seen + [sels])
114
- end.flatten.uniq
129
+ end
130
+ groups.flatten!
131
+ groups.uniq!
132
+ groups
115
133
  end
116
134
 
117
- # Unifies this selector with another {SimpleSequence}'s {SimpleSequence#members members array},
118
- # returning another `SimpleSequence`
135
+ # Unifies this selector with another {SimpleSequence}'s
136
+ # {SimpleSequence#members members array}, returning another `SimpleSequence`
119
137
  # that matches both this selector and the input selector.
120
138
  #
121
139
  # @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
122
- # @param subject [Boolean] Whether the {SimpleSequence} being merged is a subject.
140
+ # @param other_subject [Boolean] Whether the other {SimpleSequence} being merged is a subject.
123
141
  # @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
124
142
  # or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
125
143
  # @raise [Sass::SyntaxError] If this selector cannot be unified.
@@ -129,10 +147,11 @@ module Sass
129
147
  # by the time extension and unification happen,
130
148
  # this exception will only ever be raised as a result of programmer error
131
149
  def unify(sels, other_subject)
132
- return unless sseq = members.inject(sels) do |sseq, sel|
133
- return unless sseq
134
- sel.unify(sseq)
150
+ sseq = members.inject(sels) do |member, sel|
151
+ return unless member
152
+ sel.unify(member)
135
153
  end
154
+ return unless sseq
136
155
  SimpleSequence.new(sseq, other_subject || subject?)
137
156
  end
138
157
 
@@ -145,7 +164,9 @@ module Sass
145
164
  # @param sseq [SimpleSequence]
146
165
  # @return [Boolean]
147
166
  def superselector?(sseq)
148
- (base.nil? || base.eql?(sseq.base)) && rest.subset?(sseq.rest)
167
+ (base.nil? || base.eql?(sseq.base)) &&
168
+ pseudo_elements.eql?(sseq.pseudo_elements) &&
169
+ rest.subset?(sseq.rest)
149
170
  end
150
171
 
151
172
  # @see Simple#to_a
@@ -164,14 +185,14 @@ module Sass
164
185
  end
165
186
 
166
187
  # Return a copy of this simple sequence with `sources` merged into the
167
- # {#sources} set.
188
+ # {SimpleSequence#sources} set.
168
189
  #
169
190
  # @param sources [Set<Sequence>]
170
191
  # @return [SimpleSequence]
171
192
  def with_more_sources(sources)
172
193
  sseq = dup
173
194
  sseq.members = members.dup
174
- sseq.sources.merge sources
195
+ sseq.sources = self.sources | sources
175
196
  sseq
176
197
  end
177
198
 
@@ -180,16 +201,17 @@ module Sass
180
201
  def check_directives_match!(extend, parent_directives)
181
202
  dirs1 = extend.directives.map {|d| d.resolved_value}
182
203
  dirs2 = parent_directives.map {|d| d.resolved_value}
183
- return true if Sass::Util.subsequence?(dirs1, dirs2)
184
-
185
- Sass::Util.sass_warn <<WARNING
186
- DEPRECATION WARNING on line #{extend.node.line}#{" of #{extend.node.filename}" if extend.node.filename}:
187
- @extending an outer selector from within #{extend.directives.last.name} is deprecated.
188
- You may only @extend selectors within the same directive.
189
- This will be an error in Sass 3.3.
190
- It can only work once @extend is supported natively in the browser.
191
- WARNING
192
- return false
204
+ return if Sass::Util.subsequence?(dirs1, dirs2)
205
+ line = extend.node.line
206
+ filename = extend.node.filename
207
+
208
+ # TODO(nweiz): this should use the Sass stack trace of the extend node,
209
+ # not the selector.
210
+ raise Sass::SyntaxError.new(<<MESSAGE)
211
+ You may not @extend an outer selector from within #{extend.directives.last.name}.
212
+ You may only @extend selectors within the same directive.
213
+ From "@extend #{extend.target.join(', ')}" on line #{line}#{" of #{filename}" if filename}.
214
+ MESSAGE
193
215
  end
194
216
 
195
217
  def _hash
@@ -197,8 +219,8 @@ WARNING
197
219
  end
198
220
 
199
221
  def _eql?(other)
200
- other.base.eql?(self.base) && Sass::Util.set_eql?(other.rest, self.rest) &&
201
- other.subject? == self.subject?
222
+ other.base.eql?(base) && other.pseudo_elements == pseudo_elements &&
223
+ Sass::Util.set_eql?(other.rest, rest) && other.subject? == subject?
202
224
  end
203
225
  end
204
226
  end