/scalate-jruby/src/main/resources/haml-3.0.25/lib/sass/tree/node.rb

http://github.com/scalate/scalate · Ruby · 464 lines · 164 code · 39 blank · 261 comment · 21 complexity · 78cfa7d33567a52086ffc0e8aabc0d6b MD5 · raw file

  1. module Sass
  2. # A namespace for nodes in the Sass parse tree.
  3. #
  4. # The Sass parse tree has three states: dynamic, static Sass, and static CSS.
  5. #
  6. # When it's first parsed, a Sass document is in the dynamic state.
  7. # It has nodes for mixin definitions and `@for` loops and so forth,
  8. # in addition to nodes for CSS rules and properties.
  9. # Nodes that only appear in this state are called **dynamic nodes**.
  10. #
  11. # {Tree::Node#perform} returns a static Sass tree, which is different.
  12. # It still has nodes for CSS rules and properties
  13. # but it doesn't have any dynamic-generation-related nodes.
  14. # The nodes in this state are in the same structure as the Sass document:
  15. # rules and properties are nested beneath one another.
  16. # Nodes that can be in this state or in the dynamic state
  17. # are called **static nodes**.
  18. #
  19. # {Tree::Node#cssize} then returns a static CSS tree.
  20. # This is like a static Sass tree,
  21. # but the structure exactly mirrors that of the generated CSS.
  22. # Rules and properties can't be nested beneath one another in this state.
  23. #
  24. # Finally, {Tree::Node#to_s} can be called on a static CSS tree
  25. # to get the actual CSS code as a string.
  26. module Tree
  27. # The abstract superclass of all parse-tree nodes.
  28. class Node
  29. include Enumerable
  30. # The child nodes of this node.
  31. #
  32. # @return [Array<Tree::Node>]
  33. attr_accessor :children
  34. # Whether or not this node has child nodes.
  35. # This may be true even when \{#children} is empty,
  36. # in which case this node has an empty block (e.g. `{}`).
  37. #
  38. # @return [Boolean]
  39. attr_accessor :has_children
  40. # The line of the document on which this node appeared.
  41. #
  42. # @return [Fixnum]
  43. attr_accessor :line
  44. # The name of the document on which this node appeared.
  45. #
  46. # @return [String]
  47. attr_writer :filename
  48. # The options hash for the node.
  49. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  50. #
  51. # @return [{Symbol => Object}]
  52. attr_reader :options
  53. def initialize
  54. @children = []
  55. end
  56. # Sets the options hash for the node and all its children.
  57. #
  58. # @param options [{Symbol => Object}] The options
  59. # @see #options
  60. def options=(options)
  61. children.each {|c| c.options = options}
  62. @options = options
  63. end
  64. # @private
  65. def children=(children)
  66. self.has_children ||= !children.empty?
  67. @children = children
  68. end
  69. # The name of the document on which this node appeared.
  70. #
  71. # @return [String]
  72. def filename
  73. @filename || (@options && @options[:filename])
  74. end
  75. # Appends a child to the node.
  76. #
  77. # @param child [Tree::Node, Array<Tree::Node>] The child node or nodes
  78. # @raise [Sass::SyntaxError] if `child` is invalid
  79. # @see #invalid_child?
  80. def <<(child)
  81. return if child.nil?
  82. if child.is_a?(Array)
  83. child.each {|c| self << c}
  84. else
  85. check_child! child
  86. self.has_children = true
  87. @children << child
  88. end
  89. end
  90. # Raises an error if the given child node is invalid.
  91. #
  92. # @param child [Tree::Node] The child node
  93. # @raise [Sass::SyntaxError] if `child` is invalid
  94. # @see #invalid_child?
  95. def check_child!(child)
  96. if msg = invalid_child?(child)
  97. raise Sass::SyntaxError.new(msg, :line => child.line)
  98. end
  99. end
  100. # Compares this node and another object (only other {Tree::Node}s will be equal).
  101. # This does a structural comparison;
  102. # if the contents of the nodes and all the child nodes are equivalent,
  103. # then the nodes are as well.
  104. #
  105. # Only static nodes need to override this.
  106. #
  107. # @param other [Object] The object to compare with
  108. # @return [Boolean] Whether or not this node and the other object
  109. # are the same
  110. # @see Sass::Tree
  111. def ==(other)
  112. self.class == other.class && other.children == children
  113. end
  114. # True if \{#to\_s} will return `nil`;
  115. # that is, if the node shouldn't be rendered.
  116. # Should only be called in a static tree.
  117. #
  118. # @return [Boolean]
  119. def invisible?; false; end
  120. # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  121. #
  122. # @return [Symbol]
  123. def style
  124. @options[:style]
  125. end
  126. # Computes the CSS corresponding to this static CSS tree.
  127. #
  128. # \{#to_s} shouldn't be overridden directly; instead, override \{#\_to\_s}.
  129. # Only static-node subclasses need to implement \{#to\_s}.
  130. #
  131. # This may return `nil`, but it will only do so if \{#invisible?} is true.
  132. #
  133. # @param args [Array] Passed on to \{#\_to\_s}
  134. # @return [String, nil] The resulting CSS
  135. # @see Sass::Tree
  136. def to_s(*args)
  137. _to_s(*args)
  138. rescue Sass::SyntaxError => e
  139. e.modify_backtrace(:filename => filename, :line => line)
  140. raise e
  141. end
  142. # Converts a static CSS tree (e.g. the output of \{#cssize})
  143. # into another static CSS tree,
  144. # with the given extensions applied to all relevant {RuleNode}s.
  145. #
  146. # @todo Link this to the reference documentation on `@extend`
  147. # when such a thing exists.
  148. #
  149. # @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
  150. # The extensions to perform on this tree
  151. # @return [Tree::Node] The resulting tree of static CSS nodes.
  152. # @raise [Sass::SyntaxError] Only if there's a programmer error
  153. # and this is not a static CSS tree
  154. def do_extend(extends)
  155. node = dup
  156. node.children = children.map {|c| c.do_extend(extends)}
  157. node
  158. rescue Sass::SyntaxError => e
  159. e.modify_backtrace(:filename => filename, :line => line)
  160. raise e
  161. end
  162. # Converts a static Sass tree (e.g. the output of \{#perform})
  163. # into a static CSS tree.
  164. #
  165. # \{#cssize} shouldn't be overridden directly;
  166. # instead, override \{#\_cssize} or \{#cssize!}.
  167. #
  168. # @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
  169. # The extensions defined for this tree
  170. # @param parent [Node, nil] The parent node of this node.
  171. # This should only be non-nil if the parent is the same class as this node
  172. # @return [Tree::Node] The resulting tree of static nodes
  173. # @raise [Sass::SyntaxError] if some element of the tree is invalid
  174. # @see Sass::Tree
  175. def cssize(extends, parent = nil)
  176. _cssize(extends, (parent if parent.class == self.class))
  177. rescue Sass::SyntaxError => e
  178. e.modify_backtrace(:filename => filename, :line => line)
  179. raise e
  180. end
  181. # Converts a dynamic tree into a static Sass tree.
  182. # That is, runs the dynamic Sass code:
  183. # mixins, variables, control directives, and so forth.
  184. # This doesn't modify this node or any of its children.
  185. #
  186. # \{#perform} shouldn't be overridden directly;
  187. # instead, override \{#\_perform} or \{#perform!}.
  188. #
  189. # @param environment [Sass::Environment] The lexical environment containing
  190. # variable and mixin values
  191. # @return [Tree::Node] The resulting tree of static nodes
  192. # @raise [Sass::SyntaxError] if some element of the tree is invalid
  193. # @see Sass::Tree
  194. def perform(environment)
  195. _perform(environment)
  196. rescue Sass::SyntaxError => e
  197. e.modify_backtrace(:filename => filename, :line => line)
  198. raise e
  199. end
  200. # Iterates through each node in the tree rooted at this node
  201. # in a pre-order walk.
  202. #
  203. # @yield node
  204. # @yieldparam node [Node] a node in the tree
  205. def each(&block)
  206. yield self
  207. children.each {|c| c.each(&block)}
  208. end
  209. # Converts a node to Sass code that will generate it.
  210. #
  211. # @param tabs [Fixnum] The amount of tabulation to use for the Sass code
  212. # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  213. # @return [String] The Sass code corresponding to the node
  214. def to_sass(tabs = 0, opts = {})
  215. to_src(tabs, opts, :sass)
  216. end
  217. # Converts a node to SCSS code that will generate it.
  218. #
  219. # @param tabs [Fixnum] The amount of tabulation to use for the SCSS code
  220. # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  221. # @return [String] The Sass code corresponding to the node
  222. def to_scss(tabs = 0, opts = {})
  223. to_src(tabs, opts, :scss)
  224. end
  225. protected
  226. # Computes the CSS corresponding to this particular Sass node.
  227. #
  228. # This method should never raise {Sass::SyntaxError}s.
  229. # Such errors will not be properly annotated with Sass backtrace information.
  230. # All error conditions should be checked in earlier transformations,
  231. # such as \{#cssize} and \{#perform}.
  232. #
  233. # @param args [Array] ignored
  234. # @return [String, nil] The resulting CSS
  235. # @see #to_s
  236. # @see Sass::Tree
  237. def _to_s
  238. raise NotImplementedError.new("All static-node subclasses of Sass::Tree::Node must override #_to_s or #to_s.")
  239. end
  240. # Converts this static Sass node into a static CSS node,
  241. # returning the new node.
  242. # This doesn't modify this node or any of its children.
  243. #
  244. # @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
  245. # The extensions defined for this tree
  246. # @param parent [Node, nil] The parent node of this node.
  247. # This should only be non-nil if the parent is the same class as this node
  248. # @return [Tree::Node, Array<Tree::Node>] The resulting static CSS nodes
  249. # @raise [Sass::SyntaxError] if some element of the tree is invalid
  250. # @see #cssize
  251. # @see Sass::Tree
  252. def _cssize(extends, parent)
  253. node = dup
  254. node.cssize!(extends, parent)
  255. node
  256. end
  257. # Destructively converts this static Sass node into a static CSS node.
  258. # This *does* modify this node,
  259. # but will be run non-destructively by \{#\_cssize\}.
  260. #
  261. # @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
  262. # The extensions defined for this tree
  263. # @param parent [Node, nil] The parent node of this node.
  264. # This should only be non-nil if the parent is the same class as this node
  265. # @see #cssize
  266. def cssize!(extends, parent)
  267. self.children = children.map {|c| c.cssize(extends, self)}.flatten
  268. end
  269. # Runs any dynamic Sass code in this particular node.
  270. # This doesn't modify this node or any of its children.
  271. #
  272. # @param environment [Sass::Environment] The lexical environment containing
  273. # variable and mixin values
  274. # @return [Tree::Node, Array<Tree::Node>] The resulting static nodes
  275. # @see #perform
  276. # @see Sass::Tree
  277. def _perform(environment)
  278. node = dup
  279. node.perform!(environment)
  280. node
  281. end
  282. # Destructively runs dynamic Sass code in this particular node.
  283. # This *does* modify this node,
  284. # but will be run non-destructively by \{#\_perform\}.
  285. #
  286. # @param environment [Sass::Environment] The lexical environment containing
  287. # variable and mixin values
  288. # @see #perform
  289. def perform!(environment)
  290. self.children = perform_children(Environment.new(environment))
  291. self.children.each {|c| check_child! c}
  292. end
  293. # Non-destructively runs \{#perform} on all children of the current node.
  294. #
  295. # @param environment [Sass::Environment] The lexical environment containing
  296. # variable and mixin values
  297. # @return [Array<Tree::Node>] The resulting static nodes
  298. def perform_children(environment)
  299. children.map {|c| c.perform(environment)}.flatten
  300. end
  301. # Replaces SassScript in a chunk of text
  302. # with the resulting value.
  303. #
  304. # @param text [Array<String, Sass::Script::Node>] The text to interpolate
  305. # @param environment [Sass::Environment] The lexical environment containing
  306. # variable and mixin values
  307. # @return [String] The interpolated text
  308. def run_interp(text, environment)
  309. text.map do |r|
  310. next r if r.is_a?(String)
  311. val = r.perform(environment)
  312. # Interpolated strings should never render with quotes
  313. next val.value if val.is_a?(Sass::Script::String)
  314. val.to_s
  315. end.join.strip
  316. end
  317. # @see Haml::Shared.balance
  318. # @raise [Sass::SyntaxError] if the brackets aren't balanced
  319. def balance(*args)
  320. res = Haml::Shared.balance(*args)
  321. return res if res
  322. raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line)
  323. end
  324. # Returns an error message if the given child node is invalid,
  325. # and false otherwise.
  326. #
  327. # By default, all child nodes except those only allowed under specific nodes
  328. # ({Tree::MixinDefNode}, {Tree::ImportNode}, {Tree::ExtendNode}) are valid.
  329. # This is expected to be overriden by subclasses
  330. # for which some children are invalid.
  331. #
  332. # @param child [Tree::Node] A potential child node
  333. # @return [Boolean, String] Whether or not the child node is valid,
  334. # as well as the error message to display if it is invalid
  335. def invalid_child?(child)
  336. case child
  337. when Tree::MixinDefNode
  338. "Mixins may only be defined at the root of a document."
  339. when Tree::ImportNode
  340. "Import directives may only be used at the root of a document."
  341. end
  342. end
  343. # Converts a node to Sass or SCSS code that will generate it.
  344. #
  345. # This method is called by the default \{#to\_sass} and \{#to\_scss} methods,
  346. # so that the same code can be used for both with minor variations.
  347. #
  348. # @param tabs [Fixnum] The amount of tabulation to use for the SCSS code
  349. # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  350. # @param fmt [Symbol] `:sass` or `:scss`
  351. # @return [String] The Sass or SCSS code corresponding to the node
  352. def to_src(tabs, opts, fmt)
  353. raise NotImplementedError.new("All static-node subclasses of Sass::Tree::Node must override #to_#{fmt}.")
  354. end
  355. # Converts the children of this node to a Sass or SCSS string.
  356. # This will return the trailing newline for the previous line,
  357. # including brackets if this is SCSS.
  358. #
  359. # @param tabs [Fixnum] The amount of tabulation to use for the Sass code
  360. # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  361. # @param fmt [Symbol] `:sass` or `:scss`
  362. # @return [String] The Sass or SCSS code corresponding to the children
  363. def children_to_src(tabs, opts, fmt)
  364. return fmt == :sass ? "\n" : " {}\n" if children.empty?
  365. (fmt == :sass ? "\n" : " {\n") +
  366. children.map {|c| c.send("to_#{fmt}", tabs + 1, opts)}.join.rstrip +
  367. (fmt == :sass ? "\n" : " }\n")
  368. end
  369. # Converts a selector to a Sass or SCSS string.
  370. #
  371. # @param sel [Array<String, Sass::Script::Node>] The selector to convert
  372. # @param tabs [Fixnum] The indentation of the selector
  373. # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  374. # @param fmt [Symbol] `:sass` or `:scss`
  375. # @return [String] The Sass or SCSS code corresponding to the selector
  376. def selector_to_src(sel, tabs, opts, fmt)
  377. fmt == :sass ? selector_to_sass(sel, opts) : selector_to_scss(sel, tabs, opts)
  378. end
  379. # Converts a selector to a Sass string.
  380. #
  381. # @param sel [Array<String, Sass::Script::Node>] The selector to convert
  382. # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  383. # @return [String] The Sass code corresponding to the selector
  384. def selector_to_sass(sel, opts)
  385. sel.map do |r|
  386. if r.is_a?(String)
  387. r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
  388. else
  389. "\#{#{r.to_sass(opts)}}"
  390. end
  391. end.join
  392. end
  393. # Converts a selector to a SCSS string.
  394. #
  395. # @param sel [Array<String, Sass::Script::Node>] The selector to convert
  396. # @param tabs [Fixnum] The indentation of the selector
  397. # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  398. # @return [String] The SCSS code corresponding to the selector
  399. def selector_to_scss(sel, tabs, opts)
  400. sel.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(opts)}}"}.
  401. join.gsub(/^[ \t]*/, ' ' * tabs)
  402. end
  403. # Convert any underscores in a string into hyphens,
  404. # but only if the `:dasherize` option is set.
  405. #
  406. # @param s [String] The string to convert
  407. # @param opts [{Symbol => Object}] The options hash
  408. # @return [String] The converted string
  409. def dasherize(s, opts)
  410. if opts[:dasherize]
  411. s.gsub('_', '-')
  412. else
  413. s
  414. end
  415. end
  416. # Returns a semicolon if this is SCSS, or an empty string if this is Sass.
  417. #
  418. # @param fmt [Symbol] `:sass` or `:scss`
  419. # @return [String] A semicolon or the empty string
  420. def semi(fmt)
  421. fmt == :sass ? "" : ";"
  422. end
  423. end
  424. end
  425. end