sass 3.3.0.alpha.229 → 3.3.0.alpha.231

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.
data/REVISION CHANGED
@@ -1 +1 @@
1
- 7ff8fef1739f20466b1b9e9330a99fe4f7ca83e9
1
+ 4b12122cb838c1c8b2488b2c0bb3f252d261de2a
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0.alpha.229
1
+ 3.3.0.alpha.231
@@ -1 +1 @@
1
- 12 August 2013 23:04:10 GMT
1
+ 23 August 2013 23:13:38 GMT
@@ -42,6 +42,7 @@ require 'sass/selector'
42
42
  require 'sass/environment'
43
43
  require 'sass/script'
44
44
  require 'sass/scss'
45
+ require 'sass/stack'
45
46
  require 'sass/error'
46
47
  require 'sass/importers'
47
48
  require 'sass/shared'
@@ -42,6 +42,20 @@ module Sass::Script::Value
42
42
  return value.reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.map {|e| e.to_s(opts)}.join(sep_str)
43
43
  end
44
44
 
45
+ # @see Value#to_sass
46
+ def to_sass(opts = {})
47
+ return "()" if value.empty?
48
+ precedence = Sass::Script::Parser.precedence_of(separator)
49
+ value.reject {|e| e.is_a?(Null)}.map do |v|
50
+ if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
51
+ separator == :space && v.is_a?(UnaryOperation) && (v.operator == :minus || v.operator == :plus)
52
+ "(#{v.to_sass(opts)})"
53
+ else
54
+ v.to_sass(opts)
55
+ end
56
+ end.join(sep_str(nil))
57
+ end
58
+
45
59
  # @see Value#inspect
46
60
  def inspect
47
61
  "(#{value.map {|e| e.inspect}.join(sep_str(nil))})"
@@ -0,0 +1,111 @@
1
+ module Sass
2
+ # A class representing the stack when compiling a Sass file.
3
+ class Stack
4
+ # TODO: use this to generate stack information for Sass::SyntaxErrors.
5
+
6
+ # A single stack frame.
7
+ class Frame
8
+ # The filename of the file in which this stack frame was created.
9
+ #
10
+ # @return [String]
11
+ attr_reader :filename
12
+
13
+ # The line number on which this stack frame was created.
14
+ #
15
+ # @return [String]
16
+ attr_reader :line
17
+
18
+ # The type of this stack frame. This can be `:import`, `:mixin`, or
19
+ # `:base`.
20
+ #
21
+ # `:base` indicates that this is the bottom-most frame, meaning that it
22
+ # represents a single line of code rather than a nested context. The stack
23
+ # will only ever have one base frame, and it will always be the most
24
+ # deeply-nested frame.
25
+ #
26
+ # @return [Symbol?]
27
+ attr_reader :type
28
+
29
+ # The name of the stack frame. For mixin frames, this is the mixin name;
30
+ # otherwise, it's `nil`.
31
+ #
32
+ # @return [String?]
33
+ attr_reader :name
34
+
35
+ def initialize(filename, line, type, name = nil)
36
+ @filename = filename
37
+ @line = line
38
+ @type = type
39
+ @name = name
40
+ end
41
+
42
+ # Whether this frame represents an import.
43
+ #
44
+ # @return [Boolean]
45
+ def is_import?
46
+ type == :import
47
+ end
48
+
49
+ # Whether this frame represents a mixin.
50
+ #
51
+ # @return [Boolean]
52
+ def is_mixin?
53
+ type == :mixin
54
+ end
55
+
56
+ # Whether this is the base frame.
57
+ #
58
+ # @return [Boolean]
59
+ def is_base?
60
+ type == :base
61
+ end
62
+ end
63
+
64
+ # The stack frames. The last frame is the most deeply-nested.
65
+ #
66
+ # @return [Array<Frame>]
67
+ attr_reader :frames
68
+
69
+ def initialize
70
+ @frames = []
71
+ end
72
+
73
+ # Pushes a base frame onto the stack.
74
+ #
75
+ # @param filename [String] See \{Frame#filename}.
76
+ # @param line [String] See \{Frame#line}.
77
+ # @yield [] A block in which the new frame is on the stack.
78
+ def with_base(filename, line)
79
+ with_frame(filename, line, :base) {yield}
80
+ end
81
+
82
+ # Pushes an import frame onto the stack.
83
+ #
84
+ # @param filename [String] See \{Frame#filename}.
85
+ # @param line [String] See \{Frame#line}.
86
+ # @yield [] A block in which the new frame is on the stack.
87
+ def with_import(filename, line)
88
+ with_frame(filename, line, :import) {yield}
89
+ end
90
+
91
+ # Pushes a mixin frame onto the stack.
92
+ #
93
+ # @param filename [String] See \{Frame#filename}.
94
+ # @param line [String] See \{Frame#line}.
95
+ # @param name [String] See \{Frame#name}.
96
+ # @yield [] A block in which the new frame is on the stack.
97
+ def with_mixin(filename, line, name)
98
+ with_frame(filename, line, :mixin, name) {yield}
99
+ end
100
+
101
+ private
102
+
103
+ def with_frame(filename, line, type, name = nil)
104
+ @frames.pop if @frames.last && @frames.last.type == :base
105
+ @frames.push(Frame.new(filename, line, type, name))
106
+ yield
107
+ ensure
108
+ @frames.pop unless type == :base && @frames.last && @frames.last.type != :base
109
+ end
110
+ end
111
+ end
@@ -91,13 +91,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
91
91
 
92
92
  def initialize(env)
93
93
  @environment = env
94
- # Stack trace information, including mixin includes and imports.
95
- @stack = []
94
+ @stack = Sass::Stack.new
96
95
  end
97
96
 
98
97
  # If an exception is raised, this adds proper metadata to the backtrace.
99
98
  def visit(node)
100
- super(node.dup)
99
+ @stack.with_base(node.filename, node.line) {super(node.dup)}
101
100
  rescue Sass::SyntaxError => e
102
101
  e.modify_backtrace(:filename => node.filename, :line => node.line)
103
102
  raise e
@@ -221,21 +220,22 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
221
220
  return resolved_node
222
221
  end
223
222
  file = node.imported_file
224
- handle_import_loop!(node) if @stack.any? {|e| e[:filename] == file.options[:filename]}
223
+ if @stack.frames.any? {|f| f.is_import? && f.filename == file.options[:filename]}
224
+ handle_import_loop!(node)
225
+ end
225
226
 
226
227
  begin
227
- @stack.push(:filename => node.filename, :line => node.line)
228
- root = file.to_tree
229
- Sass::Tree::Visitors::CheckNesting.visit(root)
230
- node.children = root.children.map {|c| visit(c)}.flatten
231
- node
228
+ @stack.with_import(node.filename, node.line) do
229
+ root = file.to_tree
230
+ Sass::Tree::Visitors::CheckNesting.visit(root)
231
+ node.children = root.children.map {|c| visit(c)}.flatten
232
+ node
233
+ end
232
234
  rescue Sass::SyntaxError => e
233
235
  e.modify_backtrace(:filename => node.imported_file.options[:filename])
234
236
  e.add_backtrace(:filename => node.filename, :line => node.line)
235
237
  raise e
236
238
  end
237
- ensure
238
- @stack.pop unless path
239
239
  end
240
240
 
241
241
  # Loads a mixin into the environment.
@@ -249,27 +249,28 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
249
249
  # Runs a mixin.
250
250
  def visit_mixin(node)
251
251
  include_loop = true
252
- handle_include_loop!(node) if @stack.any? {|e| e[:name] == node.name}
252
+ handle_include_loop!(node) if @stack.frames.any? {|f| f.is_mixin? && f.name == node.name}
253
253
  include_loop = false
254
254
 
255
- @stack.push(:filename => node.filename, :line => node.line, :name => node.name)
256
- raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
255
+ @stack.with_mixin(node.filename, node.line, node.name) do
256
+ raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
257
257
 
258
- if node.children.any? && !mixin.has_content
259
- raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
260
- end
258
+ if node.children.any? && !mixin.has_content
259
+ raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
260
+ end
261
261
 
262
- args = node.args.map {|a| a.perform(@environment)}
263
- keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
264
- splat = node.splat.perform(@environment) if node.splat
262
+ args = node.args.map {|a| a.perform(@environment)}
263
+ keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
264
+ splat = node.splat.perform(@environment) if node.splat
265
265
 
266
- self.class.perform_arguments(mixin, args, keywords, splat) do |env|
267
- env.caller = Sass::Environment.new(@environment)
268
- env.content = node.children if node.has_children
266
+ self.class.perform_arguments(mixin, args, keywords, splat) do |env|
267
+ env.caller = Sass::Environment.new(@environment)
268
+ env.content = node.children if node.has_children
269
269
 
270
- trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
271
- with_environment(env) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
272
- trace_node
270
+ trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
271
+ with_environment(env) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
272
+ trace_node
273
+ end
273
274
  end
274
275
  rescue Sass::SyntaxError => e
275
276
  unless include_loop
@@ -277,22 +278,19 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
277
278
  e.add_backtrace(:line => node.line)
278
279
  end
279
280
  raise e
280
- ensure
281
- @stack.pop unless include_loop
282
281
  end
283
282
 
284
283
  def visit_content(node)
285
284
  return [] unless content = @environment.content
286
- @stack.push(:filename => node.filename, :line => node.line, :name => '@content')
287
- trace_node = Sass::Tree::TraceNode.from_node('@content', node)
288
- with_environment(@environment.caller) {trace_node.children = content.map {|c| visit(c.dup)}.flatten}
289
- trace_node
285
+ @stack.with_mixin(node.filename, node.line, '@content') do
286
+ trace_node = Sass::Tree::TraceNode.from_node('@content', node)
287
+ with_environment(@environment.caller) {trace_node.children = content.map {|c| visit(c.dup)}.flatten}
288
+ trace_node
289
+ end
290
290
  rescue Sass::SyntaxError => e
291
291
  e.modify_backtrace(:mixin => '@content', :line => node.line)
292
292
  e.add_backtrace(:line => node.line)
293
293
  raise e
294
- ensure
295
- @stack.pop if content
296
294
  end
297
295
 
298
296
  # Runs any SassScript that may be embedded in a property.
@@ -317,11 +315,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
317
315
  parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
318
316
  node.filename, node.options[:importer], node.line)
319
317
  node.parsed_rules ||= parser.parse_selector
320
- if node.options[:trace_selectors]
321
- @stack.push(:filename => node.filename, :line => node.line)
322
- node.stack_trace = stack_trace
323
- @stack.pop
324
- end
318
+ node.stack_trace = stack_trace if node.options[:trace_selectors]
325
319
  yield
326
320
  end
327
321
 
@@ -341,15 +335,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
341
335
 
342
336
  # Prints the expression to STDERR with a stylesheet trace.
343
337
  def visit_warn(node)
344
- @stack.push(:filename => node.filename, :line => node.line)
345
338
  res = node.expr.perform(@environment)
346
339
  res = res.value if res.is_a?(Sass::Script::Value::String)
347
340
  msg = "WARNING: #{res}\n "
348
341
  msg << stack_trace.join("\n ") << "\n"
349
342
  Sass::Util.sass_warn msg
350
343
  []
351
- ensure
352
- @stack.pop
353
344
  end
354
345
 
355
346
  # Runs the child nodes until the continuation expression becomes false.
@@ -392,16 +383,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
392
383
  private
393
384
 
394
385
  def stack_trace
395
- trace = []
396
- stack = @stack.map {|e| e.dup}.reverse
397
- stack.each_cons(2) {|(e1, e2)| e1[:caller] = e2[:name]; [e1, e2]}
398
- stack.each_with_index do |entry, i|
399
- msg = "#{i == 0 ? "on" : "from"} line #{entry[:line]}"
400
- msg << " of #{entry[:filename] || "an unknown file"}"
401
- msg << ", in `#{entry[:caller]}'" if entry[:caller]
402
- trace << msg
386
+ Sass::Util.enum_with_index(Sass::Util.enum_cons(@stack.frames.reverse + [nil], 2)).
387
+ map do |(frame, caller), i|
388
+ "#{i == 0 ? "on" : "from"} line #{frame.line}" +
389
+ " of #{frame.filename || "an unknown file"}" +
390
+ (caller && caller.name ? ", in `#{caller.name}'" : "")
403
391
  end
404
- trace
405
392
  end
406
393
 
407
394
  def run_interp_no_strip(text)
@@ -421,8 +408,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
421
408
  def handle_include_loop!(node)
422
409
  msg = "An @include loop has been found:"
423
410
  content_count = 0
424
- mixins = @stack.reverse.map {|s| s[:name]}.compact.select do |s|
425
- if s == '@content'
411
+ mixins = @stack.frames.select {|f| f.is_mixin?}.reverse.map {|f| f.name}.select do |name|
412
+ if name == '@content'
426
413
  content_count += 1
427
414
  false
428
415
  elsif content_count > 0
@@ -444,7 +431,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
444
431
 
445
432
  def handle_import_loop!(node)
446
433
  msg = "An @import loop has been found:"
447
- files = @stack.map {|s| s[:filename]}.compact
434
+ files = @stack.frames.select {|f| f.is_import?}.map {|f| f.filename}.compact
448
435
  if node.filename == node.imported_file.options[:filename]
449
436
  raise Sass::SyntaxError.new("#{msg} #{node.filename} imports itself")
450
437
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- hash: 592302791
4
+ hash: 592302787
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 3
9
9
  - 0
10
10
  - alpha
11
- - 229
12
- version: 3.3.0.alpha.229
11
+ - 231
12
+ version: 3.3.0.alpha.231
13
13
  platform: ruby
14
14
  authors:
15
15
  - Nathan Weizenbaum
@@ -19,7 +19,7 @@ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2013-08-12 00:00:00 -04:00
22
+ date: 2013-08-23 00:00:00 -04:00
23
23
  default_executable:
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
@@ -192,6 +192,7 @@ files:
192
192
  - lib/sass/tree/visitors/to_css.rb
193
193
  - lib/sass/tree/warn_node.rb
194
194
  - lib/sass/tree/while_node.rb
195
+ - lib/sass/stack.rb
195
196
  - lib/sass/util/multibyte_string_scanner.rb
196
197
  - lib/sass/util/normalized_map.rb
197
198
  - lib/sass/util/ordered_hash.rb