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.
- data/VERSION +1 -1
- data/vendor/sass/Rakefile +22 -46
- data/vendor/sass/VERSION +1 -1
- data/vendor/sass/VERSION_NAME +1 -1
- data/vendor/sass/bin/scss +8 -0
- data/vendor/sass/doc-src/SASS_CHANGELOG.md +125 -9
- data/vendor/sass/doc-src/SASS_REFERENCE.md +84 -8
- data/vendor/sass/lib/sass.rb +0 -3
- data/vendor/sass/lib/sass/cache_stores.rb +1 -0
- data/vendor/sass/lib/sass/cache_stores/base.rb +2 -2
- data/vendor/sass/lib/sass/cache_stores/chain.rb +33 -0
- data/vendor/sass/lib/sass/cache_stores/filesystem.rb +6 -4
- data/vendor/sass/lib/sass/cache_stores/memory.rb +8 -12
- data/vendor/sass/lib/sass/engine.rb +65 -56
- data/vendor/sass/lib/sass/environment.rb +5 -2
- data/vendor/sass/lib/sass/exec.rb +52 -21
- data/vendor/sass/lib/sass/importers/filesystem.rb +32 -9
- data/vendor/sass/lib/sass/less.rb +1 -1
- data/vendor/sass/lib/sass/plugin.rb +11 -1
- data/vendor/sass/lib/sass/plugin/compiler.rb +21 -12
- data/vendor/sass/lib/sass/plugin/rails.rb +8 -82
- data/vendor/sass/lib/sass/plugin/staleness_checker.rb +10 -10
- data/vendor/sass/lib/sass/railtie.rb +3 -2
- data/vendor/sass/lib/sass/script.rb +2 -25
- data/vendor/sass/lib/sass/script/color.rb +4 -15
- data/vendor/sass/lib/sass/script/funcall.rb +63 -19
- data/vendor/sass/lib/sass/script/functions.rb +257 -19
- data/vendor/sass/lib/sass/script/lexer.rb +1 -4
- data/vendor/sass/lib/sass/script/list.rb +2 -2
- data/vendor/sass/lib/sass/script/node.rb +0 -27
- data/vendor/sass/lib/sass/script/number.rb +1 -1
- data/vendor/sass/lib/sass/script/operation.rb +0 -5
- data/vendor/sass/lib/sass/script/parser.rb +30 -12
- data/vendor/sass/lib/sass/script/string.rb +2 -17
- data/vendor/sass/lib/sass/script/string_interpolation.rb +1 -0
- data/vendor/sass/lib/sass/scss/parser.rb +58 -18
- data/vendor/sass/lib/sass/scss/rx.rb +2 -1
- data/vendor/sass/lib/sass/scss/script_lexer.rb +1 -1
- data/vendor/sass/lib/sass/selector/comma_sequence.rb +2 -3
- data/vendor/sass/lib/sass/selector/sequence.rb +3 -6
- data/vendor/sass/lib/sass/selector/simple_sequence.rb +2 -3
- data/vendor/sass/lib/sass/tree/charset_node.rb +0 -15
- data/vendor/sass/lib/sass/tree/comment_node.rb +20 -71
- data/vendor/sass/lib/sass/tree/debug_node.rb +4 -22
- data/vendor/sass/lib/sass/tree/directive_node.rb +0 -52
- data/vendor/sass/lib/sass/tree/each_node.rb +8 -38
- data/vendor/sass/lib/sass/tree/extend_node.rb +12 -48
- data/vendor/sass/lib/sass/tree/for_node.rb +20 -51
- data/vendor/sass/lib/sass/tree/function_node.rb +27 -0
- data/vendor/sass/lib/sass/tree/if_node.rb +22 -57
- data/vendor/sass/lib/sass/tree/import_node.rb +0 -56
- data/vendor/sass/lib/sass/tree/media_node.rb +0 -43
- data/vendor/sass/lib/sass/tree/mixin_def_node.rb +12 -45
- data/vendor/sass/lib/sass/tree/mixin_node.rb +13 -124
- data/vendor/sass/lib/sass/tree/node.rb +18 -304
- data/vendor/sass/lib/sass/tree/prop_node.rb +24 -92
- data/vendor/sass/lib/sass/tree/return_node.rb +18 -0
- data/vendor/sass/lib/sass/tree/root_node.rb +4 -133
- data/vendor/sass/lib/sass/tree/rule_node.rb +21 -164
- data/vendor/sass/lib/sass/tree/variable_node.rb +14 -23
- data/vendor/sass/lib/sass/tree/visitors/base.rb +75 -0
- data/vendor/sass/lib/sass/tree/visitors/check_nesting.rb +134 -0
- data/vendor/sass/lib/sass/tree/visitors/convert.rb +255 -0
- data/vendor/sass/lib/sass/tree/visitors/cssize.rb +175 -0
- data/vendor/sass/lib/sass/tree/visitors/perform.rb +301 -0
- data/vendor/sass/lib/sass/tree/visitors/to_css.rb +216 -0
- data/vendor/sass/lib/sass/tree/warn_node.rb +4 -28
- data/vendor/sass/lib/sass/tree/while_node.rb +5 -35
- data/vendor/sass/lib/sass/util.rb +0 -50
- data/vendor/sass/sass.gemspec +1 -1
- data/vendor/sass/test/sass/conversion_test.rb +53 -102
- data/vendor/sass/test/sass/engine_test.rb +416 -540
- data/vendor/sass/test/sass/functions_test.rb +306 -4
- data/vendor/sass/test/sass/importer_test.rb +0 -22
- data/vendor/sass/test/sass/plugin_test.rb +51 -21
- data/vendor/sass/test/sass/results/if.css +3 -0
- data/vendor/sass/test/sass/script_conversion_test.rb +0 -38
- data/vendor/sass/test/sass/script_test.rb +19 -4
- data/vendor/sass/test/sass/scss/scss_test.rb +32 -11
- data/vendor/sass/test/sass/templates/if.sass +11 -0
- data/vendor/sass/test/sass/templates/nested_import.sass +2 -0
- data/vendor/sass/test/sass/util_test.rb +0 -21
- data/vendor/sass/test/test_helper.rb +0 -3
- metadata +268 -258
- data/vendor/sass/bin/css2sass +0 -13
- data/vendor/sass/lib/sass/cache_stores/active_support.rb +0 -28
- 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
|