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
@@ -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] The parse tree for the initial variable value
9
- # @param guarded [Boolean] Whether this is a guarded variable assignment (`||=`)
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