PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/bundle/ruby/1.9.1/gems/haml-3.1.4/vendor/sass/lib/sass/tree/visitors/convert.rb

https://bitbucket.org/mulligan/extractext
Ruby | 260 lines | 213 code | 35 blank | 12 comment | 37 complexity | c53f5cdf89115ae6de7e2e5d58e9e615 MD5 | raw file
Possible License(s): Apache-2.0, MIT, GPL-3.0, GPL-2.0, BSD-3-Clause, MPL-2.0-no-copyleft-exception, BSD-2-Clause, JSON
  1. # A visitor for converting a Sass tree into a source string.
  2. class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
  3. # Runs the visitor on a tree.
  4. #
  5. # @param root [Tree::Node] The root node of the Sass tree.
  6. # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
  7. # @param format [Symbol] `:sass` or `:scss`.
  8. # @return [String] The Sass or SCSS source for the tree.
  9. def self.visit(root, options, format)
  10. new(options, format).send(:visit, root)
  11. end
  12. protected
  13. def initialize(options, format)
  14. @options = options
  15. @format = format
  16. @tabs = 0
  17. end
  18. def visit_children(parent)
  19. @tabs += 1
  20. return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
  21. (@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : " }\n")
  22. ensure
  23. @tabs -= 1
  24. end
  25. # Ensures proper spacing between top-level nodes.
  26. def visit_root(node)
  27. Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
  28. visit(child) +
  29. if nxt &&
  30. (child.is_a?(Sass::Tree::CommentNode) &&
  31. child.line + child.lines + 1 == nxt.line) ||
  32. (child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
  33. child.line + 1 == nxt.line) ||
  34. (child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
  35. child.line + 1 == nxt.line)
  36. ""
  37. else
  38. "\n"
  39. end
  40. end.join.rstrip + "\n"
  41. end
  42. def visit_charset(node)
  43. "#{tab_str}@charset \"#{node.name}\"#{semi}\n"
  44. end
  45. def visit_comment(node)
  46. value = node.value.map do |r|
  47. next r if r.is_a?(String)
  48. "\#{#{r.to_sass(@options)}}"
  49. end.join
  50. content = if @format == :sass
  51. content = value.gsub(/\*\/$/, '').rstrip
  52. if content =~ /\A[ \t]/
  53. # Re-indent SCSS comments like this:
  54. # /* foo
  55. # bar
  56. # baz */
  57. content.gsub!(/^/, ' ')
  58. content.sub!(/\A([ \t]*)\/\*/, '/*\1')
  59. end
  60. content =
  61. unless content.include?("\n")
  62. content
  63. else
  64. content.gsub!(/\n( \*|\/\/)/, "\n ")
  65. spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
  66. sep = node.silent ? "\n//" : "\n *"
  67. if spaces >= 2
  68. content.gsub(/\n /, sep)
  69. else
  70. content.gsub(/\n#{' ' * spaces}/, sep)
  71. end
  72. end
  73. content.gsub!(/\A\/\*/, '//') if node.silent
  74. content.gsub!(/^/, tab_str)
  75. content.rstrip + "\n"
  76. else
  77. spaces = (' ' * [@tabs - value[/^ */].size, 0].max)
  78. content = if node.silent
  79. value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
  80. else
  81. value
  82. end.gsub(/^/, spaces) + "\n"
  83. content
  84. end
  85. if node.loud
  86. if node.silent
  87. content.gsub!(%r{^\s*(//!?)}, '//!')
  88. else
  89. content.sub!(%r{^\s*(/\*)}, '/*!')
  90. end
  91. end
  92. content
  93. end
  94. def visit_debug(node)
  95. "#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
  96. end
  97. def visit_directive(node)
  98. res = "#{tab_str}#{node.value}"
  99. return res + "#{semi}\n" unless node.has_children
  100. res + yield + "\n"
  101. end
  102. def visit_each(node)
  103. "#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
  104. end
  105. def visit_extend(node)
  106. "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}\n"
  107. end
  108. def visit_for(node)
  109. "#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
  110. "#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
  111. end
  112. def visit_function(node)
  113. args = node.args.map do |v, d|
  114. d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
  115. end.join(", ")
  116. "#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
  117. end
  118. def visit_if(node)
  119. name =
  120. if !@is_else; "if"
  121. elsif node.expr; "else if"
  122. else; "else"
  123. end
  124. str = "#{tab_str}@#{name}"
  125. str << " #{node.expr.to_sass(@options)}" if node.expr
  126. str << yield
  127. @is_else = true
  128. str << visit(node.else) if node.else
  129. str
  130. ensure
  131. @is_else = false
  132. end
  133. def visit_import(node)
  134. quote = @format == :scss ? '"' : ''
  135. "#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
  136. end
  137. def visit_media(node)
  138. "#{tab_str}@media #{node.query}#{yield}"
  139. end
  140. def visit_mixindef(node)
  141. args =
  142. if node.args.empty?
  143. ""
  144. else
  145. '(' + node.args.map do |v, d|
  146. if d
  147. "#{v.to_sass(@options)}: #{d.to_sass(@options)}"
  148. else
  149. v.to_sass(@options)
  150. end
  151. end.join(", ") + ')'
  152. end
  153. "#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
  154. end
  155. def visit_mixin(node)
  156. unless node.args.empty? && node.keywords.empty?
  157. args = node.args.map {|a| a.to_sass(@options)}.join(", ")
  158. keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
  159. arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
  160. end
  161. "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
  162. end
  163. def visit_prop(node)
  164. res = tab_str + node.declaration(@options, @format)
  165. return res + semi + "\n" if node.children.empty?
  166. res + yield.rstrip + semi + "\n"
  167. end
  168. def visit_return(node)
  169. "#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
  170. end
  171. def visit_rule(node)
  172. if @format == :sass
  173. name = selector_to_sass(node.rule)
  174. name = "\\" + name if name[0] == ?:
  175. name.gsub(/^/, tab_str) + yield
  176. elsif @format == :scss
  177. name = selector_to_scss(node.rule)
  178. res = name + yield
  179. if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.silent
  180. res.slice!(-3..-1)
  181. res << "\n" << tab_str << "}\n"
  182. end
  183. res
  184. end
  185. end
  186. def visit_variable(node)
  187. "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
  188. end
  189. def visit_warn(node)
  190. "#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
  191. end
  192. def visit_while(node)
  193. "#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
  194. end
  195. private
  196. def selector_to_src(sel)
  197. @format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
  198. end
  199. def selector_to_sass(sel)
  200. sel.map do |r|
  201. if r.is_a?(String)
  202. r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
  203. else
  204. "\#{#{r.to_sass(@options)}}"
  205. end
  206. end.join
  207. end
  208. def selector_to_scss(sel)
  209. sel.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(@options)}}"}.
  210. join.gsub(/^[ \t]*/, tab_str)
  211. end
  212. def semi
  213. @format == :sass ? "" : ";"
  214. end
  215. def tab_str
  216. ' ' * @tabs
  217. end
  218. def dasherize(s)
  219. if @options[:dasherize]
  220. s.gsub('_', '-')
  221. else
  222. s
  223. end
  224. end
  225. end