haml 3.1.0 → 3.1.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 (87) hide show
  1. data/VERSION +1 -1
  2. data/vendor/sass/Rakefile +22 -46
  3. data/vendor/sass/VERSION +1 -1
  4. data/vendor/sass/VERSION_NAME +1 -1
  5. data/vendor/sass/bin/scss +8 -0
  6. data/vendor/sass/doc-src/SASS_CHANGELOG.md +125 -9
  7. data/vendor/sass/doc-src/SASS_REFERENCE.md +84 -8
  8. data/vendor/sass/lib/sass.rb +0 -3
  9. data/vendor/sass/lib/sass/cache_stores.rb +1 -0
  10. data/vendor/sass/lib/sass/cache_stores/base.rb +2 -2
  11. data/vendor/sass/lib/sass/cache_stores/chain.rb +33 -0
  12. data/vendor/sass/lib/sass/cache_stores/filesystem.rb +6 -4
  13. data/vendor/sass/lib/sass/cache_stores/memory.rb +8 -12
  14. data/vendor/sass/lib/sass/engine.rb +65 -56
  15. data/vendor/sass/lib/sass/environment.rb +5 -2
  16. data/vendor/sass/lib/sass/exec.rb +52 -21
  17. data/vendor/sass/lib/sass/importers/filesystem.rb +32 -9
  18. data/vendor/sass/lib/sass/less.rb +1 -1
  19. data/vendor/sass/lib/sass/plugin.rb +11 -1
  20. data/vendor/sass/lib/sass/plugin/compiler.rb +21 -12
  21. data/vendor/sass/lib/sass/plugin/rails.rb +8 -82
  22. data/vendor/sass/lib/sass/plugin/staleness_checker.rb +10 -10
  23. data/vendor/sass/lib/sass/railtie.rb +3 -2
  24. data/vendor/sass/lib/sass/script.rb +2 -25
  25. data/vendor/sass/lib/sass/script/color.rb +4 -15
  26. data/vendor/sass/lib/sass/script/funcall.rb +63 -19
  27. data/vendor/sass/lib/sass/script/functions.rb +257 -19
  28. data/vendor/sass/lib/sass/script/lexer.rb +1 -4
  29. data/vendor/sass/lib/sass/script/list.rb +2 -2
  30. data/vendor/sass/lib/sass/script/node.rb +0 -27
  31. data/vendor/sass/lib/sass/script/number.rb +1 -1
  32. data/vendor/sass/lib/sass/script/operation.rb +0 -5
  33. data/vendor/sass/lib/sass/script/parser.rb +30 -12
  34. data/vendor/sass/lib/sass/script/string.rb +2 -17
  35. data/vendor/sass/lib/sass/script/string_interpolation.rb +1 -0
  36. data/vendor/sass/lib/sass/scss/parser.rb +58 -18
  37. data/vendor/sass/lib/sass/scss/rx.rb +2 -1
  38. data/vendor/sass/lib/sass/scss/script_lexer.rb +1 -1
  39. data/vendor/sass/lib/sass/selector/comma_sequence.rb +2 -3
  40. data/vendor/sass/lib/sass/selector/sequence.rb +3 -6
  41. data/vendor/sass/lib/sass/selector/simple_sequence.rb +2 -3
  42. data/vendor/sass/lib/sass/tree/charset_node.rb +0 -15
  43. data/vendor/sass/lib/sass/tree/comment_node.rb +20 -71
  44. data/vendor/sass/lib/sass/tree/debug_node.rb +4 -22
  45. data/vendor/sass/lib/sass/tree/directive_node.rb +0 -52
  46. data/vendor/sass/lib/sass/tree/each_node.rb +8 -38
  47. data/vendor/sass/lib/sass/tree/extend_node.rb +12 -48
  48. data/vendor/sass/lib/sass/tree/for_node.rb +20 -51
  49. data/vendor/sass/lib/sass/tree/function_node.rb +27 -0
  50. data/vendor/sass/lib/sass/tree/if_node.rb +22 -57
  51. data/vendor/sass/lib/sass/tree/import_node.rb +0 -56
  52. data/vendor/sass/lib/sass/tree/media_node.rb +0 -43
  53. data/vendor/sass/lib/sass/tree/mixin_def_node.rb +12 -45
  54. data/vendor/sass/lib/sass/tree/mixin_node.rb +13 -124
  55. data/vendor/sass/lib/sass/tree/node.rb +18 -304
  56. data/vendor/sass/lib/sass/tree/prop_node.rb +24 -92
  57. data/vendor/sass/lib/sass/tree/return_node.rb +18 -0
  58. data/vendor/sass/lib/sass/tree/root_node.rb +4 -133
  59. data/vendor/sass/lib/sass/tree/rule_node.rb +21 -164
  60. data/vendor/sass/lib/sass/tree/variable_node.rb +14 -23
  61. data/vendor/sass/lib/sass/tree/visitors/base.rb +75 -0
  62. data/vendor/sass/lib/sass/tree/visitors/check_nesting.rb +134 -0
  63. data/vendor/sass/lib/sass/tree/visitors/convert.rb +255 -0
  64. data/vendor/sass/lib/sass/tree/visitors/cssize.rb +175 -0
  65. data/vendor/sass/lib/sass/tree/visitors/perform.rb +301 -0
  66. data/vendor/sass/lib/sass/tree/visitors/to_css.rb +216 -0
  67. data/vendor/sass/lib/sass/tree/warn_node.rb +4 -28
  68. data/vendor/sass/lib/sass/tree/while_node.rb +5 -35
  69. data/vendor/sass/lib/sass/util.rb +0 -50
  70. data/vendor/sass/sass.gemspec +1 -1
  71. data/vendor/sass/test/sass/conversion_test.rb +53 -102
  72. data/vendor/sass/test/sass/engine_test.rb +416 -540
  73. data/vendor/sass/test/sass/functions_test.rb +306 -4
  74. data/vendor/sass/test/sass/importer_test.rb +0 -22
  75. data/vendor/sass/test/sass/plugin_test.rb +51 -21
  76. data/vendor/sass/test/sass/results/if.css +3 -0
  77. data/vendor/sass/test/sass/script_conversion_test.rb +0 -38
  78. data/vendor/sass/test/sass/script_test.rb +19 -4
  79. data/vendor/sass/test/sass/scss/scss_test.rb +32 -11
  80. data/vendor/sass/test/sass/templates/if.sass +11 -0
  81. data/vendor/sass/test/sass/templates/nested_import.sass +2 -0
  82. data/vendor/sass/test/sass/util_test.rb +0 -21
  83. data/vendor/sass/test/test_helper.rb +0 -3
  84. metadata +268 -258
  85. data/vendor/sass/bin/css2sass +0 -13
  86. data/vendor/sass/lib/sass/cache_stores/active_support.rb +0 -28
  87. data/vendor/sass/lib/sass/importers/rails.rb +0 -75
@@ -0,0 +1,175 @@
1
+ # A visitor for converting a static Sass tree into a static CSS tree.
2
+ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
3
+ # @param root [Tree::Node] The root node of the tree to visit.
4
+ # @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
5
+ # *and* the extensions defined for this tree
6
+ def self.visit(root); super; end
7
+
8
+ protected
9
+
10
+ # Returns the immediate parent of the current node.
11
+ # @return [Tree::Node]
12
+ attr_reader :parent
13
+
14
+ def initialize
15
+ @extends = Sass::Util::SubsetMap.new
16
+ end
17
+
18
+ # If an exception is raised, this adds proper metadata to the backtrace.
19
+ def visit(node)
20
+ super(node.dup)
21
+ rescue Sass::SyntaxError => e
22
+ e.modify_backtrace(:filename => node.filename, :line => node.line)
23
+ raise e
24
+ end
25
+
26
+ # Keeps track of the current parent node.
27
+ def visit_children(parent)
28
+ with_parent parent do
29
+ parent.children = super.flatten
30
+ parent
31
+ end
32
+ end
33
+
34
+ # Runs a block of code with the current parent node
35
+ # replaced with the given node.
36
+ #
37
+ # @param parent [Tree::Node] The new parent for the duration of the block.
38
+ # @yield A block in which the parent is set to `parent`.
39
+ # @return [Object] The return value of the block.
40
+ def with_parent(parent)
41
+ old_parent, @parent = @parent, parent
42
+ yield
43
+ ensure
44
+ @parent = old_parent
45
+ end
46
+
47
+ # In Ruby 1.8, ensures that there's only one `@charset` directive
48
+ # and that it's at the top of the document.
49
+ #
50
+ # @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
51
+ # *and* the extensions defined for this tree
52
+ def visit_root(node)
53
+ yield
54
+
55
+ # In Ruby 1.9 we can make all @charset nodes invisible
56
+ # and infer the final @charset from the encoding of the final string.
57
+ if Sass::Util.ruby1_8? && parent.nil?
58
+ charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
59
+ node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
60
+ node.children.unshift charset if charset
61
+ end
62
+
63
+ return node, @extends
64
+ rescue Sass::SyntaxError => e
65
+ e.sass_template ||= node.template
66
+ raise e
67
+ end
68
+
69
+ # Registers an extension in the `@extends` subset map.
70
+ def visit_extend(node)
71
+ node.resolved_selector.members.each do |seq|
72
+ if seq.members.size > 1
73
+ raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
74
+ end
75
+
76
+ sseq = seq.members.first
77
+ if !sseq.is_a?(Sass::Selector::SimpleSequence)
78
+ raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
79
+ end
80
+
81
+ sel = sseq.members
82
+ parent.resolved_rules.members.each do |seq|
83
+ if !seq.members.last.is_a?(Sass::Selector::SimpleSequence)
84
+ raise Sass::SyntaxError.new("#{seq} can't extend: invalid selector")
85
+ end
86
+
87
+ @extends[sel] = seq
88
+ end
89
+ end
90
+
91
+ []
92
+ end
93
+
94
+ # Modifies exception backtraces to include the imported file.
95
+ def visit_import(node)
96
+ # Don't use #visit_children to avoid adding the import node to the list of parents.
97
+ node.children.map {|c| visit(c)}.flatten
98
+ rescue Sass::SyntaxError => e
99
+ e.modify_backtrace(:filename => node.children.first.filename)
100
+ e.add_backtrace(:filename => node.filename, :line => node.line)
101
+ raise e
102
+ end
103
+
104
+ # Bubbles the `@media` directive up through RuleNodes
105
+ # and merges it with other `@media` directives.
106
+ def visit_media(node)
107
+ if parent.is_a?(Sass::Tree::RuleNode)
108
+ new_rule = parent.dup
109
+ new_rule.children = node.children
110
+ node.children = with_parent(node) {Array(visit(new_rule))}
111
+ # If the last child is actually the end of the group,
112
+ # the parent's cssize will set it properly
113
+ node.children.last.group_end = false unless node.children.empty?
114
+ else
115
+ yield
116
+ end
117
+
118
+ media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
119
+ node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
120
+ media.each {|n| n.query = "#{node.query} and #{n.query}"}
121
+ (node.children.empty? ? [] : [node]) + media
122
+ end
123
+
124
+ # Asserts that all the mixin's children are valid in their new location.
125
+ def visit_mixin(node)
126
+ # Don't use #visit_children to avoid adding the mixin node to the list of parents.
127
+ node.children.map {|c| visit(c)}.flatten
128
+ rescue Sass::SyntaxError => e
129
+ e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
130
+ e.add_backtrace(:filename => node.filename, :line => node.line)
131
+ raise e
132
+ end
133
+
134
+ # Converts nested properties into flat properties
135
+ # and updates the indentation of the prop node based on the nesting level.
136
+ def visit_prop(node)
137
+ if parent.is_a?(Sass::Tree::PropNode)
138
+ node.resolved_name = "#{parent.resolved_name}-#{node.resolved_name}"
139
+ node.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if node.style == :nested
140
+ end
141
+
142
+ yield
143
+
144
+ result = node.children.dup
145
+ if !node.resolved_value.empty? || node.children.empty?
146
+ node.send(:check!)
147
+ result.unshift(node)
148
+ end
149
+
150
+ result
151
+ end
152
+
153
+ # Resolves parent references and nested selectors,
154
+ # and updates the indentation of the rule node based on the nesting level.
155
+ def visit_rule(node)
156
+ parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
157
+ # It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
158
+ node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)
159
+
160
+ yield
161
+
162
+ rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode)}
163
+ props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode) || c.invisible?}
164
+
165
+ unless props.empty?
166
+ node.children = props
167
+ rules.each {|r| r.tabs += 1} if node.style == :nested
168
+ rules.unshift(node)
169
+ end
170
+
171
+ rules.last.group_end = true unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty?
172
+
173
+ rules
174
+ end
175
+ end
@@ -0,0 +1,301 @@
1
+ # A visitor for converting a dynamic Sass tree into a static Sass tree.
2
+ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
3
+ # @param root [Tree::Node] The root node of the tree to visit.
4
+ # @param environment [Sass::Environment] The lexical environment.
5
+ # @return [Tree::Node] The resulting tree of static nodes.
6
+ def self.visit(root, environment = Sass::Environment.new)
7
+ new(environment).send(:visit, root)
8
+ end
9
+
10
+ protected
11
+
12
+ def initialize(env)
13
+ @environment = env
14
+ end
15
+
16
+ # If an exception is raised, this add proper metadata to the backtrace.
17
+ def visit(node)
18
+ super(node.dup)
19
+ rescue Sass::SyntaxError => e
20
+ e.modify_backtrace(:filename => node.filename, :line => node.line)
21
+ raise e
22
+ end
23
+
24
+ # Keeps track of the current environment.
25
+ def visit_children(parent)
26
+ with_environment Sass::Environment.new(@environment) do
27
+ parent.children = super.flatten
28
+ parent
29
+ end
30
+ end
31
+
32
+ # Runs a block of code with the current environment replaced with the given one.
33
+ #
34
+ # @param env [Sass::Environment] The new environment for the duration of the block.
35
+ # @yield A block in which the environment is set to `env`.
36
+ # @return [Object] The return value of the block.
37
+ def with_environment(env)
38
+ old_env, @environment = @environment, env
39
+ yield
40
+ ensure
41
+ @environment = old_env
42
+ end
43
+
44
+ # Sets the options on the environment if this is the top-level root.
45
+ def visit_root(node)
46
+ @environment.options = node.options if @environment.options.nil? || @environment.options.empty?
47
+ yield
48
+ rescue Sass::SyntaxError => e
49
+ e.sass_template ||= node.template
50
+ raise e
51
+ end
52
+
53
+ # Removes this node from the tree if it's a silent comment.
54
+ def visit_comment(node)
55
+ return [] if node.invisible?
56
+ if node.evaluated?
57
+ node.value.gsub!(/(^|[^\\])\#\{([^}]*)\}/) do |md|
58
+ $1+Sass::Script.parse($2, node.line, 0, node.options).perform(@environment).to_s
59
+ end
60
+ node.value = run_interp([Sass::Script::String.new(node.value)])
61
+ end
62
+ node
63
+ end
64
+
65
+ # Prints the expression to STDERR.
66
+ def visit_debug(node)
67
+ res = node.expr.perform(@environment)
68
+ res = res.value if res.is_a?(Sass::Script::String)
69
+ if node.filename
70
+ $stderr.puts "#{node.filename}:#{node.line} DEBUG: #{res}"
71
+ else
72
+ $stderr.puts "Line #{node.line} DEBUG: #{res}"
73
+ end
74
+ []
75
+ end
76
+
77
+ # Runs the child nodes once for each value in the list.
78
+ def visit_each(node)
79
+ list = node.list.perform(@environment)
80
+
81
+ with_environment Sass::Environment.new(@environment) do
82
+ list.to_a.map do |v|
83
+ @environment.set_local_var(node.var, v)
84
+ node.children.map {|c| visit(c)}
85
+ end.flatten
86
+ end
87
+ end
88
+
89
+ # Runs SassScript interpolation in the selector,
90
+ # and then parses the result into a {Sass::Selector::CommaSequence}.
91
+ def visit_extend(node)
92
+ parser = Sass::SCSS::CssParser.new(run_interp(node.selector), node.line)
93
+ node.resolved_selector = parser.parse_selector(node.filename)
94
+ node
95
+ end
96
+
97
+ # Runs the child nodes once for each time through the loop, varying the variable each time.
98
+ def visit_for(node)
99
+ from = node.from.perform(@environment)
100
+ to = node.to.perform(@environment)
101
+ from.assert_int!
102
+ to.assert_int!
103
+
104
+ to = to.coerce(from.numerator_units, from.denominator_units)
105
+ range = Range.new(from.to_i, to.to_i, node.exclusive)
106
+
107
+ with_environment Sass::Environment.new(@environment) do
108
+ range.map do |i|
109
+ @environment.set_local_var(node.var,
110
+ Sass::Script::Number.new(i, from.numerator_units, from.denominator_units))
111
+ node.children.map {|c| visit(c)}
112
+ end.flatten
113
+ end
114
+ end
115
+
116
+ # Loads the function into the environment.
117
+ def visit_function(node)
118
+ @environment.set_function(node.name,
119
+ Sass::Callable.new(node.name, node.args, @environment, node.children))
120
+ []
121
+ end
122
+
123
+ # Runs the child nodes if the conditional expression is true;
124
+ # otherwise, tries the else nodes.
125
+ def visit_if(node)
126
+ if node.expr.nil? || node.expr.perform(@environment).to_bool
127
+ yield
128
+ node.children
129
+ elsif node.else
130
+ visit(node.else)
131
+ else
132
+ []
133
+ end
134
+ end
135
+
136
+ # Returns a static DirectiveNode if this is importing a CSS file,
137
+ # or parses and includes the imported Sass file.
138
+ def visit_import(node)
139
+ if path = node.css_import?
140
+ return Sass::Tree::DirectiveNode.new("@import url(#{path})")
141
+ end
142
+
143
+ @environment.push_frame(:filename => node.filename, :line => node.line)
144
+ root = node.imported_file.to_tree
145
+ node.children = root.children.map {|c| visit(c)}.flatten
146
+ node
147
+ rescue Sass::SyntaxError => e
148
+ e.modify_backtrace(:filename => node.imported_file.options[:filename])
149
+ e.add_backtrace(:filename => node.filename, :line => node.line)
150
+ raise e
151
+ ensure
152
+ @environment.pop_frame
153
+ end
154
+
155
+ # Loads a mixin into the environment.
156
+ def visit_mixindef(node)
157
+ @environment.set_mixin(node.name,
158
+ Sass::Callable.new(node.name, node.args, @environment, node.children))
159
+ []
160
+ end
161
+
162
+ # Runs a mixin.
163
+ def visit_mixin(node)
164
+ handle_include_loop!(node) if @environment.mixins_in_use.include?(node.name)
165
+
166
+ original_env = @environment
167
+ original_env.push_frame(:filename => node.filename, :line => node.line)
168
+ original_env.prepare_frame(:mixin => node.name)
169
+ raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
170
+
171
+ passed_args = node.args.dup
172
+ passed_keywords = node.keywords.dup
173
+
174
+ raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < passed_args.size
175
+ Mixin #{node.name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
176
+ but #{node.args.size} #{node.args.size == 1 ? 'was' : 'were'} passed.
177
+ END
178
+
179
+ passed_keywords.each do |name, value|
180
+ # TODO: Make this fast
181
+ unless mixin.args.find {|(var, default)| var.underscored_name == name}
182
+ raise Sass::SyntaxError.new("Mixin #{node.name} doesn't have an argument named $#{name}")
183
+ end
184
+ end
185
+
186
+ environment = mixin.args.zip(passed_args).
187
+ inject(Sass::Environment.new(mixin.environment)) do |env, ((var, default), value)|
188
+ env.set_local_var(var.name,
189
+ if value
190
+ value.perform(@environment)
191
+ elsif kv = passed_keywords[var.underscored_name]
192
+ kv.perform(@environment)
193
+ elsif default
194
+ default.perform(env)
195
+ end)
196
+ raise Sass::SyntaxError.new("Mixin #{node.name} is missing parameter #{var.inspect}.") unless env.var(var.name)
197
+ env
198
+ end
199
+
200
+ with_environment(environment) {node.children = mixin.tree.map {|c| visit(c)}.flatten}
201
+ node
202
+ rescue Sass::SyntaxError => e
203
+ if original_env # Don't add backtrace info if this is an @include loop
204
+ e.modify_backtrace(:mixin => node.name, :line => node.line)
205
+ e.add_backtrace(:line => node.line)
206
+ end
207
+ raise e
208
+ ensure
209
+ original_env.pop_frame if original_env
210
+ end
211
+
212
+ # Runs any SassScript that may be embedded in a property.
213
+ def visit_prop(node)
214
+ node.resolved_name = run_interp(node.name)
215
+ val = node.value.perform(@environment)
216
+ node.resolved_value = val.to_s
217
+ yield
218
+ end
219
+
220
+ # Returns the value of the expression.
221
+ def visit_return(node)
222
+ throw :_sass_return, node.expr.perform(@environment)
223
+ end
224
+
225
+ # Runs SassScript interpolation in the selector,
226
+ # and then parses the result into a {Sass::Selector::CommaSequence}.
227
+ def visit_rule(node)
228
+ parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.line)
229
+ node.parsed_rules ||= parser.parse_selector(node.filename)
230
+ yield
231
+ end
232
+
233
+ # Loads the new variable value into the environment.
234
+ def visit_variable(node)
235
+ return [] if node.guarded && !@environment.var(node.name).nil?
236
+ val = node.expr.perform(@environment)
237
+ @environment.set_var(node.name, val)
238
+ []
239
+ end
240
+
241
+ # Prints the expression to STDERR with a stylesheet trace.
242
+ def visit_warn(node)
243
+ @environment.push_frame(:filename => node.filename, :line => node.line)
244
+ res = node.expr.perform(@environment)
245
+ res = res.value if res.is_a?(Sass::Script::String)
246
+ msg = "WARNING: #{res}\n"
247
+ @environment.stack.reverse.each_with_index do |entry, i|
248
+ msg << " #{i == 0 ? "on" : "from"} line #{entry[:line]}" <<
249
+ " of #{entry[:filename] || "an unknown file"}"
250
+ msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
251
+ msg << "\n"
252
+ end
253
+ Sass::Util.sass_warn msg
254
+ []
255
+ ensure
256
+ @environment.pop_frame
257
+ end
258
+
259
+ # Runs the child nodes until the continuation expression becomes false.
260
+ def visit_while(node)
261
+ children = []
262
+ with_environment Sass::Environment.new(@environment) do
263
+ children += node.children.map {|c| visit(c)} while node.expr.perform(@environment).to_bool
264
+ end
265
+ children.flatten
266
+ end
267
+
268
+ def visit_directive(node)
269
+ if node.value['#{']
270
+ node.value = run_interp(Sass::Engine.parse_interp(node.value, node.line, 0, node.options))
271
+ end
272
+ yield
273
+ node
274
+ end
275
+
276
+ private
277
+
278
+ def run_interp(text)
279
+ text.map do |r|
280
+ next r if r.is_a?(String)
281
+ val = r.perform(@environment)
282
+ # Interpolated strings should never render with quotes
283
+ next val.value if val.is_a?(Sass::Script::String)
284
+ val.to_s
285
+ end.join.strip
286
+ end
287
+
288
+ def handle_include_loop!(node)
289
+ msg = "An @include loop has been found:"
290
+ mixins = @environment.stack.map {|s| s[:mixin]}.compact
291
+ if mixins.size == 2 && mixins[0] == mixins[1]
292
+ raise Sass::SyntaxError.new("#{msg} #{node.name} includes itself")
293
+ end
294
+
295
+ mixins << node.name
296
+ msg << "\n" << Sass::Util.enum_cons(mixins, 2).map do |m1, m2|
297
+ " #{m1} includes #{m2}"
298
+ end.join("\n")
299
+ raise Sass::SyntaxError.new(msg)
300
+ end
301
+ end