PageRenderTime 50ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/ruby/1.9.1/gems/sass-3.2.5/lib/sass/tree/visitors/convert.rb

https://github.com/KaylaStuebbe/uwsp-virtual-tour-server
Ruby | 311 lines | 255 code | 41 blank | 15 comment | 57 complexity | 50961ab4eead9914f60c11a334f1d907 MD5 | raw file
Possible License(s): Apache-2.0, MIT, GPL-2.0, BSD-3-Clause
  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. # 2 spaces by default
  18. @tab_chars = @options[:indent] || " "
  19. end
  20. def visit_children(parent)
  21. @tabs += 1
  22. return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
  23. (@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : "\n#{ @tab_chars * (@tabs-1)}}\n")
  24. ensure
  25. @tabs -= 1
  26. end
  27. # Ensures proper spacing between top-level nodes.
  28. def visit_root(node)
  29. Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
  30. visit(child) +
  31. if nxt &&
  32. (child.is_a?(Sass::Tree::CommentNode) &&
  33. child.line + child.lines + 1 == nxt.line) ||
  34. (child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
  35. child.line + 1 == nxt.line) ||
  36. (child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
  37. child.line + 1 == nxt.line)
  38. ""
  39. else
  40. "\n"
  41. end
  42. end.join.rstrip + "\n"
  43. end
  44. def visit_charset(node)
  45. "#{tab_str}@charset \"#{node.name}\"#{semi}\n"
  46. end
  47. def visit_comment(node)
  48. value = interp_to_src(node.value)
  49. content = if @format == :sass
  50. content = value.gsub(/\*\/$/, '').rstrip
  51. if content =~ /\A[ \t]/
  52. # Re-indent SCSS comments like this:
  53. # /* foo
  54. # bar
  55. # baz */
  56. content.gsub!(/^/, ' ')
  57. content.sub!(/\A([ \t]*)\/\*/, '/*\1')
  58. end
  59. content =
  60. unless content.include?("\n")
  61. content
  62. else
  63. content.gsub!(/\n( \*|\/\/)/, "\n ")
  64. spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
  65. sep = node.type == :silent ? "\n//" : "\n *"
  66. if spaces >= 2
  67. content.gsub(/\n /, sep)
  68. else
  69. content.gsub(/\n#{' ' * spaces}/, sep)
  70. end
  71. end
  72. content.gsub!(/\A\/\*/, '//') if node.type == :silent
  73. content.gsub!(/^/, tab_str)
  74. content.rstrip + "\n"
  75. else
  76. spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
  77. content = if node.type == :silent
  78. value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
  79. else
  80. value
  81. end.gsub(/^/, spaces) + "\n"
  82. content
  83. end
  84. content.sub!(%r{^\s*(/\*)}, '/*!') if node.type == :loud #'
  85. content
  86. end
  87. def visit_debug(node)
  88. "#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
  89. end
  90. def visit_directive(node)
  91. res = "#{tab_str}#{interp_to_src(node.value)}"
  92. res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2');
  93. return res + "#{semi}\n" unless node.has_children
  94. res + yield + "\n"
  95. end
  96. def visit_each(node)
  97. "#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
  98. end
  99. def visit_extend(node)
  100. "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}#{" !optional" if node.optional?}\n"
  101. end
  102. def visit_for(node)
  103. "#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
  104. "#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
  105. end
  106. def visit_function(node)
  107. args = node.args.map do |v, d|
  108. d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
  109. end.join(", ")
  110. if node.splat
  111. args << ", " unless node.args.empty?
  112. args << node.splat.to_sass(@options) << "..."
  113. end
  114. "#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
  115. end
  116. def visit_if(node)
  117. name =
  118. if !@is_else; "if"
  119. elsif node.expr; "else if"
  120. else; "else"
  121. end
  122. @is_else = false
  123. str = "#{tab_str}@#{name}"
  124. str << " #{node.expr.to_sass(@options)}" if node.expr
  125. str << yield
  126. @is_else = true
  127. str << visit(node.else) if node.else
  128. str
  129. ensure
  130. @is_else = false
  131. end
  132. def visit_import(node)
  133. quote = @format == :scss ? '"' : ''
  134. "#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
  135. end
  136. def visit_media(node)
  137. "#{tab_str}@media #{media_interp_to_src(node.query)}#{yield}"
  138. end
  139. def visit_supports(node)
  140. "#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}"
  141. end
  142. def visit_cssimport(node)
  143. if node.uri.is_a?(Sass::Script::Node)
  144. str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
  145. else
  146. str = "#{tab_str}@import #{node.uri}"
  147. end
  148. str << " #{interp_to_src(node.query)}" unless node.query.empty?
  149. "#{str}#{semi}\n"
  150. end
  151. def visit_mixindef(node)
  152. args =
  153. if node.args.empty? && node.splat.nil?
  154. ""
  155. else
  156. str = '('
  157. str << node.args.map do |v, d|
  158. if d
  159. "#{v.to_sass(@options)}: #{d.to_sass(@options)}"
  160. else
  161. v.to_sass(@options)
  162. end
  163. end.join(", ")
  164. if node.splat
  165. str << ", " unless node.args.empty?
  166. str << node.splat.to_sass(@options) << '...'
  167. end
  168. str << ')'
  169. end
  170. "#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
  171. end
  172. def visit_mixin(node)
  173. unless node.args.empty? && node.keywords.empty? && node.splat.nil?
  174. args = node.args.map {|a| a.to_sass(@options)}.join(", ")
  175. keywords = Sass::Util.hash_to_a(node.keywords).
  176. map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
  177. if node.splat
  178. splat = (args.empty? && keywords.empty?) ? "" : ", "
  179. splat = "#{splat}#{node.splat.to_sass(@options)}..."
  180. end
  181. arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
  182. end
  183. "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
  184. end
  185. def visit_content(node)
  186. "#{tab_str}@content#{semi}\n"
  187. end
  188. def visit_prop(node)
  189. res = tab_str + node.declaration(@options, @format)
  190. return res + semi + "\n" if node.children.empty?
  191. res + yield.rstrip + semi + "\n"
  192. end
  193. def visit_return(node)
  194. "#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
  195. end
  196. def visit_rule(node)
  197. if @format == :sass
  198. name = selector_to_sass(node.rule)
  199. name = "\\" + name if name[0] == ?:
  200. name.gsub(/^/, tab_str) + yield
  201. elsif @format == :scss
  202. name = selector_to_scss(node.rule)
  203. res = name + yield
  204. if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent
  205. res.slice!(-3..-1)
  206. res << "\n" << tab_str << "}\n"
  207. end
  208. res
  209. end
  210. end
  211. def visit_variable(node)
  212. "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
  213. end
  214. def visit_warn(node)
  215. "#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
  216. end
  217. def visit_while(node)
  218. "#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
  219. end
  220. private
  221. def interp_to_src(interp)
  222. interp.map do |r|
  223. next r if r.is_a?(String)
  224. "\#{#{r.to_sass(@options)}}"
  225. end.join
  226. end
  227. # Like interp_to_src, but removes the unnecessary `#{}` around the keys and
  228. # values in media expressions.
  229. def media_interp_to_src(interp)
  230. Sass::Util.enum_with_index(interp).map do |r, i|
  231. next r if r.is_a?(String)
  232. before, after = interp[i-1], interp[i+1]
  233. if before.is_a?(String) && after.is_a?(String) &&
  234. ((before[-1] == ?( && after[0] == ?:) ||
  235. (before =~ /:\s*/ && after[0] == ?)))
  236. r.to_sass(@options)
  237. else
  238. "\#{#{r.to_sass(@options)}}"
  239. end
  240. end.join
  241. end
  242. def selector_to_src(sel)
  243. @format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
  244. end
  245. def selector_to_sass(sel)
  246. sel.map do |r|
  247. if r.is_a?(String)
  248. r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
  249. else
  250. "\#{#{r.to_sass(@options)}}"
  251. end
  252. end.join
  253. end
  254. def selector_to_scss(sel)
  255. interp_to_src(sel).gsub(/^[ \t]*/, tab_str).gsub(/[ \t]*$/, '')
  256. end
  257. def semi
  258. @format == :sass ? "" : ";"
  259. end
  260. def tab_str
  261. @tab_chars * @tabs
  262. end
  263. def dasherize(s)
  264. if @options[:dasherize]
  265. s.gsub('_', '-')
  266. else
  267. s
  268. end
  269. end
  270. end