class Sass::Tree::Visitors::Convert
A visitor for converting a Sass
tree into a source string.
Public Class Methods
new(options, format)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 15 def initialize(options, format) @options = options @format = format @tabs = 0 # 2 spaces by default @tab_chars = @options[:indent] || " " @is_else = false end
visit(root, options, format)
click to toggle source
Runs the visitor on a tree.
@param root [Tree::Node] The root node of the Sass
tree. @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}). @param format [Symbol] ‘:sass` or `:scss`. @return [String] The Sass
or SCSS source for the tree.
# File lib/sass/tree/visitors/convert.rb, line 9 def self.visit(root, options, format) new(options, format).send(:visit, root) end
Protected Instance Methods
visit_atroot(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 263 def visit_atroot(node) if node.query "#{tab_str}@at-root #{query_interp_to_src(node.query)}#{yield}" elsif node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode) rule = node.children.first "#{tab_str}@at-root #{selector_to_src(rule.rule).lstrip}#{visit_children(rule)}" else "#{tab_str}@at-root#{yield}" end end
visit_charset(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 44 def visit_charset(node) "#{tab_str}@charset \"#{node.name}\"#{semi}\n" end
visit_children(parent)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 24 def visit_children(parent) @tabs += 1 return @format == :sass ? "\n" : " {}\n" if parent.children.empty? res = visit_rule_level(parent.children) if @format == :sass "\n" + res.rstrip + "\n" else " {\n" + res.rstrip + "\n#{@tab_chars * (@tabs - 1)}}\n" end ensure @tabs -= 1 end
visit_comment(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 48 def visit_comment(node) value = interp_to_src(node.value) if @format == :sass content = value.gsub(%r{\*/$}, '').rstrip if content =~ /\A[ \t]/ # Re-indent SCSS comments like this: # /* foo # bar # baz */ content.gsub!(/^/, ' ') content.sub!(%r{\A([ \t]*)/\*}, '/*\1') end if content.include?("\n") content.gsub!(/\n \*/, "\n ") spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min sep = node.type == :silent ? "\n//" : "\n *" if spaces >= 2 content.gsub!(/\n /, sep) else content.gsub!(/\n#{' ' * spaces}/, sep) end end content.gsub!(%r{\A/\*}, '//') if node.type == :silent content.gsub!(/^/, tab_str) content = content.rstrip + "\n" else spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max) content = if node.type == :silent value.gsub(%r{^[/ ]\*}, '//').gsub(%r{ *\*/$}, '') else value end.gsub(/^/, spaces) + "\n" end content end
visit_content(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 219 def visit_content(node) "#{tab_str}@content#{semi}\n" end
visit_cssimport(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 161 def visit_cssimport(node) if node.uri.is_a?(Sass::Script::Tree::Node) str = "#{tab_str}@import #{node.uri.to_sass(@options)}" else str = "#{tab_str}@import #{node.uri}" end str << " supports(#{node.supports_condition.to_src(@options)})" if node.supports_condition str << " #{interp_to_src(node.query)}" unless node.query.empty? "#{str}#{semi}\n" end
visit_debug(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 86 def visit_debug(node) "#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n" end
visit_directive(node) { || ... }
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 94 def visit_directive(node) res = "#{tab_str}#{interp_to_src(node.value)}" res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2') return res + "#{semi}\n" unless node.has_children res + yield end
visit_each(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 101 def visit_each(node) vars = node.vars.map {|var| "$#{dasherize(var)}"}.join(", ") "#{tab_str}@each #{vars} in #{node.list.to_sass(@options)}#{yield}" end
visit_error(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 90 def visit_error(node) "#{tab_str}@error #{node.expr.to_sass(@options)}#{semi}\n" end
visit_extend(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 106 def visit_extend(node) "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}" + "#{' !optional' if node.optional?}#{semi}\n" end
visit_for(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 111 def visit_for(node) "#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " + "#{node.exclusive ? 'to' : 'through'} #{node.to.to_sass(@options)}#{yield}" end
visit_function(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 116 def visit_function(node) args = node.args.map do |v, d| d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options) end.join(", ") if node.splat args << ", " unless node.args.empty? args << node.splat.to_sass(@options) << "..." end "#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}" end
visit_if(node) { || ... }
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 128 def visit_if(node) name = if !@is_else "if" elsif node.expr "else if" else "else" end @is_else = false str = "#{tab_str}@#{name}" str << " #{node.expr.to_sass(@options)}" if node.expr str << yield @is_else = true str << visit(node.else) if node.else str ensure @is_else = false end
visit_import(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 148 def visit_import(node) quote = @format == :scss ? '"' : '' "#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n" end
visit_keyframerule(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 274 def visit_keyframerule(node) "#{tab_str}#{node.resolved_value}#{yield}" end
visit_media(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 153 def visit_media(node) "#{tab_str}@media #{query_interp_to_src(node.query)}#{yield}" end
visit_mixin(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 197 def visit_mixin(node) arg_to_sass = lambda do |arg| sass = arg.to_sass(@options) sass = "(#{sass})" if arg.is_a?(Sass::Script::Tree::ListLiteral) && arg.separator == :comma sass end unless node.args.empty? && node.keywords.empty? && node.splat.nil? args = node.args.map(&arg_to_sass) keywords = node.keywords.as_stored.to_a.map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"} if node.splat splat = "#{arg_to_sass[node.splat]}..." kwarg_splat = "#{arg_to_sass[node.kwarg_splat]}..." if node.kwarg_splat end arglist = "(#{[args, splat, keywords, kwarg_splat].flatten.compact.join(', ')})" end "#{tab_str}#{@format == :sass ? '+' : '@include '}" + "#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n" end
visit_mixindef(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 172 def visit_mixindef(node) args = if node.args.empty? && node.splat.nil? "" else str = '(' str << node.args.map do |v, d| if d "#{v.to_sass(@options)}: #{d.to_sass(@options)}" else v.to_sass(@options) end end.join(", ") if node.splat str << ", " unless node.args.empty? str << node.splat.to_sass(@options) << '...' end str << ')' end "#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}" end
visit_prop(node) { || ... }
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 223 def visit_prop(node) res = tab_str + node.declaration(@options, @format) return res + semi + "\n" if node.children.empty? res + yield.rstrip + semi + "\n" end
visit_return(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 229 def visit_return(node) "#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n" end
visit_root(node)
click to toggle source
Ensures proper spacing between top-level nodes.
# File lib/sass/tree/visitors/convert.rb, line 40 def visit_root(node) visit_rule_level(node.children) end
visit_rule(node) { || ... }
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 233 def visit_rule(node) rule = node.parsed_rules ? [node.parsed_rules.to_s] : node.rule if @format == :sass name = selector_to_sass(rule) name = "\\" + name if name[0] == ?: name.gsub(/^/, tab_str) + yield elsif @format == :scss name = selector_to_scss(rule) res = name + yield if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent res.slice!(-3..-1) res << "\n" << tab_str << "}\n" end res end end
visit_supports(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 157 def visit_supports(node) "#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}" end
visit_variable(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 250 def visit_variable(node) "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}" + "#{' !global' if node.global}#{' !default' if node.guarded}#{semi}\n" end
visit_warn(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 255 def visit_warn(node) "#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n" end
visit_while(node)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 259 def visit_while(node) "#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}" end
Private Instance Methods
dasherize(s)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 343 def dasherize(s) if @options[:dasherize] s.tr('_', '-') else s end end
interp_to_src(interp)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 301 def interp_to_src(interp) interp.map {|r| r.is_a?(String) ? r : r.to_sass(@options)}.join end
query_interp_to_src(interp)
click to toggle source
Like interp_to_src
, but removes the unnecessary ‘#{}` around the keys and values in query expressions.
# File lib/sass/tree/visitors/convert.rb, line 307 def query_interp_to_src(interp) interp = interp.map do |e| next e unless e.is_a?(Sass::Script::Tree::Literal) next e unless e.value.is_a?(Sass::Script::Value::String) e.value.value end interp_to_src(interp) end
selector_to_sass(sel)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 321 def selector_to_sass(sel) sel.map do |r| if r.is_a?(String) r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "} else r.to_sass(@options) end end.join end
selector_to_scss(sel)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 331 def selector_to_scss(sel) interp_to_src(sel).gsub(/^[ \t]*/, tab_str).gsub(/[ \t]*$/, '') end
selector_to_src(sel)
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 317 def selector_to_src(sel) @format == :sass ? selector_to_sass(sel) : selector_to_scss(sel) end
semi()
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 335 def semi @format == :sass ? "" : ";" end
tab_str()
click to toggle source
# File lib/sass/tree/visitors/convert.rb, line 339 def tab_str @tab_chars * @tabs end
visit_rule_level(nodes)
click to toggle source
Visit rule-level nodes and return their conversion with appropriate whitespace added.
# File lib/sass/tree/visitors/convert.rb, line 282 def visit_rule_level(nodes) (nodes + [nil]).each_cons(2).map do |child, nxt| visit(child) + if nxt && (child.is_a?(Sass::Tree::CommentNode) && child.line + child.lines + 1 == nxt.line) || (child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) && child.line + 1 == nxt.line) || (child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) && child.line + 1 == nxt.line) || (child.is_a?(Sass::Tree::PropNode) && nxt.is_a?(Sass::Tree::PropNode)) || (child.is_a?(Sass::Tree::MixinNode) && nxt.is_a?(Sass::Tree::MixinNode) && child.line + 1 == nxt.line) "" else "\n" end end.join.rstrip + "\n" end