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

Sign up to get free protection for your applications and to get access to all the features.
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