haml 2.0.10 → 2.2.0
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/.yardopts +5 -0
- data/MIT-LICENSE +1 -1
- data/README.md +347 -0
- data/Rakefile +124 -19
- data/VERSION +1 -1
- data/VERSION_NAME +1 -0
- data/extra/haml-mode.el +397 -78
- data/extra/sass-mode.el +148 -36
- data/extra/update_watch.rb +13 -0
- data/lib/haml.rb +15 -993
- data/lib/haml/buffer.rb +131 -84
- data/lib/haml/engine.rb +129 -97
- data/lib/haml/error.rb +7 -7
- data/lib/haml/exec.rb +127 -42
- data/lib/haml/filters.rb +107 -42
- data/lib/haml/helpers.rb +210 -156
- data/lib/haml/helpers/action_view_extensions.rb +34 -39
- data/lib/haml/helpers/action_view_mods.rb +132 -139
- data/lib/haml/html.rb +77 -65
- data/lib/haml/precompiler.rb +404 -213
- data/lib/haml/shared.rb +78 -0
- data/lib/haml/template.rb +14 -14
- data/lib/haml/template/patch.rb +2 -2
- data/lib/haml/template/plugin.rb +2 -3
- data/lib/haml/util.rb +211 -6
- data/lib/haml/version.rb +30 -13
- data/lib/sass.rb +7 -856
- data/lib/sass/css.rb +169 -161
- data/lib/sass/engine.rb +344 -328
- data/lib/sass/environment.rb +79 -0
- data/lib/sass/error.rb +33 -11
- data/lib/sass/files.rb +139 -0
- data/lib/sass/plugin.rb +160 -117
- data/lib/sass/plugin/merb.rb +7 -6
- data/lib/sass/plugin/rails.rb +5 -6
- data/lib/sass/repl.rb +58 -0
- data/lib/sass/script.rb +59 -0
- data/lib/sass/script/bool.rb +17 -0
- data/lib/sass/script/color.rb +183 -0
- data/lib/sass/script/funcall.rb +50 -0
- data/lib/sass/script/functions.rb +198 -0
- data/lib/sass/script/lexer.rb +178 -0
- data/lib/sass/script/literal.rb +177 -0
- data/lib/sass/script/node.rb +14 -0
- data/lib/sass/script/number.rb +381 -0
- data/lib/sass/script/operation.rb +45 -0
- data/lib/sass/script/parser.rb +172 -0
- data/lib/sass/script/string.rb +12 -0
- data/lib/sass/script/unary_operation.rb +34 -0
- data/lib/sass/script/variable.rb +31 -0
- data/lib/sass/tree/comment_node.rb +73 -10
- data/lib/sass/tree/debug_node.rb +30 -0
- data/lib/sass/tree/directive_node.rb +42 -17
- data/lib/sass/tree/file_node.rb +41 -0
- data/lib/sass/tree/for_node.rb +48 -0
- data/lib/sass/tree/if_node.rb +54 -0
- data/lib/sass/tree/mixin_def_node.rb +29 -0
- data/lib/sass/tree/mixin_node.rb +48 -0
- data/lib/sass/tree/node.rb +214 -11
- data/lib/sass/tree/prop_node.rb +109 -0
- data/lib/sass/tree/rule_node.rb +178 -51
- data/lib/sass/tree/variable_node.rb +34 -0
- data/lib/sass/tree/while_node.rb +31 -0
- data/test/haml/engine_test.rb +331 -36
- data/test/haml/helper_test.rb +12 -1
- data/test/haml/results/content_for_layout.xhtml +0 -3
- data/test/haml/results/filters.xhtml +2 -0
- data/test/haml/results/list.xhtml +1 -1
- data/test/haml/template_test.rb +7 -2
- data/test/haml/templates/content_for_layout.haml +0 -2
- data/test/haml/templates/list.haml +1 -1
- data/test/haml/util_test.rb +92 -0
- data/test/sass/css2sass_test.rb +69 -24
- data/test/sass/engine_test.rb +586 -64
- data/test/sass/functions_test.rb +125 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +81 -28
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/{constants.css → script.css} +4 -4
- data/test/sass/results/subdir/subdir.css +2 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/script_test.rb +258 -0
- data/test/sass/templates/import.sass +1 -1
- data/test/sass/templates/importee.sass +7 -2
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/{constants.sass → script.sass} +11 -10
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/subdir.sass +2 -2
- data/test/sass/templates/units.sass +11 -0
- data/test/test_helper.rb +14 -0
- metadata +77 -19
- data/FAQ +0 -138
- data/README.rdoc +0 -319
- data/lib/sass/constant.rb +0 -216
- data/lib/sass/constant/color.rb +0 -101
- data/lib/sass/constant/literal.rb +0 -54
- data/lib/sass/constant/nil.rb +0 -9
- data/lib/sass/constant/number.rb +0 -87
- data/lib/sass/constant/operation.rb +0 -30
- data/lib/sass/constant/string.rb +0 -22
- data/lib/sass/tree/attr_node.rb +0 -57
- data/lib/sass/tree/value_node.rb +0 -20
data/lib/sass/css.rb
CHANGED
@@ -3,29 +3,28 @@ require 'sass/tree/node'
|
|
3
3
|
require 'strscan'
|
4
4
|
|
5
5
|
module Sass
|
6
|
-
# :stopdoc:
|
7
6
|
module Tree
|
8
7
|
class Node
|
9
|
-
|
8
|
+
# Converts a node to Sass code that will generate it.
|
9
|
+
#
|
10
|
+
# @param tabs [Fixnum] The amount of tabulation to use for the Sass code
|
11
|
+
# @param opts [Hash<Symbol, Object>] An options hash (see {Sass::CSS#initialize})
|
12
|
+
# @return [String] The Sass code corresponding to the node
|
13
|
+
def to_sass(tabs = 0, opts = {})
|
10
14
|
result = ''
|
11
15
|
|
12
16
|
children.each do |child|
|
13
|
-
result << "#{child.to_sass(0, opts)}\n"
|
17
|
+
result << "#{' ' * tabs}#{child.to_sass(0, opts)}\n"
|
14
18
|
end
|
15
19
|
|
16
20
|
result
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
20
|
-
class ValueNode
|
21
|
-
def to_sass(tabs, opts = {})
|
22
|
-
"#{value}\n"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
24
|
class RuleNode
|
25
|
+
# @see Node#to_sass
|
27
26
|
def to_sass(tabs, opts = {})
|
28
|
-
str = "\n#{' ' * tabs}#{
|
27
|
+
str = "\n#{' ' * tabs}#{rules.first}#{children.any? { |c| c.is_a? PropNode } ? "\n" : ''}"
|
29
28
|
|
30
29
|
children.each do |child|
|
31
30
|
str << "#{child.to_sass(tabs + 1, opts)}"
|
@@ -35,96 +34,51 @@ module Sass
|
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
|
-
class
|
37
|
+
class PropNode
|
38
|
+
# @see Node#to_sass
|
39
39
|
def to_sass(tabs, opts = {})
|
40
|
-
"#{' ' * tabs}#{opts[:
|
40
|
+
"#{' ' * tabs}#{opts[:old] ? ':' : ''}#{name}#{opts[:old] ? '' : ':'} #{value}\n"
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
class DirectiveNode
|
45
|
+
# @see Node#to_sass
|
45
46
|
def to_sass(tabs, opts = {})
|
46
47
|
"#{' ' * tabs}#{value}#{children.map {|c| c.to_sass(tabs + 1, opts)}}\n"
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
# This class
|
52
|
-
# It
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
@hash = {}
|
60
|
-
end
|
61
|
-
|
62
|
-
def initialize_copy(other)
|
63
|
-
@hash = other.instance_variable_get('@hash').clone
|
64
|
-
end
|
65
|
-
|
66
|
-
def [](key)
|
67
|
-
@hash[key] && @hash[key].value
|
68
|
-
end
|
69
|
-
|
70
|
-
def []=(key, value)
|
71
|
-
node = Node.new(key, value)
|
72
|
-
|
73
|
-
if old = @hash[key]
|
74
|
-
if old.prev
|
75
|
-
old.prev.next = old.next
|
76
|
-
else # old is @first and @last
|
77
|
-
@first = @last = nil
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
if @first.nil?
|
82
|
-
@first = @last = node
|
83
|
-
else
|
84
|
-
node.prev = @last
|
85
|
-
@last.next = node
|
86
|
-
@last = node
|
87
|
-
end
|
88
|
-
|
89
|
-
@hash[key] = node
|
90
|
-
value
|
91
|
-
end
|
92
|
-
|
93
|
-
def each
|
94
|
-
return unless @first
|
95
|
-
yield [@first.key, @first.value]
|
96
|
-
node = @first
|
97
|
-
yield [node.key, node.value] while node = node.next
|
98
|
-
self
|
99
|
-
end
|
100
|
-
|
101
|
-
def values
|
102
|
-
self.map { |k, v| v }
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# :startdoc:
|
107
|
-
|
108
|
-
# This class contains the functionality used in the +css2sass+ utility,
|
109
|
-
# namely converting CSS documents to Sass templates.
|
52
|
+
# This class converts CSS documents into Sass templates.
|
53
|
+
# It works by parsing the CSS document into a {Sass::Tree} structure,
|
54
|
+
# and then applying various transformations to the structure
|
55
|
+
# to produce more concise and idiomatic Sass.
|
56
|
+
#
|
57
|
+
# Example usage:
|
58
|
+
#
|
59
|
+
# Sass::CSS.new("p { color: blue }").render #=> "p\n color: blue"
|
110
60
|
class CSS
|
111
|
-
|
112
|
-
#
|
113
|
-
#
|
61
|
+
# @param template [String] The CSS code
|
62
|
+
# @option options :old [Boolean] (false)
|
63
|
+
# Whether or not to output old property syntax
|
64
|
+
# (`:color blue` as opposed to `color: blue`).
|
114
65
|
def initialize(template, options = {})
|
115
66
|
if template.is_a? IO
|
116
67
|
template = template.read
|
117
68
|
end
|
118
69
|
|
119
|
-
@options = options
|
70
|
+
@options = options.dup
|
71
|
+
# Backwards compatibility
|
72
|
+
@options[:old] = true if @options[:alternate] == false
|
120
73
|
@template = StringScanner.new(template)
|
121
74
|
end
|
122
75
|
|
123
|
-
#
|
124
|
-
#
|
76
|
+
# Converts the CSS template into Sass code.
|
77
|
+
#
|
78
|
+
# @return [String] The resulting Sass code
|
125
79
|
def render
|
126
80
|
begin
|
127
|
-
build_tree.to_sass(@options).strip + "\n"
|
81
|
+
build_tree.to_sass(0, @options).strip + "\n"
|
128
82
|
rescue Exception => err
|
129
83
|
line = @template.string[0...@template.pos].split("\n").size
|
130
84
|
|
@@ -135,8 +89,11 @@ module Sass
|
|
135
89
|
|
136
90
|
private
|
137
91
|
|
92
|
+
# Parses the CSS template and applies various transformations
|
93
|
+
#
|
94
|
+
# @return [Tree::Node] The root node of the parsed tree
|
138
95
|
def build_tree
|
139
|
-
root = Tree::Node.new
|
96
|
+
root = Tree::Node.new
|
140
97
|
whitespace
|
141
98
|
rules root
|
142
99
|
expand_commas root
|
@@ -147,6 +104,9 @@ module Sass
|
|
147
104
|
root
|
148
105
|
end
|
149
106
|
|
107
|
+
# Parses a set of CSS rules.
|
108
|
+
#
|
109
|
+
# @param root [Tree::Node] The parent node of the rules
|
150
110
|
def rules(root)
|
151
111
|
while r = rule
|
152
112
|
root << r
|
@@ -154,13 +114,28 @@ module Sass
|
|
154
114
|
end
|
155
115
|
end
|
156
116
|
|
117
|
+
# Parses a single CSS rule.
|
118
|
+
#
|
119
|
+
# @return [Tree::Node] The parsed rule
|
157
120
|
def rule
|
158
|
-
|
121
|
+
rule = ""
|
122
|
+
loop do
|
123
|
+
token = @template.scan(/(?:[^\{\};\/\s]|\/[^*])+/)
|
124
|
+
if token.nil?
|
125
|
+
return if rule.empty?
|
126
|
+
break
|
127
|
+
end
|
128
|
+
rule << token
|
129
|
+
break unless @template.match?(/\s|\/\*/)
|
130
|
+
whitespace
|
131
|
+
rule << " "
|
132
|
+
end
|
133
|
+
|
159
134
|
rule.strip!
|
160
135
|
directive = rule[0] == ?@
|
161
136
|
|
162
137
|
if directive
|
163
|
-
node = Tree::DirectiveNode.new(rule
|
138
|
+
node = Tree::DirectiveNode.new(rule)
|
164
139
|
return node if @template.scan(/;/)
|
165
140
|
|
166
141
|
assert_match /\{/
|
@@ -171,12 +146,15 @@ module Sass
|
|
171
146
|
end
|
172
147
|
|
173
148
|
assert_match /\{/
|
174
|
-
node = Tree::RuleNode.new(rule
|
175
|
-
|
149
|
+
node = Tree::RuleNode.new(rule)
|
150
|
+
properties(node)
|
176
151
|
return node
|
177
152
|
end
|
178
153
|
|
179
|
-
|
154
|
+
# Parses a set of CSS properties within a rule.
|
155
|
+
#
|
156
|
+
# @param rule [Tree::RuleNode] The parent node of the properties
|
157
|
+
def properties(rule)
|
180
158
|
while @template.scan(/[^:\}\s]+/)
|
181
159
|
name = @template[0]
|
182
160
|
whitespace
|
@@ -189,12 +167,15 @@ module Sass
|
|
189
167
|
end
|
190
168
|
|
191
169
|
assert_match /(;|(?=\}))/
|
192
|
-
rule << Tree::
|
170
|
+
rule << Tree::PropNode.new(name, value, nil)
|
193
171
|
end
|
194
172
|
|
195
173
|
assert_match /\}/
|
196
174
|
end
|
197
175
|
|
176
|
+
# Moves the scanner over a section of whitespace or comments.
|
177
|
+
#
|
178
|
+
# @return [String] The ignored whitespace
|
198
179
|
def whitespace
|
199
180
|
space = @template.scan(/\s*/) || ''
|
200
181
|
|
@@ -207,37 +188,53 @@ module Sass
|
|
207
188
|
return space
|
208
189
|
end
|
209
190
|
|
191
|
+
# Moves the scanner over a regular expression,
|
192
|
+
# raising an exception if it doesn't match.
|
193
|
+
#
|
194
|
+
# @param re [Regexp] The regular expression to assert
|
210
195
|
def assert_match(re)
|
211
|
-
if
|
212
|
-
|
213
|
-
|
214
|
-
expected = re.source == Regexp.escape(re.source) ? "\"#{re.source}\"" : re.inspect
|
215
|
-
raise Exception.new("Invalid CSS on line #{line}: expected #{expected}")
|
196
|
+
if @template.scan(re)
|
197
|
+
whitespace
|
198
|
+
return
|
216
199
|
end
|
217
|
-
|
200
|
+
|
201
|
+
line = @template.string[0..@template.pos].count "\n"
|
202
|
+
pos = @template.pos
|
203
|
+
|
204
|
+
after = @template.string[pos - 15...pos]
|
205
|
+
after = "..." + after if pos >= 15
|
206
|
+
|
207
|
+
# Display basic regexps as plain old strings
|
208
|
+
expected = re.source == Regexp.escape(re.source) ? "\"#{re.source}\"" : re.inspect
|
209
|
+
|
210
|
+
was = @template.rest[0...15]
|
211
|
+
was += "..." if @template.rest.size >= 15
|
212
|
+
raise Exception.new(<<MESSAGE)
|
213
|
+
Invalid CSS on line #{line + 1} after #{after.inspect}:
|
214
|
+
expected #{expected}, was #{was.inspect}
|
215
|
+
MESSAGE
|
218
216
|
end
|
219
217
|
|
220
218
|
# Transform
|
221
219
|
#
|
222
|
-
#
|
223
|
-
#
|
220
|
+
# foo, bar, baz
|
221
|
+
# color: blue
|
224
222
|
#
|
225
223
|
# into
|
226
224
|
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
225
|
+
# foo
|
226
|
+
# color: blue
|
227
|
+
# bar
|
228
|
+
# color: blue
|
229
|
+
# baz
|
230
|
+
# color: blue
|
233
231
|
#
|
234
|
-
#
|
235
|
-
# but it's necessary to get nesting to work properly.
|
232
|
+
# @param root [Tree::Node] The parent node
|
236
233
|
def expand_commas(root)
|
237
234
|
root.children.map! do |child|
|
238
|
-
next child unless Tree::RuleNode === child && child.
|
239
|
-
child.
|
240
|
-
node = Tree::RuleNode.new(rule.strip
|
235
|
+
next child unless Tree::RuleNode === child && child.rules.first.include?(',')
|
236
|
+
child.rules.first.split(',').map do |rule|
|
237
|
+
node = Tree::RuleNode.new(rule.strip)
|
241
238
|
node.children = child.children
|
242
239
|
node
|
243
240
|
end
|
@@ -247,71 +244,77 @@ module Sass
|
|
247
244
|
|
248
245
|
# Make rules use parent refs so that
|
249
246
|
#
|
250
|
-
#
|
251
|
-
#
|
252
|
-
#
|
253
|
-
#
|
247
|
+
# foo
|
248
|
+
# color: green
|
249
|
+
# foo.bar
|
250
|
+
# color: blue
|
254
251
|
#
|
255
252
|
# becomes
|
256
253
|
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
260
|
-
#
|
254
|
+
# foo
|
255
|
+
# color: green
|
256
|
+
# &.bar
|
257
|
+
# color: blue
|
261
258
|
#
|
262
259
|
# This has the side effect of nesting rules,
|
263
260
|
# so that
|
264
261
|
#
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
262
|
+
# foo
|
263
|
+
# color: green
|
264
|
+
# foo bar
|
265
|
+
# color: red
|
266
|
+
# foo baz
|
267
|
+
# color: blue
|
271
268
|
#
|
272
269
|
# becomes
|
273
270
|
#
|
274
|
-
#
|
275
|
-
#
|
276
|
-
#
|
277
|
-
#
|
278
|
-
#
|
279
|
-
#
|
271
|
+
# foo
|
272
|
+
# color: green
|
273
|
+
# & bar
|
274
|
+
# color: red
|
275
|
+
# & baz
|
276
|
+
# color: blue
|
280
277
|
#
|
278
|
+
# @param root [Tree::Node] The parent node
|
281
279
|
def parent_ref_rules(root)
|
282
|
-
|
280
|
+
current_rule = nil
|
283
281
|
root.children.select { |c| Tree::RuleNode === c }.each do |child|
|
284
282
|
root.children.delete child
|
285
|
-
first, rest = child.
|
286
|
-
|
283
|
+
first, rest = child.rules.first.scan(/^(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?$/).first
|
284
|
+
|
285
|
+
if current_rule.nil? || current_rule.rules.first != first
|
286
|
+
current_rule = Tree::RuleNode.new(first)
|
287
|
+
root << current_rule
|
288
|
+
end
|
289
|
+
|
287
290
|
if rest
|
288
|
-
child.
|
289
|
-
|
291
|
+
child.rules = ["&" + rest]
|
292
|
+
current_rule << child
|
290
293
|
else
|
291
|
-
|
294
|
+
current_rule.children += child.children
|
292
295
|
end
|
293
296
|
end
|
294
297
|
|
295
|
-
|
296
|
-
root.children += rules.values
|
298
|
+
root.children.each { |v| parent_ref_rules(v) }
|
297
299
|
end
|
298
300
|
|
299
301
|
# Remove useless parent refs so that
|
300
302
|
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
303
|
+
# foo
|
304
|
+
# & bar
|
305
|
+
# color: blue
|
304
306
|
#
|
305
307
|
# becomes
|
306
308
|
#
|
307
|
-
#
|
308
|
-
#
|
309
|
-
#
|
309
|
+
# foo
|
310
|
+
# bar
|
311
|
+
# color: blue
|
310
312
|
#
|
313
|
+
# @param root [Tree::Node] The parent node
|
311
314
|
def remove_parent_refs(root)
|
312
315
|
root.children.each do |child|
|
313
316
|
if child.is_a?(Tree::RuleNode)
|
314
|
-
child.
|
317
|
+
child.rules.first.gsub! /^& +/, ''
|
315
318
|
remove_parent_refs child
|
316
319
|
end
|
317
320
|
end
|
@@ -319,39 +322,43 @@ module Sass
|
|
319
322
|
|
320
323
|
# Flatten rules so that
|
321
324
|
#
|
322
|
-
#
|
323
|
-
#
|
324
|
-
# baz
|
325
|
+
# foo
|
326
|
+
# bar
|
325
327
|
# color: red
|
326
328
|
#
|
327
329
|
# becomes
|
328
330
|
#
|
329
|
-
#
|
330
|
-
#
|
331
|
+
# foo bar
|
332
|
+
# color: red
|
331
333
|
#
|
332
334
|
# and
|
333
335
|
#
|
334
|
-
#
|
335
|
-
#
|
336
|
-
#
|
336
|
+
# foo
|
337
|
+
# &.bar
|
338
|
+
# color: blue
|
337
339
|
#
|
338
340
|
# becomes
|
339
341
|
#
|
340
|
-
#
|
341
|
-
#
|
342
|
+
# foo.bar
|
343
|
+
# color: blue
|
342
344
|
#
|
345
|
+
# @param root [Tree::Node] The parent node
|
343
346
|
def flatten_rules(root)
|
344
347
|
root.children.each { |child| flatten_rule(child) if child.is_a?(Tree::RuleNode) }
|
345
348
|
end
|
346
349
|
|
350
|
+
# Flattens a single rule
|
351
|
+
#
|
352
|
+
# @param rule [Tree::RuleNode] The candidate for flattening
|
353
|
+
# @see #flatten_rules
|
347
354
|
def flatten_rule(rule)
|
348
355
|
while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
|
349
356
|
child = rule.children.first
|
350
357
|
|
351
|
-
if child.
|
352
|
-
rule.
|
358
|
+
if child.rules.first[0] == ?&
|
359
|
+
rule.rules = [child.rules.first.gsub(/^&/, rule.rules.first)]
|
353
360
|
else
|
354
|
-
rule.
|
361
|
+
rule.rules = ["#{rule.rules.first} #{child.rules.first}"]
|
355
362
|
end
|
356
363
|
|
357
364
|
rule.children = child.children
|
@@ -362,25 +369,26 @@ module Sass
|
|
362
369
|
|
363
370
|
# Transform
|
364
371
|
#
|
365
|
-
#
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
372
|
+
# foo
|
373
|
+
# bar
|
374
|
+
# color: blue
|
375
|
+
# baz
|
376
|
+
# color: blue
|
370
377
|
#
|
371
378
|
# into
|
372
379
|
#
|
373
|
-
#
|
374
|
-
#
|
375
|
-
#
|
380
|
+
# foo
|
381
|
+
# bar, baz
|
382
|
+
# color: blue
|
376
383
|
#
|
384
|
+
# @param rule [Tree::RuleNode] The candidate for flattening
|
377
385
|
def fold_commas(root)
|
378
386
|
prev_rule = nil
|
379
387
|
root.children.map! do |child|
|
380
388
|
next child unless child.is_a?(Tree::RuleNode)
|
381
389
|
|
382
390
|
if prev_rule && prev_rule.children == child.children
|
383
|
-
prev_rule.
|
391
|
+
prev_rule.rules.first << ", #{child.rules.first}"
|
384
392
|
next nil
|
385
393
|
end
|
386
394
|
|