From 8e787b51b6ec1c562757fb470b937e602e89e17c Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Sat, 10 Apr 2010 11:51:43 -0700 Subject: [PATCH] [Sass] Keep track of the runtime stack so it can be inspected without unwinding. --- lib/sass/environment.rb | 46 +++++++++++++++++++++++++++++++++++- lib/sass/tree/import_node.rb | 3 +++ lib/sass/tree/mixin_node.rb | 37 ++++++++++++++++------------- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/lib/sass/environment.rb b/lib/sass/environment.rb index 4f0c577c..e79a4389 100644 --- a/lib/sass/environment.rb +++ b/lib/sass/environment.rb @@ -23,7 +23,8 @@ module Sass @vars = {} @mixins = {} @parent = parent - + @stack = [] + @ignore_parent_stack = false set_var("important", Script::String.new("!important")) unless @parent end @@ -35,6 +36,49 @@ module Sass @options || (parent && parent.options) || {} end + # Push lexical frame information onto the runtime stack. + # @param frame_info [{Symbol => Object}] + # Frame information has the following keys: + # + # `:filename` + # : The name of the file in which the lexical scope changed. + # + # `:mixin` + # : The name of the mixin in which the lexical scope changed, + # or `nil` if it wasn't within in a mixin. + # + # `:line` + # : The line of the file on which the lexical scope changed. Never nil. + # + # `:import` + # : Set to `true` when the lexical scope is changing due to an import. + def push(frame_info) + @stack.push frame_info + end + + # Pop runtime frame information from the stack. + def pop + @stack.pop + end + + # A list of the runtime stack frame information + # The last element in the list was pushed onto the stack most recently. + def stack + prev = (!@ignore_parent_stack && parent && parent.stack) || [] + prev + @stack + end + + # Temporarily assume the runtime stack that is passed in. + # @param stk A stack value from another environment. + def with_stack(stk) + @stack, old_stack = stk, @stack + @ignore_parent_stack = true + yield self + ensure + @ignore_parent_stack = false + @stack = old_stack + end + class << self private diff --git a/lib/sass/tree/import_node.rb b/lib/sass/tree/import_node.rb index 6378c713..0c4f2009 100644 --- a/lib/sass/tree/import_node.rb +++ b/lib/sass/tree/import_node.rb @@ -62,6 +62,7 @@ module Sass # @param environment [Sass::Environment] The lexical environment containing # variable and mixin values def perform!(environment) + environment.push(:filename => @filename, :line => @line, :import => true) root = Sass::Files.tree_for(full_filename, @options) @template = root.template self.children = root.children @@ -70,6 +71,8 @@ module Sass e.modify_backtrace(:filename => full_filename) e.add_backtrace(:filename => @filename, :line => @line) raise e + ensure + environment.pop end private diff --git a/lib/sass/tree/mixin_node.rb b/lib/sass/tree/mixin_node.rb index 8b3f4701..e2882ec2 100644 --- a/lib/sass/tree/mixin_node.rb +++ b/lib/sass/tree/mixin_node.rb @@ -50,6 +50,8 @@ module Sass::Tree # @raise [Sass::SyntaxError] if an incorrect number of arguments was passed # @see Sass::Tree def perform!(environment) + original_env = environment + original_env.push(:filename => filename, :mixin => @name, :line => line) raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.") unless mixin = environment.mixin(@name) raise Sass::SyntaxError.new(< e e.modify_backtrace(:mixin => @name, :line => @line) e.add_backtrace(:line => @line) raise e + ensure + original_env.pop end end end