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

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