sass 3.3.0.rc.2 → 3.3.0.rc.3

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 (76) hide show
  1. checksums.yaml +15 -0
  2. data/CONTRIBUTING +1 -1
  3. data/README.md +7 -7
  4. data/Rakefile +4 -2
  5. data/VERSION +1 -1
  6. data/VERSION_DATE +1 -1
  7. data/bin/sass +5 -1
  8. data/bin/sass-convert +5 -1
  9. data/bin/scss +5 -1
  10. data/ext/mkrf_conf.rb +23 -0
  11. data/lib/sass/css.rb +1 -1
  12. data/lib/sass/engine.rb +19 -9
  13. data/lib/sass/environment.rb +8 -0
  14. data/lib/sass/exec.rb +4 -4
  15. data/lib/sass/features.rb +0 -1
  16. data/lib/sass/importers/base.rb +13 -6
  17. data/lib/sass/importers/deprecated_path.rb +8 -2
  18. data/lib/sass/importers/filesystem.rb +33 -7
  19. data/lib/sass/logger.rb +1 -4
  20. data/lib/sass/logger/base.rb +0 -2
  21. data/lib/sass/logger/log_level.rb +0 -2
  22. data/lib/sass/plugin.rb +2 -2
  23. data/lib/sass/plugin/compiler.rb +23 -11
  24. data/lib/sass/plugin/configuration.rb +0 -1
  25. data/lib/sass/railtie.rb +1 -0
  26. data/lib/sass/script/css_lexer.rb +0 -1
  27. data/lib/sass/script/css_parser.rb +0 -1
  28. data/lib/sass/script/functions.rb +158 -96
  29. data/lib/sass/script/lexer.rb +29 -35
  30. data/lib/sass/script/parser.rb +10 -3
  31. data/lib/sass/script/tree.rb +0 -1
  32. data/lib/sass/script/tree/funcall.rb +21 -5
  33. data/lib/sass/script/tree/list_literal.rb +0 -1
  34. data/lib/sass/script/value/arg_list.rb +7 -3
  35. data/lib/sass/script/value/bool.rb +0 -1
  36. data/lib/sass/script/value/null.rb +0 -1
  37. data/lib/sass/script/value/number.rb +2 -6
  38. data/lib/sass/scss/css_parser.rb +0 -1
  39. data/lib/sass/scss/parser.rb +5 -5
  40. data/lib/sass/scss/script_lexer.rb +0 -1
  41. data/lib/sass/scss/script_parser.rb +0 -1
  42. data/lib/sass/selector.rb +11 -1
  43. data/lib/sass/selector/comma_sequence.rb +3 -4
  44. data/lib/sass/selector/sequence.rb +11 -7
  45. data/lib/sass/selector/simple_sequence.rb +42 -11
  46. data/lib/sass/source/map.rb +6 -19
  47. data/lib/sass/tree/at_root_node.rb +1 -1
  48. data/lib/sass/tree/mixin_node.rb +2 -2
  49. data/lib/sass/tree/prop_node.rb +0 -1
  50. data/lib/sass/tree/variable_node.rb +0 -5
  51. data/lib/sass/tree/visitors/check_nesting.rb +0 -1
  52. data/lib/sass/tree/visitors/convert.rb +2 -2
  53. data/lib/sass/tree/visitors/cssize.rb +184 -84
  54. data/lib/sass/tree/visitors/deep_copy.rb +0 -1
  55. data/lib/sass/tree/visitors/perform.rb +14 -44
  56. data/lib/sass/util.rb +59 -12
  57. data/lib/sass/util/cross_platform_random.rb +19 -0
  58. data/lib/sass/util/normalized_map.rb +17 -1
  59. data/test/sass/compiler_test.rb +10 -0
  60. data/test/sass/conversion_test.rb +36 -0
  61. data/test/sass/css2sass_test.rb +19 -0
  62. data/test/sass/engine_test.rb +54 -105
  63. data/test/sass/functions_test.rb +233 -26
  64. data/test/sass/importer_test.rb +72 -10
  65. data/test/sass/plugin_test.rb +14 -0
  66. data/test/sass/script_conversion_test.rb +4 -4
  67. data/test/sass/script_test.rb +58 -21
  68. data/test/sass/scss/css_test.rb +8 -1
  69. data/test/sass/scss/scss_test.rb +376 -179
  70. data/test/sass/source_map_test.rb +8 -0
  71. data/test/sass/templates/subdir/import_up1.scss +1 -0
  72. data/test/sass/templates/subdir/import_up2.scss +1 -0
  73. data/test/sass/util_test.rb +16 -0
  74. data/test/test_helper.rb +12 -4
  75. metadata +269 -287
  76. data/lib/sass/script/tree/selector.rb +0 -30
@@ -72,8 +72,7 @@ module Sass
72
72
 
73
73
  TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge(
74
74
  :const => "variable (e.g. $foo)",
75
- :ident => "identifier (e.g. middle)",
76
- :bool => "boolean (e.g. true, false)")
75
+ :ident => "identifier (e.g. middle)")
77
76
 
78
77
  # A list of operator strings ordered with longer names first
79
78
  # so that `>` and `<` don't clobber `>=` and `<=`.
@@ -90,11 +89,9 @@ module Sass
90
89
  :single_line_comment => SINGLE_LINE_COMMENT,
91
90
  :variable => /(\$)(#{IDENT})/,
92
91
  :ident => /(#{IDENT})(\()?/,
93
- :number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
92
+ :number => /(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
93
+ :unary_minus_number => /-(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
94
94
  :color => HEXCOLOR,
95
- :bool => /(true|false)\b/,
96
- :null => /null\b/,
97
- :selector => /&/,
98
95
  :ident_op => /(#{Regexp.union(*IDENT_OP_NAMES.map do |s|
99
96
  Regexp.new(Regexp.escape(s) + "(?!#{NMCHAR}|\Z)")
100
97
  end)})/,
@@ -102,7 +99,6 @@ module Sass
102
99
  }
103
100
 
104
101
  class << self
105
-
106
102
  private
107
103
 
108
104
  def string_re(open, close)
@@ -254,9 +250,9 @@ module Sass
254
250
  return string(interp_type, true)
255
251
  end
256
252
 
257
- variable || string(:double, false) || string(:single, false) || number ||
258
- color || bool || null || selector || string(:uri, false) ||
259
- raw(UNICODERANGE) || special_fun || special_val || ident_op || ident || op
253
+ variable || string(:double, false) || string(:single, false) || number || color ||
254
+ string(:uri, false) || raw(UNICODERANGE) || special_fun || special_val || ident_op ||
255
+ ident || op
260
256
  end
261
257
 
262
258
  def variable
@@ -292,10 +288,29 @@ module Sass
292
288
  end
293
289
 
294
290
  def number
295
- return unless scan(REGULAR_EXPRESSIONS[:number])
296
- value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
297
- value = -value if @scanner[1]
298
- script_number = Script::Value::Number.new(value, Array(@scanner[4]))
291
+ # Handling unary minus is complicated by the fact that whitespace is an
292
+ # operator in SassScript. We want "1-2" to be parsed as "1 - 2", but we
293
+ # want "1 -2" to be parsed as "1 (-2)". To accomplish this, we only
294
+ # parse a unary minus as part of a number literal if there's whitespace
295
+ # before and not after it. Cases like "(-2)" are handled by the unary
296
+ # minus logic in the parser instead.
297
+ if @scanner.peek(1) == '-'
298
+ return if @scanner.pos == 0
299
+ @scanner.pos -= 1
300
+ # Don't use @scanner.scan so we don't mess up the match data.
301
+ unary_minus_allowed = @scanner.peek(1) =~ /\s/
302
+ @scanner.pos += 1
303
+
304
+ return unless unary_minus_allowed
305
+ return unless scan(REGULAR_EXPRESSIONS[:unary_minus_number])
306
+ minus = true
307
+ else
308
+ return unless scan(REGULAR_EXPRESSIONS[:number])
309
+ minus = false
310
+ end
311
+
312
+ value = (@scanner[1] ? @scanner[1].to_f : @scanner[2].to_i) * (minus ? -1 : 1)
313
+ script_number = Script::Value::Number.new(value, Array(@scanner[3]))
299
314
  [:number, script_number]
300
315
  end
301
316
 
@@ -309,27 +324,6 @@ MESSAGE
309
324
  [:color, script_color]
310
325
  end
311
326
 
312
- def bool
313
- s = scan(REGULAR_EXPRESSIONS[:bool])
314
- return unless s
315
- script_bool = Script::Value::Bool.new(s == 'true')
316
- [:bool, script_bool]
317
- end
318
-
319
- def null
320
- return unless scan(REGULAR_EXPRESSIONS[:null])
321
- script_null = Script::Value::Null.new
322
- [:null, script_null]
323
- end
324
-
325
- def selector
326
- start_pos = source_position
327
- return unless scan(REGULAR_EXPRESSIONS[:selector])
328
- script_selector = Script::Tree::Selector.new
329
- script_selector.source_range = range(start_pos)
330
- [:selector, script_selector]
331
- end
332
-
333
327
  def special_fun
334
328
  str1 = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
335
329
  return unless str1
@@ -382,9 +382,16 @@ RUBY
382
382
 
383
383
  name = @lexer.next
384
384
  if (color = Sass::Script::Value::Color::COLOR_NAMES[name.value.downcase])
385
- return literal_node(Sass::Script::Value::Color.new(color), name.source_range)
385
+ literal_node(Sass::Script::Value::Color.new(color), name.source_range)
386
+ elsif name.value == "true"
387
+ literal_node(Sass::Script::Value::Bool.new(true), name.source_range)
388
+ elsif name.value == "false"
389
+ literal_node(Sass::Script::Value::Bool.new(false), name.source_range)
390
+ elsif name.value == "null"
391
+ literal_node(Sass::Script::Value::Null.new, name.source_range)
392
+ else
393
+ literal_node(Sass::Script::Value::String.new(name.value, :identifier), name.source_range)
386
394
  end
387
- literal_node(Script::Value::String.new(name.value, :identifier), name.source_range)
388
395
  end
389
396
 
390
397
  def funcall
@@ -543,7 +550,7 @@ RUBY
543
550
  end
544
551
 
545
552
  def literal
546
- t = try_toks(:color, :bool, :null)
553
+ t = try_tok(:color)
547
554
  return literal_node(t.value, t.source_range) if t
548
555
  end
549
556
 
@@ -13,4 +13,3 @@ require 'sass/script/tree/string_interpolation'
13
13
  require 'sass/script/tree/literal'
14
14
  require 'sass/script/tree/list_literal'
15
15
  require 'sass/script/tree/map_literal'
16
- require 'sass/script/tree/selector'
@@ -20,7 +20,7 @@ module Sass::Script::Tree
20
20
 
21
21
  # The keyword arguments to the function.
22
22
  #
23
- # @return [{String => Node}]
23
+ # @return [Sass::Util::NormalizedMap<Node>]
24
24
  attr_reader :keywords
25
25
 
26
26
  # The first splat argument for this function, if one exists.
@@ -128,7 +128,7 @@ module Sass::Script::Tree
128
128
  splat = Sass::Tree::Visitors::Perform.perform_splat(
129
129
  @splat, keywords, @kwarg_splat, environment)
130
130
  if (fn = environment.function(@name))
131
- return perform_sass_fn(fn, args, splat, environment)
131
+ return without_original(perform_sass_fn(fn, args, splat, environment))
132
132
  end
133
133
 
134
134
  args = construct_ruby_args(ruby_name, args, splat, environment)
@@ -136,8 +136,9 @@ module Sass::Script::Tree
136
136
  if Sass::Script::Functions.callable?(ruby_name)
137
137
  local_environment = Sass::Environment.new(environment.global_env, environment.options)
138
138
  local_environment.caller = Sass::ReadOnlyEnvironment.new(environment, environment.options)
139
- opts(Sass::Script::Functions::EvaluationContext.new(
139
+ result = opts(Sass::Script::Functions::EvaluationContext.new(
140
140
  local_environment).send(ruby_name, *args))
141
+ without_original(result)
141
142
  else
142
143
  opts(to_literal(args))
143
144
  end
@@ -173,6 +174,13 @@ module Sass::Script::Tree
173
174
  @signature ||= Sass::Script::Functions.signature(name.to_sym, @args.size, @keywords.size)
174
175
  end
175
176
 
177
+ def without_original(value)
178
+ return value unless value.is_a?(Sass::Script::Value::Number)
179
+ value = value.dup
180
+ value.original = nil
181
+ value
182
+ end
183
+
176
184
  def construct_ruby_args(name, args, splat, environment)
177
185
  args += splat.to_a if splat
178
186
 
@@ -198,9 +206,16 @@ module Sass::Script::Tree
198
206
  return args
199
207
  end
200
208
 
201
- args = args + (signature.args[args.size..-1] || []).map do |argname|
209
+ argnames = signature.args[args.size..-1] || []
210
+ deprecated_argnames = (signature.deprecated && signature.deprecated[args.size..-1]) || []
211
+ args = args + argnames.zip(deprecated_argnames).map do |(argname, deprecated_argname)|
202
212
  if keywords.has_key?(argname)
203
213
  keywords.delete(argname)
214
+ elsif deprecated_argname && keywords.has_key?(deprecated_argname)
215
+ deprecated_argname = keywords.denormalize(deprecated_argname)
216
+ Sass::Util.sass_warn("DEPRECATION WARNING: The `$#{deprecated_argname}' argument for " +
217
+ "`#{name}()' has been renamed to `$#{argname}'.")
218
+ keywords.delete(deprecated_argname)
204
219
  else
205
220
  raise Sass::SyntaxError.new("Function #{name} requires an argument named $#{argname}")
206
221
  end
@@ -208,7 +223,8 @@ module Sass::Script::Tree
208
223
 
209
224
  if keywords.size > 0
210
225
  if signature.var_kwargs
211
- args << keywords
226
+ # Don't pass a NormalizedMap to a Ruby function.
227
+ args << keywords.to_hash
212
228
  else
213
229
  argname = keywords.keys.sort.first
214
230
  if signature.args.include?(argname)
@@ -44,7 +44,6 @@ module Sass::Script::Tree
44
44
  members.join(sep_str(nil))
45
45
  end
46
46
 
47
-
48
47
  # @see Node#deep_copy
49
48
  def deep_copy
50
49
  node = dup
@@ -14,16 +14,20 @@ module Sass::Script::Value
14
14
  # Creates a new argument list.
15
15
  #
16
16
  # @param value [Array<Value>] See \{List#value}.
17
- # @param keywords [Hash<String, Value>] See \{#keywords}
17
+ # @param keywords [Hash<String, Value>, NormalizedMap<Value>] See \{#keywords}
18
18
  # @param separator [String] See \{List#separator}.
19
19
  def initialize(value, keywords, separator)
20
20
  super(value, separator)
21
- @keywords = keywords
21
+ if keywords.is_a?(Sass::Util::NormalizedMap)
22
+ @keywords = keywords
23
+ else
24
+ @keywords = Sass::Util::NormalizedMap.new(keywords)
25
+ end
22
26
  end
23
27
 
24
28
  # The keyword arguments attached to this list.
25
29
  #
26
- # @return [Hash<String, Value>]
30
+ # @return [NormalizedMap<Value>]
27
31
  def keywords
28
32
  @keywords_accessed = true
29
33
  @keywords
@@ -1,7 +1,6 @@
1
1
  module Sass::Script::Value
2
2
  # A SassScript object representing a boolean (true or false) value.
3
3
  class Bool < Base
4
-
5
4
  # The true value in SassScript.
6
5
  #
7
6
  # This is assigned before new is overridden below so that we use the default implementation.
@@ -1,7 +1,6 @@
1
1
  module Sass::Script::Value
2
2
  # A SassScript object representing a null value.
3
3
  class Null < Base
4
-
5
4
  # The null value in SassScript.
6
5
  #
7
6
  # This is assigned before new is overridden below so that we use the default implementation.
@@ -175,13 +175,9 @@ module Sass::Script::Value
175
175
  # @param other [Number] The right-hand side of the operator
176
176
  # @return [Number] This number modulo the other
177
177
  # @raise [NoMethodError] if `other` is an invalid type
178
- # @raise [Sass::UnitConversionError] if `other` has any units
178
+ # @raise [Sass::UnitConversionError] if `other` has incompatible units
179
179
  def mod(other)
180
180
  if other.is_a?(Number)
181
- unless other.unitless?
182
- raise Sass::UnitConversionError.new(
183
- "Cannot modulo by a number with units: #{other.inspect}.")
184
- end
185
181
  operate(other, :%)
186
182
  else
187
183
  raise NoMethodError.new(nil, :mod)
@@ -388,7 +384,7 @@ module Sass::Script::Value
388
384
  end
389
385
  end
390
386
 
391
- OPERATIONS = [:+, :-, :<=, :<, :>, :>=]
387
+ OPERATIONS = [:+, :-, :<=, :<, :>, :>=, :%]
392
388
 
393
389
  def operate(other, operation)
394
390
  this = self
@@ -7,7 +7,6 @@ module Sass
7
7
  # parent references, nested selectors, and so forth.
8
8
  # It does support all the same CSS hacks as the SCSS parser, though.
9
9
  class CssParser < StaticParser
10
-
11
10
  private
12
11
 
13
12
  def placeholder_selector; nil; end
@@ -5,7 +5,6 @@ module Sass
5
5
  # The parser for SCSS.
6
6
  # It parses a string of code into a tree of {Sass::Tree::Node}s.
7
7
  class Parser
8
-
9
8
  # Expose for the SASS parser.
10
9
  attr_accessor :offset
11
10
 
@@ -147,12 +146,12 @@ module Sass
147
146
  end
148
147
 
149
148
  def process_comment(text, node)
150
- silent = text =~ %r{^//}
151
- loud = !silent && text =~ %r{^/[/*]!}
149
+ silent = text =~ %r{\A//}
150
+ loud = !silent && text =~ %r{\A/[/*]!}
152
151
  line = @line - text.count("\n")
153
152
 
154
153
  if silent
155
- value = [text.sub(%r{^\s*//}, '/*').gsub(%r{^\s*//}, ' *') + ' */']
154
+ value = [text.sub(%r{\A\s*//}, '/*').gsub(%r{^\s*//}, ' *') + ' */']
156
155
  else
157
156
  value = Sass::Engine.parse_interp(
158
157
  text, line, @scanner.pos - text.size, :filename => @filename)
@@ -375,6 +374,7 @@ module Sass
375
374
  return unless (str = tok(STRING)) || (uri = tok?(/url\(/i))
376
375
  if uri
377
376
  str = sass_script(:parse_string)
377
+ ss
378
378
  media = media_query_list
379
379
  ss
380
380
  return node(Tree::CssImportNode.new(str, media.to_a), start_pos)
@@ -812,7 +812,7 @@ module Sass
812
812
 
813
813
  def parent_selector
814
814
  return unless tok(/&/)
815
- Selector::Parent.new
815
+ Selector::Parent.new(interp_ident(NAME) || [])
816
816
  end
817
817
 
818
818
  def class_selector
@@ -4,7 +4,6 @@ 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
-
8
7
  private
9
8
 
10
9
  def variable
@@ -6,7 +6,6 @@ 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
-
10
9
  private
11
10
 
12
11
  # @private
@@ -27,9 +27,19 @@ module Sass
27
27
  # The function of this is to be replaced by the parent selector
28
28
  # in the nested hierarchy.
29
29
  class Parent < Simple
30
+ # The identifier following the `&`. Often empty.
31
+ #
32
+ # @return [Array<String, Sass::Script::Tree::Node>]
33
+ attr_reader :suffix
34
+
35
+ # @param name [Array<String, Sass::Script::Tree::Node>] See \{#suffix}
36
+ def initialize(suffix = [])
37
+ @suffix = suffix
38
+ end
39
+
30
40
  # @see Selector#to_a
31
41
  def to_a
32
- ["&"]
42
+ ["&", *@suffix]
33
43
  end
34
44
 
35
45
  # Always raises an exception.
@@ -37,10 +37,9 @@ module Sass
37
37
  return self
38
38
  end
39
39
 
40
- CommaSequence.new(
41
- super_cseq.members.map do |super_seq|
42
- @members.map {|seq| seq.resolve_parent_refs(super_seq, implicit_parent)}
43
- end.flatten)
40
+ CommaSequence.new(Sass::Util.flatten_vertically(@members.map do |seq|
41
+ seq.resolve_parent_refs(super_cseq, implicit_parent).members
42
+ end))
44
43
  end
45
44
 
46
45
  # Non-destrucively extends this selector with the extensions specified in a hash
@@ -43,13 +43,13 @@ module Sass
43
43
  # by replacing them with the given parent selector,
44
44
  # handling commas appropriately.
45
45
  #
46
- # @param super_seq [Sequence] The parent selector sequence
46
+ # @param super_cseq [CommaSequence] The parent selector
47
47
  # @param implicit_parent [Boolean] Whether the the parent
48
48
  # selector should automatically be prepended to the resolved
49
49
  # selector if it contains no parent refs.
50
- # @return [Sequence] This selector, with parent references resolved
50
+ # @return [CommaSequence] This selector, with parent references resolved
51
51
  # @raise [Sass::SyntaxError] If a parent selector is invalid
52
- def resolve_parent_refs(super_seq, implicit_parent)
52
+ def resolve_parent_refs(super_cseq, implicit_parent)
53
53
  members = @members.dup
54
54
  nl = (members.first == "\n" && members.shift)
55
55
  contains_parent_ref = members.any? do |seq_or_op|
@@ -64,11 +64,15 @@ module Sass
64
64
  members += old_members
65
65
  end
66
66
 
67
- Sequence.new(
68
- members.map do |seq_or_op|
69
- next seq_or_op unless seq_or_op.is_a?(SimpleSequence)
70
- seq_or_op.resolve_parent_refs(super_seq)
67
+ CommaSequence.new(Sass::Util.paths(members.map do |sseq_or_op|
68
+ next [sseq_or_op] unless sseq_or_op.is_a?(SimpleSequence)
69
+ sseq_or_op.resolve_parent_refs(super_cseq).members
70
+ end).map do |path|
71
+ Sequence.new(path.map do |seq_or_op|
72
+ next seq_or_op unless seq_or_op.is_a?(Sequence)
73
+ seq_or_op.members
71
74
  end.flatten)
75
+ end)
72
76
  end
73
77
 
74
78
  # Non-destructively extends this selector with the extensions specified in a hash
@@ -77,21 +77,52 @@ module Sass
77
77
  # by replacing them with the given parent selector,
78
78
  # handling commas appropriately.
79
79
  #
80
- # @param super_seq [Sequence] The parent selector sequence
81
- # @return [Array<SimpleSequence>] This selector, with parent references resolved.
82
- # This is an array because the parent selector is itself a {Sequence}
80
+ # @param super_cseq [CommaSequence] The parent selector
81
+ # @return [CommaSequence] This selector, with parent references resolved
83
82
  # @raise [Sass::SyntaxError] If a parent selector is invalid
84
- def resolve_parent_refs(super_seq)
83
+ def resolve_parent_refs(super_cseq)
85
84
  # Parent selector only appears as the first selector in the sequence
86
- return [self] unless @members.first.is_a?(Parent)
87
-
88
- return super_seq.members if @members.size == 1
89
- unless super_seq.members.last.is_a?(SimpleSequence)
90
- raise Sass::SyntaxError.new("Invalid parent selector: " + super_seq.to_a.join)
85
+ unless (parent = @members.first).is_a?(Parent)
86
+ return CommaSequence.new([Sequence.new([self])])
91
87
  end
92
88
 
93
- super_seq.members[0...-1] +
94
- [SimpleSequence.new(super_seq.members.last.members + @members[1..-1], subject?)]
89
+ return super_cseq if @members.size == 1 && parent.suffix.empty?
90
+
91
+ CommaSequence.new(super_cseq.members.map do |super_seq|
92
+ unless super_seq.members.last.is_a?(SimpleSequence)
93
+ raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
94
+ super_seq.to_a.join + '"')
95
+ end
96
+
97
+ parent_sub = super_seq.members.last.members
98
+ unless parent.suffix.empty?
99
+ parent_sub = parent_sub.dup
100
+ parent_sub[-1] = parent_sub.last.dup
101
+ case parent_sub.last
102
+ when Sass::Selector::Class, Sass::Selector::Id, Sass::Selector::Placeholder
103
+ parent_sub[-1] = parent_sub.last.class.new(parent_sub.last.name + parent.suffix)
104
+ when Sass::Selector::Element
105
+ parent_sub[-1] = parent_sub.last.class.new(
106
+ parent_sub.last.name + parent.suffix,
107
+ parent_sub.last.namespace)
108
+ when Sass::Selector::Pseudo
109
+ if parent_sub.last.arg
110
+ raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
111
+ super_seq.to_a.join + '"')
112
+ end
113
+ parent_sub[-1] = parent_sub.last.class.new(
114
+ parent_sub.last.type,
115
+ parent_sub.last.name + parent.suffix,
116
+ nil)
117
+ else
118
+ raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
119
+ super_seq.to_a.join + '"')
120
+ end
121
+ end
122
+
123
+ Sequence.new(super_seq.members[0...-1] +
124
+ [SimpleSequence.new(parent_sub + @members[1..-1], subject?)])
125
+ end)
95
126
  end
96
127
 
97
128
  # Non-destrucively extends this selector with the extensions specified in a hash