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
@@ -4,36 +4,27 @@ module Sass
|
|
4
4
|
#
|
5
5
|
# @see Sass::Tree
|
6
6
|
class VariableNode < Node
|
7
|
+
# The name of the variable.
|
8
|
+
# @return [String]
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# The parse tree for the variable value.
|
12
|
+
# @return [Script::Node]
|
13
|
+
attr_reader :expr
|
14
|
+
|
15
|
+
# Whether this is a guarded variable assignment (`!default`).
|
16
|
+
# @return [Boolean]
|
17
|
+
attr_reader :guarded
|
18
|
+
|
7
19
|
# @param name [String] The name of the variable
|
8
|
-
# @param expr [Script::Node]
|
9
|
-
# @param guarded [Boolean]
|
20
|
+
# @param expr [Script::Node] See \{#expr}
|
21
|
+
# @param guarded [Boolean] See \{#guarded}
|
10
22
|
def initialize(name, expr, guarded)
|
11
23
|
@name = name
|
12
24
|
@expr = expr
|
13
25
|
@guarded = guarded
|
14
26
|
super()
|
15
27
|
end
|
16
|
-
|
17
|
-
protected
|
18
|
-
|
19
|
-
# @see Node#to_src
|
20
|
-
def to_src(tabs, opts, fmt)
|
21
|
-
"#{' ' * tabs}$#{dasherize(@name, opts)}: #{@expr.to_sass(opts)}#{' !default' if @guarded}#{semi fmt}\n"
|
22
|
-
end
|
23
|
-
|
24
|
-
# Loads the new variable value into the environment.
|
25
|
-
#
|
26
|
-
# @param environment [Sass::Environment] The lexical environment containing
|
27
|
-
# variable and mixin values
|
28
|
-
def _perform(environment)
|
29
|
-
return [] if @guarded && !environment.var(@name).nil?
|
30
|
-
val = @expr.perform(environment)
|
31
|
-
if @expr.context == :equals && val.is_a?(Sass::Script::String)
|
32
|
-
val = Sass::Script::String.new(val.value)
|
33
|
-
end
|
34
|
-
environment.set_var(@name, val)
|
35
|
-
[]
|
36
|
-
end
|
37
28
|
end
|
38
29
|
end
|
39
30
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Visitors are used to traverse the Sass parse tree.
|
2
|
+
# Visitors should extend {Visitors::Base},
|
3
|
+
# which provides a small amount of scaffolding for traversal.
|
4
|
+
module Sass::Tree::Visitors
|
5
|
+
# The abstract base class for Sass visitors.
|
6
|
+
# Visitors should extend this class,
|
7
|
+
# then implement `visit_*` methods for each node they care about
|
8
|
+
# (e.g. `visit_rule` for {RuleNode} or `visit_for` for {ForNode}).
|
9
|
+
# These methods take the node in question as argument.
|
10
|
+
# They may `yield` to visit the child nodes of the current node.
|
11
|
+
#
|
12
|
+
# *Note*: due to the unusual nature of {Sass::Tree::IfNode},
|
13
|
+
# special care must be taken to ensure that it is properly handled.
|
14
|
+
# In particular, there is no built-in scaffolding
|
15
|
+
# for dealing with the return value of `@else` nodes.
|
16
|
+
#
|
17
|
+
# @abstract
|
18
|
+
class Base
|
19
|
+
# Runs the visitor on a tree.
|
20
|
+
#
|
21
|
+
# @param root [Tree::Node] The root node of the Sass tree.
|
22
|
+
# @return [Object] The return value of \{#visit} for the root node.
|
23
|
+
def self.visit(root)
|
24
|
+
new.send(:visit, root)
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
# Runs the visitor on the given node.
|
30
|
+
# This can be overridden by subclasses that need to do something for each node.
|
31
|
+
#
|
32
|
+
# @param node [Tree::Node] The node to visit.
|
33
|
+
# @return [Object] The return value of the `visit_*` method for this node.
|
34
|
+
def visit(node)
|
35
|
+
method = "visit_#{node_name node}"
|
36
|
+
if self.respond_to?(method)
|
37
|
+
self.send(method, node) {visit_children(node)}
|
38
|
+
else
|
39
|
+
visit_children(node)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Visit the child nodes for a given node.
|
44
|
+
# This can be overridden by subclasses that need to do something
|
45
|
+
# with the child nodes' return values.
|
46
|
+
#
|
47
|
+
# This method is run when `visit_*` methods `yield`,
|
48
|
+
# and its return value is returned from the `yield`.
|
49
|
+
#
|
50
|
+
# @param parent [Tree::Node] The parent node of the children to visit.
|
51
|
+
# @return [Array<Object>] The return values of the `visit_*` methods for the children.
|
52
|
+
def visit_children(parent)
|
53
|
+
parent.children.map {|c| visit(c)}
|
54
|
+
end
|
55
|
+
|
56
|
+
NODE_NAME_RE = /.*::(.*?)Node$/
|
57
|
+
|
58
|
+
# Returns the name of a node as used in the `visit_*` method.
|
59
|
+
#
|
60
|
+
# @param [Tree::Node] node The node.
|
61
|
+
# @return [String] The name.
|
62
|
+
def node_name(node)
|
63
|
+
@@node_names ||= {}
|
64
|
+
@@node_names[node.class.name] ||= node.class.name.gsub(NODE_NAME_RE, '\\1').downcase
|
65
|
+
end
|
66
|
+
|
67
|
+
# `yield`s, then runs the visitor on the `@else` clause if the node has one.
|
68
|
+
# This exists to ensure that the contents of the `@else` clause get visited.
|
69
|
+
def visit_if(node)
|
70
|
+
yield
|
71
|
+
visit(node.else) if node.else
|
72
|
+
node
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# A visitor for checking that all nodes are properly nested.
|
2
|
+
class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
3
|
+
protected
|
4
|
+
|
5
|
+
def visit(node)
|
6
|
+
if error = (@parent && (
|
7
|
+
try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
|
8
|
+
try_send("invalid_#{node_name node}_parent?", @parent, node))) ||
|
9
|
+
(@real_parent && (
|
10
|
+
try_send("invalid_#{node_name @real_parent}_real_child?", @real_parent, node) ||
|
11
|
+
try_send("invalid_#{node_name node}_real_parent?", @real_parent, node)))
|
12
|
+
raise Sass::SyntaxError.new(error)
|
13
|
+
end
|
14
|
+
super
|
15
|
+
rescue Sass::SyntaxError => e
|
16
|
+
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
17
|
+
raise e
|
18
|
+
end
|
19
|
+
|
20
|
+
PARENT_CLASSES = [ Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode,
|
21
|
+
Sass::Tree::ImportNode, Sass::Tree::MixinNode, Sass::Tree::WhileNode]
|
22
|
+
def visit_children(parent)
|
23
|
+
old_parent = @parent
|
24
|
+
@parent = parent unless is_any_of?(parent, PARENT_CLASSES)
|
25
|
+
old_real_parent, @real_parent = @real_parent, parent
|
26
|
+
super
|
27
|
+
ensure
|
28
|
+
@parent = old_parent
|
29
|
+
@real_parent = old_real_parent
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_root(node)
|
33
|
+
yield
|
34
|
+
rescue Sass::SyntaxError => e
|
35
|
+
e.sass_template ||= node.template
|
36
|
+
raise e
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit_import(node)
|
40
|
+
yield
|
41
|
+
rescue Sass::SyntaxError => e
|
42
|
+
e.modify_backtrace(:filename => node.children.first.filename)
|
43
|
+
e.add_backtrace(:filename => node.filename, :line => node.line)
|
44
|
+
raise e
|
45
|
+
end
|
46
|
+
|
47
|
+
def invalid_charset_parent?(parent, child)
|
48
|
+
"@charset may only be used at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
|
49
|
+
end
|
50
|
+
|
51
|
+
INVALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode]
|
52
|
+
def invalid_extend_parent?(parent, child)
|
53
|
+
unless is_any_of?(parent, INVALID_EXTEND_PARENTS)
|
54
|
+
"Extend directives may only be used within rules."
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def invalid_function_parent?(parent, child)
|
59
|
+
"Functions may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
|
60
|
+
end
|
61
|
+
|
62
|
+
INVALID_FUNCTION_CHILDREN = [
|
63
|
+
Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::EachNode,
|
64
|
+
Sass::Tree::ForNode, Sass::Tree::IfNode, Sass::Tree::ReturnNode,
|
65
|
+
Sass::Tree::VariableNode, Sass::Tree::WarnNode, Sass::Tree::WhileNode
|
66
|
+
]
|
67
|
+
def invalid_function_child?(parent, child)
|
68
|
+
unless is_any_of?(child, INVALID_FUNCTION_CHILDREN)
|
69
|
+
"Functions can only contain variable declarations and control directives."
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
INVALID_IMPORT_PARENTS = [
|
74
|
+
Sass::Tree::IfNode, Sass::Tree::ForNode, Sass::Tree::WhileNode,
|
75
|
+
Sass::Tree::EachNode, Sass::Tree::MixinDefNode
|
76
|
+
]
|
77
|
+
def invalid_import_parent?(parent, child)
|
78
|
+
if is_any_of?(@real_parent, INVALID_IMPORT_PARENTS)
|
79
|
+
return "Import directives may not be used within control directives or mixins."
|
80
|
+
end
|
81
|
+
return if parent.is_a?(Sass::Tree::RootNode)
|
82
|
+
return "CSS import directives may only be used at the root of a document." if child.css_import?
|
83
|
+
# If this is a nested @import, we need to make sure it doesn't have anything
|
84
|
+
# that's legal at top-level but not in the current context (e.g. mixin defs).
|
85
|
+
child.imported_file.to_tree.children.each {|c| visit(c)}
|
86
|
+
nil
|
87
|
+
rescue Sass::SyntaxError => e
|
88
|
+
e.modify_backtrace(:filename => child.imported_file.options[:filename])
|
89
|
+
e.add_backtrace(:filename => child.filename, :line => child.line)
|
90
|
+
raise e
|
91
|
+
end
|
92
|
+
|
93
|
+
def invalid_import_real_parent?(parent, child)
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
def invalid_mixindef_parent?(parent, child)
|
98
|
+
"Mixins may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
|
99
|
+
end
|
100
|
+
|
101
|
+
INVALID_PROP_CHILDREN = [Sass::Tree::CommentNode, Sass::Tree::PropNode]
|
102
|
+
def invalid_prop_child?(parent, child)
|
103
|
+
unless is_any_of?(child, INVALID_PROP_CHILDREN)
|
104
|
+
"Illegal nesting: Only properties may be nested beneath properties."
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
INVALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
|
109
|
+
Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode]
|
110
|
+
def invalid_prop_parent?(parent, child)
|
111
|
+
unless is_any_of?(parent, INVALID_PROP_PARENTS)
|
112
|
+
"Properties are only allowed within rules, directives, or other properties." + child.pseudo_class_selector_message
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def invalid_return_parent?(parent, child)
|
117
|
+
"@return may only be used within a function." unless parent.is_a?(Sass::Tree::FunctionNode)
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def is_any_of?(val, classes)
|
123
|
+
for c in classes
|
124
|
+
return true if val.is_a?(c)
|
125
|
+
end
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
|
129
|
+
def try_send(method, *args, &block)
|
130
|
+
return unless respond_to?(method)
|
131
|
+
send(method, *args, &block)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
@@ -0,0 +1,255 @@
|
|
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
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def initialize(options, format)
|
16
|
+
@options = options
|
17
|
+
@format = format
|
18
|
+
@tabs = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_children(parent)
|
22
|
+
@tabs += 1
|
23
|
+
return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
|
24
|
+
(@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : " }\n")
|
25
|
+
ensure
|
26
|
+
@tabs -= 1
|
27
|
+
end
|
28
|
+
|
29
|
+
# Ensures proper spacing between top-level nodes.
|
30
|
+
def visit_root(node)
|
31
|
+
Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
|
32
|
+
visit(child) +
|
33
|
+
if nxt &&
|
34
|
+
(child.is_a?(Sass::Tree::CommentNode) &&
|
35
|
+
child.line + child.value.count("\n") + 1 == nxt.line) ||
|
36
|
+
(child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
|
37
|
+
child.line + 1 == nxt.line) ||
|
38
|
+
(child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
|
39
|
+
child.line + 1 == nxt.line)
|
40
|
+
""
|
41
|
+
else
|
42
|
+
"\n"
|
43
|
+
end
|
44
|
+
end.join.rstrip + "\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit_charset(node)
|
48
|
+
"#{tab_str}@charset \"#{node.name}\"#{semi}\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def visit_comment(node)
|
52
|
+
content = if @format == :sass
|
53
|
+
content = node.value.gsub(/\*\/$/, '').rstrip
|
54
|
+
if content =~ /\A[ \t]/
|
55
|
+
# Re-indent SCSS comments like this:
|
56
|
+
# /* foo
|
57
|
+
# bar
|
58
|
+
# baz */
|
59
|
+
content.gsub!(/^/, ' ')
|
60
|
+
content.sub!(/\A([ \t]*)\/\*/, '/*\1')
|
61
|
+
end
|
62
|
+
|
63
|
+
content =
|
64
|
+
unless content.include?("\n")
|
65
|
+
content
|
66
|
+
else
|
67
|
+
content.gsub!(/\n( \*|\/\/)/, "\n ")
|
68
|
+
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
|
69
|
+
sep = node.silent ? "\n//" : "\n *"
|
70
|
+
if spaces >= 2
|
71
|
+
content.gsub(/\n /, sep)
|
72
|
+
else
|
73
|
+
content.gsub(/\n#{' ' * spaces}/, sep)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
content.gsub!(/\A\/\*/, '//') if node.silent
|
78
|
+
content.gsub!(/^/, tab_str)
|
79
|
+
content.rstrip + "\n"
|
80
|
+
else
|
81
|
+
spaces = (' ' * [@tabs - node.value[/^ */].size, 0].max)
|
82
|
+
content = if node.silent
|
83
|
+
node.value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
|
84
|
+
else
|
85
|
+
node.value
|
86
|
+
end.gsub(/^/, spaces) + "\n"
|
87
|
+
content
|
88
|
+
end
|
89
|
+
if node.loud
|
90
|
+
if node.silent
|
91
|
+
content.gsub!(%r{^\s*(//!?)}, '//!')
|
92
|
+
else
|
93
|
+
content.sub!(%r{^\s*(/\*)}, '/*!')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
content
|
97
|
+
end
|
98
|
+
|
99
|
+
def visit_debug(node)
|
100
|
+
"#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
def visit_directive(node)
|
104
|
+
res = "#{tab_str}#{node.value}"
|
105
|
+
return res + "#{semi}\n" unless node.has_children
|
106
|
+
res + yield + "\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
def visit_each(node)
|
110
|
+
"#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def visit_extend(node)
|
114
|
+
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}\n"
|
115
|
+
end
|
116
|
+
|
117
|
+
def visit_for(node)
|
118
|
+
"#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
|
119
|
+
"#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def visit_function(node)
|
123
|
+
args = node.args.map do |v, d|
|
124
|
+
d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
|
125
|
+
end.join(", ")
|
126
|
+
|
127
|
+
"#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def visit_if(node)
|
131
|
+
name =
|
132
|
+
if !@is_else; "if"
|
133
|
+
elsif node.expr; "else if"
|
134
|
+
else; "else"
|
135
|
+
end
|
136
|
+
str = "#{tab_str}@#{name}"
|
137
|
+
str << " #{node.expr.to_sass(@options)}" if node.expr
|
138
|
+
str << yield
|
139
|
+
@is_else = true
|
140
|
+
str << visit(node.else) if node.else
|
141
|
+
str
|
142
|
+
ensure
|
143
|
+
@is_else = false
|
144
|
+
end
|
145
|
+
|
146
|
+
def visit_import(node)
|
147
|
+
quote = @format == :scss ? '"' : ''
|
148
|
+
"#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
|
149
|
+
end
|
150
|
+
|
151
|
+
def visit_media(node)
|
152
|
+
"#{tab_str}@media #{node.query}#{yield}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def visit_mixindef(node)
|
156
|
+
args =
|
157
|
+
if node.args.empty?
|
158
|
+
""
|
159
|
+
else
|
160
|
+
'(' + node.args.map do |v, d|
|
161
|
+
if d
|
162
|
+
"#{v.to_sass(@options)}: #{d.to_sass(@options)}"
|
163
|
+
else
|
164
|
+
v.to_sass(@options)
|
165
|
+
end
|
166
|
+
end.join(", ") + ')'
|
167
|
+
end
|
168
|
+
|
169
|
+
"#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
|
170
|
+
end
|
171
|
+
|
172
|
+
def visit_mixin(node)
|
173
|
+
unless node.args.empty? && node.keywords.empty?
|
174
|
+
args = node.args.map {|a| a.to_sass(@options)}.join(", ")
|
175
|
+
keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
|
176
|
+
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
|
177
|
+
end
|
178
|
+
"#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
|
179
|
+
end
|
180
|
+
|
181
|
+
def visit_prop(node)
|
182
|
+
res = tab_str + node.declaration(@options, @format)
|
183
|
+
return res + semi + "\n" if node.children.empty?
|
184
|
+
res + yield.rstrip + semi + "\n"
|
185
|
+
end
|
186
|
+
|
187
|
+
def visit_return(node)
|
188
|
+
"#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
|
189
|
+
end
|
190
|
+
|
191
|
+
def visit_rule(node)
|
192
|
+
if @format == :sass
|
193
|
+
name = selector_to_sass(node.rule)
|
194
|
+
name = "\\" + name if name[0] == ?:
|
195
|
+
name.gsub(/^/, tab_str) + yield
|
196
|
+
elsif @format == :scss
|
197
|
+
name = selector_to_scss(node.rule)
|
198
|
+
res = name + yield
|
199
|
+
if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.silent
|
200
|
+
res.slice!(-3..-1)
|
201
|
+
res << "\n" << tab_str << "}\n"
|
202
|
+
end
|
203
|
+
res
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def visit_variable(node)
|
208
|
+
"#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
|
209
|
+
end
|
210
|
+
|
211
|
+
def visit_warn(node)
|
212
|
+
"#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
|
213
|
+
end
|
214
|
+
|
215
|
+
def visit_while(node)
|
216
|
+
"#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
|
217
|
+
end
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
def selector_to_src(sel)
|
222
|
+
@format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
|
223
|
+
end
|
224
|
+
|
225
|
+
def selector_to_sass(sel)
|
226
|
+
sel.map do |r|
|
227
|
+
if r.is_a?(String)
|
228
|
+
r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
|
229
|
+
else
|
230
|
+
"\#{#{r.to_sass(@options)}}"
|
231
|
+
end
|
232
|
+
end.join
|
233
|
+
end
|
234
|
+
|
235
|
+
def selector_to_scss(sel)
|
236
|
+
sel.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(@options)}}"}.
|
237
|
+
join.gsub(/^[ \t]*/, tab_str)
|
238
|
+
end
|
239
|
+
|
240
|
+
def semi
|
241
|
+
@format == :sass ? "" : ";"
|
242
|
+
end
|
243
|
+
|
244
|
+
def tab_str
|
245
|
+
' ' * @tabs
|
246
|
+
end
|
247
|
+
|
248
|
+
def dasherize(s)
|
249
|
+
if @options[:dasherize]
|
250
|
+
s.gsub('_', '-')
|
251
|
+
else
|
252
|
+
s
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|