haml 2.2.24 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (168) hide show
  1. data/.yardopts +0 -1
  2. data/README.md +91 -151
  3. data/REMEMBER +11 -1
  4. data/Rakefile +73 -55
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/css2sass +7 -1
  8. data/bin/sass-convert +7 -0
  9. data/extra/haml-mode.el +2 -1
  10. data/lib/haml/buffer.rb +22 -4
  11. data/lib/haml/engine.rb +5 -1
  12. data/lib/haml/exec.rb +231 -46
  13. data/lib/haml/filters.rb +19 -8
  14. data/lib/haml/helpers.rb +47 -20
  15. data/lib/haml/helpers/action_view_extensions.rb +2 -4
  16. data/lib/haml/helpers/action_view_mods.rb +11 -8
  17. data/lib/haml/helpers/xss_mods.rb +13 -2
  18. data/lib/haml/html.rb +179 -48
  19. data/lib/haml/html/erb.rb +141 -0
  20. data/lib/haml/precompiler.rb +40 -15
  21. data/lib/haml/railtie.rb +1 -5
  22. data/lib/haml/root.rb +3 -0
  23. data/lib/haml/template.rb +4 -14
  24. data/lib/haml/util.rb +120 -30
  25. data/lib/haml/version.rb +25 -2
  26. data/lib/sass.rb +5 -1
  27. data/lib/sass/callbacks.rb +50 -0
  28. data/lib/sass/css.rb +40 -191
  29. data/lib/sass/engine.rb +170 -74
  30. data/lib/sass/environment.rb +8 -2
  31. data/lib/sass/error.rb +163 -25
  32. data/lib/sass/files.rb +31 -28
  33. data/lib/sass/plugin.rb +268 -87
  34. data/lib/sass/plugin/rails.rb +9 -4
  35. data/lib/sass/repl.rb +1 -1
  36. data/lib/sass/script.rb +31 -29
  37. data/lib/sass/script/bool.rb +1 -0
  38. data/lib/sass/script/color.rb +290 -23
  39. data/lib/sass/script/css_lexer.rb +22 -0
  40. data/lib/sass/script/css_parser.rb +28 -0
  41. data/lib/sass/script/funcall.rb +22 -3
  42. data/lib/sass/script/functions.rb +523 -33
  43. data/lib/sass/script/interpolation.rb +42 -0
  44. data/lib/sass/script/lexer.rb +169 -52
  45. data/lib/sass/script/literal.rb +58 -9
  46. data/lib/sass/script/node.rb +79 -1
  47. data/lib/sass/script/number.rb +20 -5
  48. data/lib/sass/script/operation.rb +49 -3
  49. data/lib/sass/script/parser.rb +162 -28
  50. data/lib/sass/script/string.rb +50 -2
  51. data/lib/sass/script/unary_operation.rb +25 -2
  52. data/lib/sass/script/variable.rb +21 -4
  53. data/lib/sass/scss.rb +14 -0
  54. data/lib/sass/scss/css_parser.rb +39 -0
  55. data/lib/sass/scss/parser.rb +683 -0
  56. data/lib/sass/scss/rx.rb +112 -0
  57. data/lib/sass/scss/script_lexer.rb +13 -0
  58. data/lib/sass/scss/script_parser.rb +25 -0
  59. data/lib/sass/tree/comment_node.rb +69 -27
  60. data/lib/sass/tree/debug_node.rb +7 -2
  61. data/lib/sass/tree/directive_node.rb +41 -35
  62. data/lib/sass/tree/for_node.rb +6 -0
  63. data/lib/sass/tree/if_node.rb +13 -1
  64. data/lib/sass/tree/import_node.rb +52 -27
  65. data/lib/sass/tree/mixin_def_node.rb +18 -0
  66. data/lib/sass/tree/mixin_node.rb +41 -6
  67. data/lib/sass/tree/node.rb +197 -70
  68. data/lib/sass/tree/prop_node.rb +152 -57
  69. data/lib/sass/tree/root_node.rb +118 -0
  70. data/lib/sass/tree/rule_node.rb +193 -96
  71. data/lib/sass/tree/variable_node.rb +9 -5
  72. data/lib/sass/tree/while_node.rb +4 -0
  73. data/test/benchmark.rb +5 -5
  74. data/test/haml/engine_test.rb +147 -10
  75. data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
  76. data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
  77. data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
  78. data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
  79. data/test/haml/helper_test.rb +91 -24
  80. data/test/haml/html2haml/erb_tests.rb +410 -0
  81. data/test/haml/html2haml_test.rb +210 -66
  82. data/test/haml/results/filters.xhtml +1 -1
  83. data/test/haml/results/just_stuff.xhtml +2 -0
  84. data/test/haml/spec_test.rb +44 -0
  85. data/test/haml/template_test.rb +22 -2
  86. data/test/haml/templates/helpers.haml +0 -13
  87. data/test/haml/templates/just_stuff.haml +2 -0
  88. data/test/haml/util_test.rb +48 -0
  89. data/test/sass/callbacks_test.rb +61 -0
  90. data/test/sass/conversion_test.rb +884 -0
  91. data/test/sass/css2sass_test.rb +99 -18
  92. data/test/sass/data/hsl-rgb.txt +319 -0
  93. data/test/sass/engine_test.rb +1049 -131
  94. data/test/sass/functions_test.rb +398 -47
  95. data/test/sass/more_results/more_import.css +1 -1
  96. data/test/sass/more_templates/more_import.sass +3 -3
  97. data/test/sass/plugin_test.rb +184 -10
  98. data/test/sass/results/compact.css +1 -1
  99. data/test/sass/results/complex.css +5 -5
  100. data/test/sass/results/compressed.css +1 -1
  101. data/test/sass/results/expanded.css +1 -1
  102. data/test/sass/results/import.css +3 -1
  103. data/test/sass/results/mixins.css +12 -12
  104. data/test/sass/results/nested.css +1 -1
  105. data/test/sass/results/options.css +1 -0
  106. data/test/sass/results/parent_ref.css +4 -4
  107. data/test/sass/results/script.css +3 -3
  108. data/test/sass/results/scss_import.css +15 -0
  109. data/test/sass/results/scss_importee.css +2 -0
  110. data/test/sass/script_conversion_test.rb +153 -0
  111. data/test/sass/script_test.rb +137 -70
  112. data/test/sass/scss/css_test.rb +811 -0
  113. data/test/sass/scss/rx_test.rb +156 -0
  114. data/test/sass/scss/scss_test.rb +871 -0
  115. data/test/sass/scss/test_helper.rb +37 -0
  116. data/test/sass/templates/alt.sass +2 -2
  117. data/test/sass/templates/bork1.sass +2 -0
  118. data/test/sass/templates/bork3.sass +2 -0
  119. data/test/sass/templates/bork4.sass +2 -0
  120. data/test/sass/templates/import.sass +4 -4
  121. data/test/sass/templates/importee.sass +3 -3
  122. data/test/sass/templates/line_numbers.sass +1 -1
  123. data/test/sass/templates/mixin_bork.sass +5 -0
  124. data/test/sass/templates/mixins.sass +2 -2
  125. data/test/sass/templates/nested_bork1.sass +2 -0
  126. data/test/sass/templates/nested_bork2.sass +2 -0
  127. data/test/sass/templates/nested_bork3.sass +2 -0
  128. data/test/sass/templates/nested_bork4.sass +2 -0
  129. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  130. data/test/sass/templates/options.sass +2 -0
  131. data/test/sass/templates/parent_ref.sass +2 -2
  132. data/test/sass/templates/script.sass +69 -69
  133. data/test/sass/templates/scss_import.scss +10 -0
  134. data/test/sass/templates/scss_importee.scss +1 -0
  135. data/test/sass/templates/units.sass +10 -10
  136. data/test/test_helper.rb +20 -8
  137. data/vendor/fssm/LICENSE +20 -0
  138. data/vendor/fssm/README.markdown +55 -0
  139. data/vendor/fssm/Rakefile +59 -0
  140. data/vendor/fssm/VERSION.yml +5 -0
  141. data/vendor/fssm/example.rb +9 -0
  142. data/vendor/fssm/fssm.gemspec +77 -0
  143. data/vendor/fssm/lib/fssm.rb +33 -0
  144. data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  145. data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  146. data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  147. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  148. data/vendor/fssm/lib/fssm/monitor.rb +26 -0
  149. data/vendor/fssm/lib/fssm/path.rb +91 -0
  150. data/vendor/fssm/lib/fssm/pathname.rb +502 -0
  151. data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  152. data/vendor/fssm/lib/fssm/state/file.rb +24 -0
  153. data/vendor/fssm/lib/fssm/support.rb +63 -0
  154. data/vendor/fssm/lib/fssm/tree.rb +176 -0
  155. data/vendor/fssm/profile/prof-cache.rb +40 -0
  156. data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  157. data/vendor/fssm/profile/prof-pathname.rb +68 -0
  158. data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  159. data/vendor/fssm/profile/prof.html +2379 -0
  160. data/vendor/fssm/spec/path_spec.rb +75 -0
  161. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  162. data/vendor/fssm/spec/root/file.css +0 -0
  163. data/vendor/fssm/spec/root/file.rb +0 -0
  164. data/vendor/fssm/spec/root/file.yml +0 -0
  165. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  166. data/vendor/fssm/spec/spec_helper.rb +14 -0
  167. metadata +94 -14
  168. data/test/sass/templates/bork.sass +0 -2
@@ -16,6 +16,24 @@ module Sass
16
16
 
17
17
  protected
18
18
 
19
+ def to_src(tabs, opts, fmt)
20
+ args =
21
+ if @args.empty?
22
+ ""
23
+ else
24
+ '(' + @args.map do |v, d|
25
+ if d
26
+ "#{v.to_sass}: #{d.to_sass}"
27
+ else
28
+ v.to_sass
29
+ end
30
+ end.join(", ") + ')'
31
+ end
32
+
33
+ "#{' ' * tabs}#{fmt == :sass ? '=' : '@mixin '}#{@name}#{args}" +
34
+ children_to_src(tabs, opts, fmt)
35
+ end
36
+
19
37
  # Loads the mixin into the environment.
20
38
  #
21
39
  # @param environment [Sass::Environment] The lexical environment containing
@@ -1,10 +1,18 @@
1
1
  require 'sass/tree/node'
2
2
 
3
3
  module Sass::Tree
4
- # A dynamic node representing a mixin include.
4
+ # A static node representing a mixin include.
5
+ # When in a static tree, the sole purpose is to wrap exceptions
6
+ # to add the mixin to the backtrace.
5
7
  #
6
8
  # @see Sass::Tree
7
9
  class MixinNode < Node
10
+ # @see Node#options=
11
+ def options=(opts)
12
+ super
13
+ @args.each {|a| a.context = :equals} if opts[:sass2]
14
+ end
15
+
8
16
  # @param name [String] The name of the mixin
9
17
  # @param args [Array<Script::Node>] The arguments to the mixin
10
18
  def initialize(name, args)
@@ -13,18 +21,36 @@ module Sass::Tree
13
21
  super()
14
22
  end
15
23
 
24
+ # @see Node#cssize
25
+ def cssize(parent = nil)
26
+ _cssize(parent) # Pass on the parent even if it's not a MixinNode
27
+ end
28
+
16
29
  protected
17
30
 
31
+ def to_src(tabs, opts, fmt)
32
+ args = '(' + @args.map {|a| a.to_sass}.join(", ") + ')' unless @args.empty?
33
+ "#{' ' * tabs}#{fmt == :sass ? '+' : '@include '}#{@name}#{args}#{semi fmt}\n"
34
+ end
35
+
36
+ # @see Node#_cssize
37
+ def _cssize(parent)
38
+ children.map {|c| c.cssize(parent)}.flatten
39
+ rescue Sass::SyntaxError => e
40
+ e.modify_backtrace(:mixin => @name, :line => line)
41
+ e.add_backtrace(:filename => filename, :line => line)
42
+ raise e
43
+ end
44
+
18
45
  # Runs the mixin.
19
46
  #
20
47
  # @param environment [Sass::Environment] The lexical environment containing
21
48
  # variable and mixin values
22
- # @return [Array<Tree::Node>] The resulting static nodes
23
49
  # @raise [Sass::SyntaxError] if there is no mixin with the given name
24
50
  # @raise [Sass::SyntaxError] if an incorrect number of arguments was passed
25
51
  # @see Sass::Tree
26
- def _perform(environment)
27
- raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.", @line) unless mixin = environment.mixin(@name)
52
+ def perform!(environment)
53
+ raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.") unless mixin = environment.mixin(@name)
28
54
 
29
55
  raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < @args.size
30
56
  Mixin #{@name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
@@ -37,12 +63,21 @@ END
37
63
  if value
38
64
  value.perform(environment)
39
65
  elsif default
40
- default.perform(env)
66
+ val = default.perform(env)
67
+ if default.context == :equals && val.is_a?(Sass::Script::String)
68
+ val = Sass::Script::String.new(val.value)
69
+ end
70
+ val
41
71
  end)
42
72
  raise Sass::SyntaxError.new("Mixin #{@name} is missing parameter #{var.inspect}.") unless env.var(var.name)
43
73
  env
44
74
  end
45
- mixin.tree.map {|c| c.perform(environment)}.flatten
75
+
76
+ self.children = mixin.tree.map {|c| c.perform(environment)}.flatten
77
+ rescue Sass::SyntaxError => e
78
+ e.modify_backtrace(:mixin => @name, :line => @line)
79
+ e.add_backtrace(:line => @line)
80
+ raise e
46
81
  end
47
82
  end
48
83
  end
@@ -1,28 +1,45 @@
1
1
  module Sass
2
2
  # A namespace for nodes in the Sass parse tree.
3
3
  #
4
- # The Sass parse tree has two states.
5
- # When it's first parsed, it has nodes for mixin definitions
6
- # and for loops and so forth,
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,
7
8
  # in addition to nodes for CSS rules and properties.
9
+ # Nodes that only appear in this state are called **dynamic nodes**.
8
10
  #
9
- # However, {Tree::Node#perform} returns a different sort of tree.
10
- # This tree maps more closely to the resulting CSS document
11
- # than it does to the original Sass document.
12
- # It still has nodes for CSS rules and properties,
11
+ # {Tree::Node#perform} returns a static Sass tree, which is different.
12
+ # It still has nodes for CSS rules and properties
13
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.
14
23
  #
15
- # Nodes that only appear in the pre-perform state are called **dynamic nodes**;
16
- # those that appear in both states are called **static nodes**.
24
+ # Finally, {Tree::Node#to_s} can be called on a static CSS tree
25
+ # to get the actual CSS code as a string.
17
26
  module Tree
18
- # This class doubles as the root node of the parse tree
19
- # and the superclass of all other parse-tree nodes.
27
+ # The abstract superclass of all parse-tree nodes.
20
28
  class Node
29
+ include Enumerable
30
+
21
31
  # The child nodes of this node.
22
32
  #
23
33
  # @return [Array<Tree::Node>]
24
34
  attr_accessor :children
25
35
 
36
+ # Whether or not this node has child nodes.
37
+ # This may be true even when \{#children} is empty,
38
+ # in which case this node has an empty block (e.g. `{}`).
39
+ #
40
+ # @return [Boolean]
41
+ attr_accessor :has_children
42
+
26
43
  # The line of the document on which this node appeared.
27
44
  #
28
45
  # @return [Fixnum]
@@ -52,6 +69,12 @@ module Sass
52
69
  @options = options
53
70
  end
54
71
 
72
+ # @private
73
+ def children=(children)
74
+ self.has_children ||= !children.empty?
75
+ @children = children
76
+ end
77
+
55
78
  # The name of the document on which this node appeared.
56
79
  #
57
80
  # @return [String]
@@ -65,21 +88,14 @@ module Sass
65
88
  # @raise [Sass::SyntaxError] if `child` is invalid
66
89
  # @see #invalid_child?
67
90
  def <<(child)
91
+ return if child.nil?
68
92
  if msg = invalid_child?(child)
69
- raise Sass::SyntaxError.new(msg, child.line)
93
+ raise Sass::SyntaxError.new(msg, :line => child.line)
70
94
  end
95
+ self.has_children = true
71
96
  @children << child
72
97
  end
73
98
 
74
- # Return the last child node.
75
- #
76
- # We need this because {Tree::Node} duck types as an Array for {Sass::Engine}.
77
- #
78
- # @return [Tree::Node] The last child node
79
- def last
80
- children.last
81
- end
82
-
83
99
  # Compares this node and another object (only other {Tree::Node}s will be equal).
84
100
  # This does a structural comparison;
85
101
  # if the contents of the nodes and all the child nodes are equivalent,
@@ -100,7 +116,7 @@ module Sass
100
116
  # @see #perform
101
117
  # @see #to_s
102
118
  def render
103
- perform(Environment.new).to_s
119
+ perform(Environment.new).cssize.to_s
104
120
  end
105
121
 
106
122
  # True if \{#to\_s} will return `nil`;
@@ -110,43 +126,55 @@ module Sass
110
126
  # @return [Boolean]
111
127
  def invisible?; false; end
112
128
 
113
- # Computes the CSS corresponding to this Sass tree.
129
+ # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
130
+ #
131
+ # @return [Symbol]
132
+ def style
133
+ @options[:style]
134
+ end
135
+
136
+ # Computes the CSS corresponding to this static CSS tree.
114
137
  #
138
+ # \{#to_s} shouldn't be overridden directly; instead, override \{#\_to\_s}.
115
139
  # Only static-node subclasses need to implement \{#to\_s}.
116
140
  #
117
141
  # This may return `nil`, but it will only do so if \{#invisible?} is true.
118
142
  #
143
+ # @param args [Array] Passed on to \{#\_to\_s}
119
144
  # @return [String, nil] The resulting CSS
145
+ # @see Sass::Tree
146
+ def to_s(*args)
147
+ _to_s(*args)
148
+ rescue Sass::SyntaxError => e
149
+ e.modify_backtrace(:filename => filename, :line => line)
150
+ raise e
151
+ end
152
+
153
+ # Converts a static Sass tree (e.g. the output of \{#perform})
154
+ # into a static CSS tree.
155
+ #
156
+ # \{#cssize} shouldn't be overridden directly;
157
+ # instead, override \{#\_cssize} or \{#cssize!}.
158
+ #
159
+ # @param parent [Node, nil] The parent node of this node.
160
+ # This should only be non-nil if the parent is the same class as this node
161
+ # @return [Tree::Node] The resulting tree of static nodes
120
162
  # @raise [Sass::SyntaxError] if some element of the tree is invalid
121
163
  # @see Sass::Tree
122
- def to_s
123
- result = String.new
124
- children.each do |child|
125
- if child.is_a? PropNode
126
- message = "Properties aren't allowed at the root of a document." +
127
- child.pseudo_class_selector_message
128
- raise Sass::SyntaxError.new(message, child.line)
129
- else
130
- next if child.invisible?
131
- child_str = child.to_s(1)
132
- result << child_str + (style == :compressed ? '' : "\n")
133
- end
134
- end
135
- result.rstrip!
136
- return "" if result.empty?
137
- return result + "\n"
138
- rescue Sass::SyntaxError => e; e.add_metadata(filename, line)
164
+ def cssize(parent = nil)
165
+ _cssize((parent if parent.class == self.class))
166
+ rescue Sass::SyntaxError => e
167
+ e.modify_backtrace(:filename => filename, :line => line)
168
+ raise e
139
169
  end
140
170
 
141
- # Runs the dynamic Sass code:
171
+ # Converts a dynamic tree into a static Sass tree.
172
+ # That is, runs the dynamic Sass code:
142
173
  # mixins, variables, control directives, and so forth.
143
174
  # This doesn't modify this node or any of its children.
144
175
  #
145
176
  # \{#perform} shouldn't be overridden directly;
146
- # if you want to return a new node (or list of nodes),
147
- # override \{#\_perform};
148
- # if you want to destructively modify this node,
149
- # override \{#perform!}.
177
+ # instead, override \{#\_perform} or \{#perform!}.
150
178
  #
151
179
  # @param environment [Sass::Environment] The lexical environment containing
152
180
  # variable and mixin values
@@ -154,20 +182,84 @@ module Sass
154
182
  # @raise [Sass::SyntaxError] if some element of the tree is invalid
155
183
  # @see Sass::Tree
156
184
  def perform(environment)
157
- environment.options = @options if self.class == Tree::Node
158
185
  _perform(environment)
159
- rescue Sass::SyntaxError => e; e.add_metadata(filename, line)
186
+ rescue Sass::SyntaxError => e
187
+ e.modify_backtrace(:filename => filename, :line => line)
188
+ raise e
160
189
  end
161
190
 
162
- # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
191
+ # Iterates through each node in the tree rooted at this node
192
+ # in a pre-order walk.
163
193
  #
164
- # @return [Symbol]
165
- def style
166
- @options[:style]
194
+ # @yield node
195
+ # @yieldparam node [Node] a node in the tree
196
+ def each(&block)
197
+ yield self
198
+ children.each {|c| c.each(&block)}
199
+ end
200
+
201
+ # Converts a node to Sass code that will generate it.
202
+ #
203
+ # @param tabs [Fixnum] The amount of tabulation to use for the Sass code
204
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
205
+ # @return [String] The Sass code corresponding to the node
206
+ def to_sass(tabs = 0, opts = {})
207
+ to_src(tabs, opts, :sass)
208
+ end
209
+
210
+ # Converts a node to SCSS code that will generate it.
211
+ #
212
+ # @param tabs [Fixnum] The amount of tabulation to use for the SCSS code
213
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
214
+ # @return [String] The Sass code corresponding to the node
215
+ def to_scss(tabs = 0, opts = {})
216
+ to_src(tabs, opts, :scss)
167
217
  end
168
218
 
169
219
  protected
170
220
 
221
+ # Computes the CSS corresponding to this particular Sass node.
222
+ #
223
+ # This method should never raise {Sass::SyntaxError}s.
224
+ # Such errors will not be properly annotated with Sass backtrace information.
225
+ # All error conditions should be checked in earlier transformations,
226
+ # such as \{#cssize} and \{#perform}.
227
+ #
228
+ # @param args [Array] ignored
229
+ # @return [String, nil] The resulting CSS
230
+ # @see #to_s
231
+ # @see Sass::Tree
232
+ def _to_s
233
+ raise NotImplementedError.new("All static-node subclasses of Sass::Tree::Node must override #_to_s or #to_s.")
234
+ end
235
+
236
+ # Converts this static Sass node into a static CSS node,
237
+ # returning the new node.
238
+ # This doesn't modify this node or any of its children.
239
+ #
240
+ # @param parent [Node, nil] The parent node of this node.
241
+ # This should only be non-nil if the parent is the same class as this node
242
+ # @return [Tree::Node, Array<Tree::Node>] The resulting static CSS nodes
243
+ # @raise [Sass::SyntaxError] if some element of the tree is invalid
244
+ # @see #cssize
245
+ # @see Sass::Tree
246
+ def _cssize(parent)
247
+ node = dup
248
+ node.cssize!(parent)
249
+ node
250
+ end
251
+
252
+ # Destructively converts this static Sass node into a static CSS node.
253
+ # This *does* modify this node,
254
+ # but will be run non-destructively by \{#\_cssize\}.
255
+ #
256
+ # @param parent [Node, nil] The parent node of this node.
257
+ # This should only be non-nil if the parent is the same class as this node
258
+ # @see #cssize
259
+ def cssize!(parent)
260
+ self.children = children.map {|c| c.cssize(self)}.flatten
261
+ end
262
+
171
263
  # Runs any dynamic Sass code in this particular node.
172
264
  # This doesn't modify this node or any of its children.
173
265
  #
@@ -202,27 +294,21 @@ module Sass
202
294
  children.map {|c| c.perform(environment)}.flatten
203
295
  end
204
296
 
205
- # Replaces SassScript in a chunk of text (via `#{}`)
297
+ # Replaces SassScript in a chunk of text
206
298
  # with the resulting value.
207
299
  #
208
- # @param text [String] The text to interpolate
300
+ # @param text [Array<String, Sass::Script::Node>] The text to interpolate
209
301
  # @param environment [Sass::Environment] The lexical environment containing
210
302
  # variable and mixin values
211
303
  # @return [String] The interpolated text
212
- def interpolate(text, environment)
213
- res = ''
214
- rest = Haml::Shared.handle_interpolation text do |scan|
215
- escapes = scan[2].size
216
- res << scan.matched[0...-2 - escapes]
217
- if escapes % 2 == 1
218
- res << "\\" * (escapes - 1) << '#{'
219
- else
220
- res << "\\" * [0, escapes - 1].max
221
- res << Script::Parser.new(scan, line, scan.pos - scan.matched_size, filename).
222
- parse_interpolated.perform(environment).to_s
223
- end
224
- end
225
- res + rest
304
+ def run_interp(text, environment)
305
+ text.map do |r|
306
+ next r if r.is_a?(String)
307
+ val = r.perform(environment)
308
+ # Interpolated strings should never render with quotes
309
+ next val.value if val.is_a?(Sass::Script::String)
310
+ val.to_s
311
+ end.join.strip
226
312
  end
227
313
 
228
314
  # @see Haml::Shared.balance
@@ -230,13 +316,14 @@ module Sass
230
316
  def balance(*args)
231
317
  res = Haml::Shared.balance(*args)
232
318
  return res if res
233
- raise Sass::SyntaxError.new("Unbalanced brackets.", line)
319
+ raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line)
234
320
  end
235
321
 
236
322
  # Returns an error message if the given child node is invalid,
237
323
  # and false otherwise.
238
324
  #
239
- # By default, all child nodes are valid.
325
+ # By default, all child nodes except those only allowed at root level
326
+ # ({Tree::MixinDefNode}, {Tree::ImportNode}) are valid.
240
327
  # This is expected to be overriden by subclasses
241
328
  # for which some children are invalid.
242
329
  #
@@ -244,7 +331,47 @@ module Sass
244
331
  # @return [Boolean, String] Whether or not the child node is valid,
245
332
  # as well as the error message to display if it is invalid
246
333
  def invalid_child?(child)
247
- false
334
+ case child
335
+ when Tree::MixinDefNode
336
+ "Mixins may only be defined at the root of a document."
337
+ when Tree::ImportNode
338
+ "Import directives may only be used at the root of a document."
339
+ end
340
+ end
341
+
342
+ # Converts a node to Sass or SCSS code that will generate it.
343
+ #
344
+ # This method is called by the default \{#to\_sass} and \{#to\_scss} methods,
345
+ # so that the same code can be used for both with minor variations.
346
+ #
347
+ # @param tabs [Fixnum] The amount of tabulation to use for the SCSS code
348
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
349
+ # @param fmt [Symbol] `:sass` or `:scss`
350
+ # @return [String] The Sass or SCSS code corresponding to the node
351
+ def to_src(tabs, opts, fmt)
352
+ raise NotImplementedError.new("All static-node subclasses of Sass::Tree::Node must override #to_#{fmt}.")
353
+ end
354
+
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 CSS code corresponding to the children
363
+ def children_to_src(tabs, opts, fmt)
364
+ (fmt == :sass ? "\n" : " {\n") +
365
+ children.map {|c| c.send("to_#{fmt}", tabs + 1, opts)}.join.rstrip +
366
+ (fmt == :sass ? "\n" : " }\n")
367
+ end
368
+
369
+ # Returns a semicolon if this is SCSS, or an empty string if this is Sass.
370
+ #
371
+ # @param fmt [Symbol] `:sass` or `:scss`
372
+ # @return [String] A semicolon or the empty string
373
+ def semi(fmt)
374
+ fmt == :sass ? "" : ";"
248
375
  end
249
376
  end
250
377
  end