module Sass # A namespace for nodes in the Sass parse tree. # # The Sass parse tree has three states: dynamic, static Sass, and static CSS. # # When it's first parsed, a Sass document is in the dynamic state. # It has nodes for mixin definitions and `@for` loops and so forth, # in addition to nodes for CSS rules and properties. # Nodes that only appear in this state are called **dynamic nodes**. # # {Tree::Visitors::Perform} creates a static Sass tree, which is # different. It still has nodes for CSS rules and properties but it # doesn't have any dynamic-generation-related nodes. The nodes in # this state are in a similar structure to the Sass document: rules # and properties are nested beneath one another, although the # {Tree::RuleNode} selectors are already in their final state. Nodes # that can be in this state or in the dynamic state are called # **static nodes**; nodes that can only be in this state are called # **solely static nodes**. # # {Tree::Visitors::Cssize} is then used to create a static CSS tree. # This is like a static Sass tree, # but the structure exactly mirrors that of the generated CSS. # Rules and properties can't be nested beneath one another in this state. # # Finally, {Tree::Visitors::ToCss} can be called on a static CSS tree # to get the actual CSS code as a string. module Tree # The abstract superclass of all parse-tree nodes. class Node include Enumerable # The child nodes of this node. # # @return [Array] attr_reader :children # Whether or not this node has child nodes. # This may be true even when \{#children} is empty, # in which case this node has an empty block (e.g. `{}`). # # @return [Boolean] attr_accessor :has_children # The line of the document on which this node appeared. # # @return [Fixnum] attr_accessor :line # The source range in the document on which this node appeared. # # @return [Sass::Source::Range] attr_accessor :source_range # The name of the document on which this node appeared. # # @return [String] attr_writer :filename # The options hash for the node. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}. # # @return [{Symbol => Object}] attr_reader :options def initialize @children = [] end # Sets the options hash for the node and all its children. # # @param options [{Symbol => Object}] The options # @see #options def options=(options) Sass::Tree::Visitors::SetOptions.visit(self, options) end # @private def children=(children) self.has_children ||= !children.empty? @children = children end # The name of the document on which this node appeared. # # @return [String] def filename @filename || (@options && @options[:filename]) end # Appends a child to the node. # # @param child [Tree::Node, Array] The child node or nodes # @raise [Sass::SyntaxError] if `child` is invalid def <<(child) return if child.nil? if child.is_a?(Array) child.each {|c| self << c} else self.has_children = true @children << child end end # Compares this node and another object (only other {Tree::Node}s will be equal). # This does a structural comparison; # if the contents of the nodes and all the child nodes are equivalent, # then the nodes are as well. # # Only static nodes need to override this. # # @param other [Object] The object to compare with # @return [Boolean] Whether or not this node and the other object # are the same # @see Sass::Tree def ==(other) self.class == other.class && other.children == children end # True if \{#to\_s} will return `nil`; # that is, if the node shouldn't be rendered. # Should only be called in a static tree. # # @return [Boolean] def invisible?; false; end # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}. # # @return [Symbol] def style @options[:style] end # Computes the CSS corresponding to this static CSS tree. # # @return [String] The resulting CSS # @see Sass::Tree def css Sass::Tree::Visitors::ToCss.new.visit(self) end # Computes the CSS corresponding to this static CSS tree, along with # the respective source map. # # @return [(String, Sass::Source::Map)] The resulting CSS and the source map # @see Sass::Tree def css_with_sourcemap visitor = Sass::Tree::Visitors::ToCss.new(:build_source_mapping) result = visitor.visit(self) return result, visitor.source_mapping end # Returns a representation of the node for debugging purposes. # # @return [String] def inspect return self.class.to_s unless has_children "(#{self.class} #{children.map {|c| c.inspect}.join(' ')})" end # Iterates through each node in the tree rooted at this node # in a pre-order walk. # # @yield node # @yieldparam node [Node] a node in the tree def each yield self children.each {|c| c.each {|n| yield n}} end # Converts a node to Sass code that will generate it. # # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}) # @return [String] The Sass code corresponding to the node def to_sass(options = {}) Sass::Tree::Visitors::Convert.visit(self, options, :sass) end # Converts a node to SCSS code that will generate it. # # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}) # @return [String] The Sass code corresponding to the node def to_scss(options = {}) Sass::Tree::Visitors::Convert.visit(self, options, :scss) end # Return a deep clone of this node. # The child nodes are cloned, but options are not. # # @return [Node] def deep_copy Sass::Tree::Visitors::DeepCopy.visit(self) end # Whether or not this node bubbles up through RuleNodes. # # @return [Boolean] def bubbles? false end protected # @see Sass::Shared.balance # @raise [Sass::SyntaxError] if the brackets aren't balanced def balance(*args) res = Sass::Shared.balance(*args) return res if res raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line) end end end end