class Sass::CSS

Sass::CSS.new(“p { color: blue }”).render #=> “pn color: blue”
Example usage:
to produce more concise and idiomatic Sass.
and then applying various transformations to the structure
It works by parsing the CSS document into a {Sass::Tree} structure,
This class converts CSS documents into Sass templates.

def assert_match(re)

Parameters:
  • re (Regexp) -- The regular expression to assert
def assert_match(re)
  if @template.scan(re)
    whitespace
    return
  end
  line = @template.string[0..@template.pos].count "\n"
  pos = @template.pos
  after = @template.string[pos - 15...pos]
  after = "..." + after if pos >= 15
  # Display basic regexps as plain old strings
  expected = re.source == Regexp.escape(re.source) ? "\"#{re.source}\"" : re.inspect
  was = @template.rest[0...15]
  was += "..." if @template.rest.size >= 15
  raise Exception.new(<<MESSAGE)
lid CSS on line #{line + 1} after #{after.inspect}:
pected #{expected}, was #{was.inspect}
AGE
end

def build_tree

Returns:
  • (Tree::Node) - The root node of the parsed tree
def build_tree
  root = Tree::Node.new
  whitespace
  rules              root
  expand_commas      root
  parent_ref_rules   root
  remove_parent_refs root
  flatten_rules      root
  fold_commas        root
  root
end

def expand_commas(root)

Parameters:
  • root (Tree::Node) -- The parent node
def expand_commas(root)
  root.children.map! do |child|
    next child unless Tree::RuleNode === child && child.rules.first.include?(',')
    child.rules.first.split(',').map do |rule|
      node = Tree::RuleNode.new(rule.strip)
      node.children = child.children
      node
    end
  end
  root.children.flatten!
end

def flatten_rule(rule)

Other tags:
    See: #flatten_rules -

Parameters:
  • rule (Tree::RuleNode) -- The candidate for flattening
def flatten_rule(rule)
  while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
    child = rule.children.first
    if child.rules.first[0] == ?&
      rule.rules = [child.rules.first.gsub(/^&/, rule.rules.first)]
    else
      rule.rules = ["#{rule.rules.first} #{child.rules.first}"]
    end
    rule.children = child.children
  end
  flatten_rules(rule)
end

def flatten_rules(root)

Parameters:
  • root (Tree::Node) -- The parent node
def flatten_rules(root)
  root.children.each { |child| flatten_rule(child) if child.is_a?(Tree::RuleNode) }
end

def fold_commas(root)

Parameters:
  • rule (Tree::RuleNode) -- The candidate for flattening
def fold_commas(root)
  prev_rule = nil
  root.children.map! do |child|
    next child unless child.is_a?(Tree::RuleNode)
    if prev_rule && prev_rule.children == child.children
      prev_rule.rules.first << ", #{child.rules.first}"
      next nil
    end
    fold_commas(child)
    prev_rule = child
    child
  end
  root.children.compact!
end

def initialize(template, options = {})

Options Hash: (**options)
  • :old (Boolean) --

Parameters:
  • template (String) -- The CSS code
def initialize(template, options = {})
  if template.is_a? IO
    template = template.read
  end
  @options = options.dup
  # Backwards compatibility
  @options[:old] = true if @options[:alternate] == false
  @template = StringScanner.new(template)
end

def parent_ref_rules(root)

Parameters:
  • root (Tree::Node) -- The parent node
def parent_ref_rules(root)
  current_rule = nil
  root.children.select { |c| Tree::RuleNode === c }.each do |child|
    root.children.delete child
    first, rest = child.rules.first.scan(/^(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?$/).first
    if current_rule.nil? || current_rule.rules.first != first
      current_rule = Tree::RuleNode.new(first)
      root << current_rule
    end
    if rest
      child.rules = ["&" + rest]
      current_rule << child
    else
      current_rule.children += child.children
    end
  end
  root.children.each { |v| parent_ref_rules(v) }
end

def properties(rule)

Parameters:
  • rule (Tree::RuleNode) -- The parent node of the properties
def properties(rule)
  while @template.scan(/[^:\}\s]+/)
    name = @template[0]
    whitespace
    assert_match /:/
    value = ''
    while @template.scan(/[^;\s\}]+/)
      value << @template[0] << whitespace
    end
    assert_match /(;|(?=\}))/
    rule << Tree::PropNode.new(name, value, nil)
  end
  assert_match /\}/
end

def remove_parent_refs(root)

Parameters:
  • root (Tree::Node) -- The parent node
def remove_parent_refs(root)
  root.children.each do |child|
    if child.is_a?(Tree::RuleNode)
      child.rules.first.gsub! /^& +/, ''
      remove_parent_refs child
    end
  end
end

def render

Returns:
  • (String) - The resulting Sass code
def render
  begin
    build_tree.to_sass(0, @options).strip + "\n"
  rescue Exception => err
    line = @template.string[0...@template.pos].split("\n").size
    err.backtrace.unshift "(css):#{line}"
    raise err
  end
end

def rule

Returns:
  • (Tree::Node) - The parsed rule
def rule
  rule = ""
  loop do
    token = @template.scan(/(?:[^\{\};\/\s]|\/[^*])+/)
    if token.nil?
      return if rule.empty?
      break
    end
    rule << token
    break unless @template.match?(/\s|\/\*/)
    whitespace
    rule << " "
  end
  rule.strip!
  directive = rule[0] == ?@
  if directive
    node = Tree::DirectiveNode.new(rule)
    return node if @template.scan(/;/)
    assert_match /\{/
    whitespace
    rules(node)
    return node
  end
  assert_match /\{/
  node = Tree::RuleNode.new(rule)
  properties(node)
  return node
end

def rules(root)

Parameters:
  • root (Tree::Node) -- The parent node of the rules
def rules(root)
  while r = rule
    root << r
    whitespace
  end
end

def whitespace

Returns:
  • (String) - The ignored whitespace
def whitespace
  space = @template.scan(/\s*/) || ''
  # If we've hit a comment,
  # go past it and look for more whitespace
  if @template.scan(/\/\*/)
    @template.scan_until(/\*\//)
    return space + whitespace
  end
  return space
end