# Visitors are used to traverse the Sass parse tree. # Visitors should extend {Visitors::Base}, # which provides a small amount of scaffolding for traversal. module Sass::Tree::Visitors # The abstract base class for Sass visitors. # Visitors should extend this class, # then implement `visit_*` methods for each node they care about # (e.g. `visit_rule` for {RuleNode} or `visit_for` for {ForNode}). # These methods take the node in question as argument. # They may `yield` to visit the child nodes of the current node. # # *Note*: due to the unusual nature of {Sass::Tree::IfNode}, # special care must be taken to ensure that it is properly handled. # In particular, there is no built-in scaffolding # for dealing with the return value of `@else` nodes. # # @abstract class Base # Runs the visitor on a tree. # # @param root [Tree::Node] The root node of the Sass tree. # @return [Object] The return value of \{#visit} for the root node. def self.visit(root) new.send(:visit, root) end protected # Runs the visitor on the given node. # This can be overridden by subclasses that need to do something for each node. # # @param node [Tree::Node] The node to visit. # @return [Object] The return value of the `visit_*` method for this node. def visit(node) if respond_to?(node.class.visit_method, true) send(node.class.visit_method, node) {visit_children(node)} else visit_children(node) end end # Visit the child nodes for a given node. # This can be overridden by subclasses that need to do something # with the child nodes' return values. # # This method is run when `visit_*` methods `yield`, # and its return value is returned from the `yield`. # # @param parent [Tree::Node] The parent node of the children to visit. # @return [Array] The return values of the `visit_*` methods for the children. def visit_children(parent) parent.children.map {|c| visit(c)} end # Returns the name of a node as used in the `visit_*` method. # # @param [Tree::Node] node The node. # @return [String] The name. def self.node_name(node) Sass::Util.deprecated(self, "Call node.class.node_name instead.") node.class.node_name end # `yield`s, then runs the visitor on the `@else` clause if the node has one. # This exists to ensure that the contents of the `@else` clause get visited. def visit_if(node) yield visit(node.else) if node.else node end end end