syntax_tree 0.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/CHANGELOG.md +98 -1
- data/Gemfile.lock +5 -4
- data/README.md +8 -1
- data/exe/stree +4 -80
- data/lib/syntax_tree/cli.rb +244 -0
- data/lib/syntax_tree/prettyprint.rb +23 -11
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree.rb +1405 -590
- metadata +4 -3
data/lib/syntax_tree.rb
CHANGED
@@ -7,7 +7,7 @@ require "stringio"
|
|
7
7
|
|
8
8
|
require_relative "syntax_tree/version"
|
9
9
|
|
10
|
-
# If PrettyPrint::
|
10
|
+
# If PrettyPrint::Align isn't defined, then we haven't gotten the updated
|
11
11
|
# version of prettyprint. In that case we'll define our own. This is going to
|
12
12
|
# overwrite a bunch of methods, so silencing them as well.
|
13
13
|
unless PrettyPrint.const_defined?(:Align)
|
@@ -26,12 +26,14 @@ class SyntaxTree < Ripper
|
|
26
26
|
# every character in the string is 1 byte in length, so we can just return the
|
27
27
|
# start of the line + the index.
|
28
28
|
class SingleByteString
|
29
|
+
attr_reader :start
|
30
|
+
|
29
31
|
def initialize(start)
|
30
32
|
@start = start
|
31
33
|
end
|
32
34
|
|
33
35
|
def [](byteindex)
|
34
|
-
|
36
|
+
start + byteindex
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -40,7 +42,10 @@ class SyntaxTree < Ripper
|
|
40
42
|
# an array of indices, such that array[byteindex] will be equal to the index
|
41
43
|
# of the character within the string.
|
42
44
|
class MultiByteString
|
45
|
+
attr_reader :start, :indices
|
46
|
+
|
43
47
|
def initialize(start, line)
|
48
|
+
@start = start
|
44
49
|
@indices = []
|
45
50
|
|
46
51
|
line.each_char.with_index(start) do |char, index|
|
@@ -48,8 +53,11 @@ class SyntaxTree < Ripper
|
|
48
53
|
end
|
49
54
|
end
|
50
55
|
|
56
|
+
# Technically it's possible for the column index to be a negative value if
|
57
|
+
# there's a BOM at the beginning of the file, which is the reason we need to
|
58
|
+
# compare it to 0 here.
|
51
59
|
def [](byteindex)
|
52
|
-
|
60
|
+
indices[byteindex < 0 ? 0 : byteindex]
|
53
61
|
end
|
54
62
|
end
|
55
63
|
|
@@ -66,8 +74,7 @@ class SyntaxTree < Ripper
|
|
66
74
|
|
67
75
|
def ==(other)
|
68
76
|
other.is_a?(Location) && start_line == other.start_line &&
|
69
|
-
start_char == other.start_char &&
|
70
|
-
end_line == other.end_line &&
|
77
|
+
start_char == other.start_char && end_line == other.end_line &&
|
71
78
|
end_char == other.end_char
|
72
79
|
end
|
73
80
|
|
@@ -75,7 +82,7 @@ class SyntaxTree < Ripper
|
|
75
82
|
Location.new(
|
76
83
|
start_line: start_line,
|
77
84
|
start_char: start_char,
|
78
|
-
end_line: other.end_line,
|
85
|
+
end_line: [end_line, other.end_line].max,
|
79
86
|
end_char: other.end_char
|
80
87
|
)
|
81
88
|
end
|
@@ -113,16 +120,21 @@ class SyntaxTree < Ripper
|
|
113
120
|
# A slightly enhanced PP that knows how to format recursively including
|
114
121
|
# comments.
|
115
122
|
class Formatter < PP
|
116
|
-
|
123
|
+
COMMENT_PRIORITY = 1
|
124
|
+
HEREDOC_PRIORITY = 2
|
125
|
+
|
126
|
+
attr_reader :source, :stack, :quote
|
117
127
|
|
118
|
-
def initialize(
|
119
|
-
super
|
128
|
+
def initialize(source, ...)
|
129
|
+
super(...)
|
130
|
+
|
131
|
+
@source = source
|
120
132
|
@stack = []
|
121
133
|
@quote = "\""
|
122
134
|
end
|
123
135
|
|
124
|
-
def format(node)
|
125
|
-
stack << node
|
136
|
+
def format(node, stackable: true)
|
137
|
+
stack << node if stackable
|
126
138
|
doc = nil
|
127
139
|
|
128
140
|
# If there are comments, then we're going to format them around the node
|
@@ -136,11 +148,17 @@ class SyntaxTree < Ripper
|
|
136
148
|
breakable(force: true)
|
137
149
|
end
|
138
150
|
|
139
|
-
|
151
|
+
# If the node has a stree-ignore comment right before it, then we're
|
152
|
+
# going to just print out the node as it was seen in the source.
|
153
|
+
if leading.last&.ignore?
|
154
|
+
doc = text(source[node.location.start_char...node.location.end_char])
|
155
|
+
else
|
156
|
+
doc = node.format(self)
|
157
|
+
end
|
140
158
|
|
141
159
|
# Print all comments that were found after the node.
|
142
160
|
trailing.each do |comment|
|
143
|
-
line_suffix do
|
161
|
+
line_suffix(priority: COMMENT_PRIORITY) do
|
144
162
|
text(" ")
|
145
163
|
comment.format(self)
|
146
164
|
break_parent
|
@@ -150,7 +168,7 @@ class SyntaxTree < Ripper
|
|
150
168
|
doc = node.format(self)
|
151
169
|
end
|
152
170
|
|
153
|
-
stack.pop
|
171
|
+
stack.pop if stackable
|
154
172
|
doc
|
155
173
|
end
|
156
174
|
|
@@ -173,6 +191,10 @@ class SyntaxTree < Ripper
|
|
173
191
|
# [Array[ String ]] the list of lines in the source
|
174
192
|
attr_reader :lines
|
175
193
|
|
194
|
+
# [Array[ SingleByteString | MultiByteString ]] the list of objects that
|
195
|
+
# represent the start of each line in character offsets
|
196
|
+
attr_reader :line_counts
|
197
|
+
|
176
198
|
# [Array[ untyped ]] a running list of tokens that have been found in the
|
177
199
|
# source. This list changes a lot as certain nodes will "consume" these tokens
|
178
200
|
# to determine their bounds.
|
@@ -248,6 +270,12 @@ class SyntaxTree < Ripper
|
|
248
270
|
|
249
271
|
last_index += line.size
|
250
272
|
end
|
273
|
+
|
274
|
+
# Make sure line counts is filled out with the first and last line at
|
275
|
+
# minimum so that it has something to compare against if the parser is in a
|
276
|
+
# lineno=2 state for an empty file.
|
277
|
+
@line_counts << SingleByteString.new(0) if @line_counts.empty?
|
278
|
+
@line_counts << SingleByteString.new(last_index)
|
251
279
|
end
|
252
280
|
|
253
281
|
def self.parse(source)
|
@@ -259,13 +287,26 @@ class SyntaxTree < Ripper
|
|
259
287
|
def self.format(source)
|
260
288
|
output = []
|
261
289
|
|
262
|
-
formatter = Formatter.new(output)
|
290
|
+
formatter = Formatter.new(source, output)
|
263
291
|
parse(source).format(formatter)
|
264
292
|
|
265
293
|
formatter.flush
|
266
294
|
output.join
|
267
295
|
end
|
268
296
|
|
297
|
+
# Returns the source from the given filepath taking into account any potential
|
298
|
+
# magic encoding comments.
|
299
|
+
def self.read(filepath)
|
300
|
+
encoding =
|
301
|
+
File.open(filepath, "r") do |file|
|
302
|
+
header = file.readline
|
303
|
+
header += file.readline if header.start_with?("#!")
|
304
|
+
Ripper.new(header).tap(&:parse).encoding
|
305
|
+
end
|
306
|
+
|
307
|
+
File.read(filepath, encoding: encoding)
|
308
|
+
end
|
309
|
+
|
269
310
|
private
|
270
311
|
|
271
312
|
# ----------------------------------------------------------------------------
|
@@ -280,7 +321,7 @@ class SyntaxTree < Ripper
|
|
280
321
|
# this line, then we add the number of columns into this line that we've gone
|
281
322
|
# through.
|
282
323
|
def char_pos
|
283
|
-
|
324
|
+
line_counts[lineno - 1][column]
|
284
325
|
end
|
285
326
|
|
286
327
|
# As we build up a list of tokens, we'll periodically need to go backwards and
|
@@ -294,7 +335,7 @@ class SyntaxTree < Ripper
|
|
294
335
|
# (which would happen to be the innermost keyword). Then the outer one would
|
295
336
|
# only be able to grab the first one. In this way all of the tokens act as
|
296
337
|
# their own stack.
|
297
|
-
def find_token(type, value = :any, consume: true)
|
338
|
+
def find_token(type, value = :any, consume: true, location: nil)
|
298
339
|
index =
|
299
340
|
tokens.rindex do |token|
|
300
341
|
token.is_a?(type) && (value == :any || (token.value == value))
|
@@ -307,8 +348,16 @@ class SyntaxTree < Ripper
|
|
307
348
|
# could also be caused by accidentally attempting to consume a token twice
|
308
349
|
# by two different parser event handlers.
|
309
350
|
unless index
|
310
|
-
|
311
|
-
|
351
|
+
token = value == :any ? type.name.split("::", 2).last : value
|
352
|
+
message = "Cannot find expected #{token}"
|
353
|
+
|
354
|
+
if location
|
355
|
+
lineno = location.start_line
|
356
|
+
column = location.start_char - line_counts[lineno - 1].start
|
357
|
+
raise ParseError.new(message, lineno, column)
|
358
|
+
else
|
359
|
+
raise ParseError.new(message, lineno, column)
|
360
|
+
end
|
312
361
|
end
|
313
362
|
|
314
363
|
tokens.delete_at(index)
|
@@ -476,7 +525,7 @@ class SyntaxTree < Ripper
|
|
476
525
|
q.text(value)
|
477
526
|
else
|
478
527
|
q.text(q.quote)
|
479
|
-
q.text(value[1])
|
528
|
+
q.text(value[1] == "\"" ? "\\\"" : value[1])
|
480
529
|
q.text(q.quote)
|
481
530
|
end
|
482
531
|
end
|
@@ -628,7 +677,9 @@ class SyntaxTree < Ripper
|
|
628
677
|
def format(q)
|
629
678
|
q.text("__END__")
|
630
679
|
q.breakable(force: true)
|
631
|
-
|
680
|
+
|
681
|
+
separator = -> { q.breakable(indent: false, force: true) }
|
682
|
+
q.seplist(value.split(/\r?\n/, -1), separator) { |line| q.text(line) }
|
632
683
|
end
|
633
684
|
|
634
685
|
def pretty_print(q)
|
@@ -654,7 +705,7 @@ class SyntaxTree < Ripper
|
|
654
705
|
def on___end__(value)
|
655
706
|
@__end__ =
|
656
707
|
EndContent.new(
|
657
|
-
value:
|
708
|
+
value: source[(char_pos + value.length)..-1],
|
658
709
|
location: Location.token(line: lineno, char: char_pos, size: value.size)
|
659
710
|
)
|
660
711
|
end
|
@@ -725,11 +776,11 @@ class SyntaxTree < Ripper
|
|
725
776
|
|
726
777
|
q.group do
|
727
778
|
q.text(keyword)
|
728
|
-
q.format(left_argument)
|
779
|
+
q.format(left_argument, stackable: false)
|
729
780
|
q.group do
|
730
781
|
q.nest(keyword.length) do
|
731
782
|
q.breakable(force: left_argument.comments.any?)
|
732
|
-
q.format(AliasArgumentFormatter.new(right))
|
783
|
+
q.format(AliasArgumentFormatter.new(right), stackable: false)
|
733
784
|
end
|
734
785
|
end
|
735
786
|
end
|
@@ -1122,7 +1173,7 @@ class SyntaxTree < Ripper
|
|
1122
1173
|
# method(&expression)
|
1123
1174
|
#
|
1124
1175
|
class ArgBlock
|
1125
|
-
# [untyped] the expression being turned into a block
|
1176
|
+
# [nil | untyped] the expression being turned into a block
|
1126
1177
|
attr_reader :value
|
1127
1178
|
|
1128
1179
|
# [Location] the location of this node
|
@@ -1143,15 +1194,17 @@ class SyntaxTree < Ripper
|
|
1143
1194
|
|
1144
1195
|
def format(q)
|
1145
1196
|
q.text("&")
|
1146
|
-
q.format(value)
|
1197
|
+
q.format(value) if value
|
1147
1198
|
end
|
1148
1199
|
|
1149
1200
|
def pretty_print(q)
|
1150
1201
|
q.group(2, "(", ")") do
|
1151
1202
|
q.text("arg_block")
|
1152
1203
|
|
1153
|
-
|
1154
|
-
|
1204
|
+
if value
|
1205
|
+
q.breakable
|
1206
|
+
q.pp(value)
|
1207
|
+
end
|
1155
1208
|
|
1156
1209
|
q.pp(Comment::List.new(comments))
|
1157
1210
|
end
|
@@ -1170,17 +1223,34 @@ class SyntaxTree < Ripper
|
|
1170
1223
|
# (false | untyped) block
|
1171
1224
|
# ) -> Args
|
1172
1225
|
def on_args_add_block(arguments, block)
|
1173
|
-
|
1226
|
+
operator = find_token(Op, "&", consume: false)
|
1174
1227
|
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1228
|
+
# If we can't find the & operator, then there's no block to add to the list,
|
1229
|
+
# so we're just going to return the arguments as-is.
|
1230
|
+
return arguments unless operator
|
1231
|
+
|
1232
|
+
# Now we know we have an & operator, so we're going to delete it from the
|
1233
|
+
# list of tokens to make sure it doesn't get confused with anything else.
|
1234
|
+
tokens.delete(operator)
|
1235
|
+
|
1236
|
+
# Construct the location that represents the block argument.
|
1237
|
+
location = operator.location
|
1238
|
+
location = operator.location.to(block.location) if block
|
1239
|
+
|
1240
|
+
# If there are any arguments and the operator we found from the list is not
|
1241
|
+
# after them, then we're going to return the arguments as-is because we're
|
1242
|
+
# looking at an & that occurs before the arguments are done.
|
1243
|
+
if arguments.parts.any? && location.start_char < arguments.location.end_char
|
1244
|
+
return arguments
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
# Otherwise, we're looking at an actual block argument (with or without a
|
1248
|
+
# block, which could be missing because it could be a bare & since 3.1.0).
|
1249
|
+
arg_block = ArgBlock.new(value: block, location: location)
|
1180
1250
|
|
1181
1251
|
Args.new(
|
1182
1252
|
parts: arguments.parts << arg_block,
|
1183
|
-
location: arguments.location.to(
|
1253
|
+
location: arguments.location.to(location)
|
1184
1254
|
)
|
1185
1255
|
end
|
1186
1256
|
|
@@ -1349,7 +1419,11 @@ class SyntaxTree < Ripper
|
|
1349
1419
|
q.indent do
|
1350
1420
|
q.breakable("")
|
1351
1421
|
q.seplist(contents.parts, -> { q.breakable }) do |part|
|
1352
|
-
|
1422
|
+
if part.is_a?(StringLiteral)
|
1423
|
+
q.format(part.parts.first)
|
1424
|
+
else
|
1425
|
+
q.text(part.value[1..-1])
|
1426
|
+
end
|
1353
1427
|
end
|
1354
1428
|
end
|
1355
1429
|
q.breakable("")
|
@@ -1373,10 +1447,39 @@ class SyntaxTree < Ripper
|
|
1373
1447
|
q.format(part.value)
|
1374
1448
|
end
|
1375
1449
|
end
|
1450
|
+
q.breakable("")
|
1451
|
+
end
|
1452
|
+
end
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
class VarRefsFormatter
|
1456
|
+
# [Args] the contents of the array
|
1457
|
+
attr_reader :contents
|
1458
|
+
|
1459
|
+
def initialize(contents)
|
1460
|
+
@contents = contents
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
def format(q)
|
1464
|
+
q.group(0, "[", "]") do
|
1465
|
+
q.indent do
|
1466
|
+
q.breakable("")
|
1467
|
+
|
1468
|
+
separator = -> do
|
1469
|
+
q.text(",")
|
1470
|
+
q.fill_breakable
|
1471
|
+
end
|
1472
|
+
|
1473
|
+
q.seplist(contents.parts, separator) { |part| q.format(part) }
|
1474
|
+
end
|
1475
|
+
q.breakable("")
|
1376
1476
|
end
|
1377
1477
|
end
|
1378
1478
|
end
|
1379
1479
|
|
1480
|
+
# [LBracket] the bracket that opens this array
|
1481
|
+
attr_reader :lbracket
|
1482
|
+
|
1380
1483
|
# [nil | Args] the contents of the array
|
1381
1484
|
attr_reader :contents
|
1382
1485
|
|
@@ -1386,22 +1489,18 @@ class SyntaxTree < Ripper
|
|
1386
1489
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
1387
1490
|
attr_reader :comments
|
1388
1491
|
|
1389
|
-
def initialize(contents:, location:, comments: [])
|
1492
|
+
def initialize(lbracket:, contents:, location:, comments: [])
|
1493
|
+
@lbracket = lbracket
|
1390
1494
|
@contents = contents
|
1391
1495
|
@location = location
|
1392
1496
|
@comments = comments
|
1393
1497
|
end
|
1394
1498
|
|
1395
1499
|
def child_nodes
|
1396
|
-
[contents]
|
1500
|
+
[lbracket, contents]
|
1397
1501
|
end
|
1398
1502
|
|
1399
1503
|
def format(q)
|
1400
|
-
unless contents
|
1401
|
-
q.text("[]")
|
1402
|
-
return
|
1403
|
-
end
|
1404
|
-
|
1405
1504
|
if qwords?
|
1406
1505
|
QWordsFormatter.new(contents).format(q)
|
1407
1506
|
return
|
@@ -1412,12 +1511,23 @@ class SyntaxTree < Ripper
|
|
1412
1511
|
return
|
1413
1512
|
end
|
1414
1513
|
|
1415
|
-
q
|
1416
|
-
q
|
1417
|
-
|
1418
|
-
|
1514
|
+
if var_refs?(q)
|
1515
|
+
VarRefsFormatter.new(contents).format(q)
|
1516
|
+
return
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
q.group do
|
1520
|
+
q.format(lbracket)
|
1521
|
+
|
1522
|
+
if contents
|
1523
|
+
q.indent do
|
1524
|
+
q.breakable("")
|
1525
|
+
q.format(contents)
|
1526
|
+
end
|
1419
1527
|
end
|
1528
|
+
|
1420
1529
|
q.breakable("")
|
1530
|
+
q.text("]")
|
1421
1531
|
end
|
1422
1532
|
end
|
1423
1533
|
|
@@ -1441,21 +1551,40 @@ class SyntaxTree < Ripper
|
|
1441
1551
|
private
|
1442
1552
|
|
1443
1553
|
def qwords?
|
1444
|
-
|
1554
|
+
lbracket.comments.empty? && contents && contents.comments.empty? &&
|
1555
|
+
contents.parts.length > 1 &&
|
1445
1556
|
contents.parts.all? do |part|
|
1446
|
-
|
1447
|
-
|
1448
|
-
part.
|
1449
|
-
|
1557
|
+
case part
|
1558
|
+
when StringLiteral
|
1559
|
+
part.comments.empty? && part.parts.length == 1 &&
|
1560
|
+
part.parts.first.is_a?(TStringContent) &&
|
1561
|
+
!part.parts.first.value.match?(/[\s\[\]\\]/)
|
1562
|
+
when CHAR
|
1563
|
+
!part.value.match?(/[\[\]\\]/)
|
1564
|
+
else
|
1565
|
+
false
|
1566
|
+
end
|
1450
1567
|
end
|
1451
1568
|
end
|
1452
1569
|
|
1453
1570
|
def qsymbols?
|
1454
|
-
|
1571
|
+
lbracket.comments.empty? && contents && contents.comments.empty? &&
|
1572
|
+
contents.parts.length > 1 &&
|
1455
1573
|
contents.parts.all? do |part|
|
1456
1574
|
part.is_a?(SymbolLiteral) && part.comments.empty?
|
1457
1575
|
end
|
1458
1576
|
end
|
1577
|
+
|
1578
|
+
def var_refs?(q)
|
1579
|
+
lbracket.comments.empty? && contents && contents.comments.empty? &&
|
1580
|
+
contents.parts.all? do |part|
|
1581
|
+
part.is_a?(VarRef) && part.comments.empty?
|
1582
|
+
end &&
|
1583
|
+
(
|
1584
|
+
contents.parts.sum { |part| part.value.value.length + 2 } >
|
1585
|
+
q.maxwidth * 2
|
1586
|
+
)
|
1587
|
+
end
|
1459
1588
|
end
|
1460
1589
|
|
1461
1590
|
# :call-seq:
|
@@ -1467,13 +1596,16 @@ class SyntaxTree < Ripper
|
|
1467
1596
|
rbracket = find_token(RBracket)
|
1468
1597
|
|
1469
1598
|
ArrayLiteral.new(
|
1599
|
+
lbracket: lbracket,
|
1470
1600
|
contents: contents,
|
1471
1601
|
location: lbracket.location.to(rbracket.location)
|
1472
1602
|
)
|
1473
1603
|
else
|
1474
|
-
tstring_end =
|
1604
|
+
tstring_end =
|
1605
|
+
find_token(TStringEnd, location: contents.beginning.location)
|
1475
1606
|
|
1476
1607
|
contents.class.new(
|
1608
|
+
beginning: contents.beginning,
|
1477
1609
|
elements: contents.elements,
|
1478
1610
|
location: contents.location.to(tstring_end.location)
|
1479
1611
|
)
|
@@ -1554,7 +1686,7 @@ class SyntaxTree < Ripper
|
|
1554
1686
|
end
|
1555
1687
|
|
1556
1688
|
def child_nodes
|
1557
|
-
[constant, *
|
1689
|
+
[constant, *requireds, rest, *posts]
|
1558
1690
|
end
|
1559
1691
|
|
1560
1692
|
def format(q)
|
@@ -1563,20 +1695,23 @@ class SyntaxTree < Ripper
|
|
1563
1695
|
parts += posts
|
1564
1696
|
|
1565
1697
|
if constant
|
1566
|
-
q.
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1698
|
+
q.group do
|
1699
|
+
q.format(constant)
|
1700
|
+
q.text("[")
|
1701
|
+
q.seplist(parts) { |part| q.format(part) }
|
1702
|
+
q.text("]")
|
1703
|
+
end
|
1704
|
+
|
1570
1705
|
return
|
1571
1706
|
end
|
1572
1707
|
|
1573
1708
|
parent = q.parent
|
1574
|
-
if parts.length == 1 || PATTERNS.
|
1709
|
+
if parts.length == 1 || PATTERNS.include?(parent.class)
|
1575
1710
|
q.text("[")
|
1576
1711
|
q.seplist(parts) { |part| q.format(part) }
|
1577
1712
|
q.text("]")
|
1578
1713
|
else
|
1579
|
-
q.seplist(parts) { |part| q.format(part) }
|
1714
|
+
q.group { q.seplist(parts) { |part| q.format(part) } }
|
1580
1715
|
end
|
1581
1716
|
end
|
1582
1717
|
|
@@ -1642,6 +1777,23 @@ class SyntaxTree < Ripper
|
|
1642
1777
|
)
|
1643
1778
|
end
|
1644
1779
|
|
1780
|
+
# Determins if the following value should be indented or not.
|
1781
|
+
module AssignFormatting
|
1782
|
+
def self.skip_indent?(value)
|
1783
|
+
(value.is_a?(Call) && skip_indent?(value.receiver)) ||
|
1784
|
+
[
|
1785
|
+
ArrayLiteral,
|
1786
|
+
HashLiteral,
|
1787
|
+
Heredoc,
|
1788
|
+
Lambda,
|
1789
|
+
QSymbols,
|
1790
|
+
QWords,
|
1791
|
+
Symbols,
|
1792
|
+
Words
|
1793
|
+
].include?(value.class)
|
1794
|
+
end
|
1795
|
+
end
|
1796
|
+
|
1645
1797
|
# Assign represents assigning something to a variable or constant. Generally,
|
1646
1798
|
# the left side of the assignment is going to be any node that ends with the
|
1647
1799
|
# name "Field".
|
@@ -1717,10 +1869,8 @@ class SyntaxTree < Ripper
|
|
1717
1869
|
private
|
1718
1870
|
|
1719
1871
|
def skip_indent?
|
1720
|
-
target.
|
1721
|
-
|
1722
|
-
value.is_a?(Heredoc) ||
|
1723
|
-
value.is_a?(Lambda)
|
1872
|
+
target.comments.empty? &&
|
1873
|
+
(target.is_a?(ARefField) || AssignFormatting.skip_indent?(value))
|
1724
1874
|
end
|
1725
1875
|
end
|
1726
1876
|
|
@@ -1768,15 +1918,11 @@ class SyntaxTree < Ripper
|
|
1768
1918
|
end
|
1769
1919
|
|
1770
1920
|
def format(q)
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
q.format(value)
|
1776
|
-
end
|
1921
|
+
if value&.is_a?(HashLiteral)
|
1922
|
+
format_contents(q)
|
1923
|
+
else
|
1924
|
+
q.group { format_contents(q) }
|
1777
1925
|
end
|
1778
|
-
|
1779
|
-
value.is_a?(HashLiteral) ? contents.call : q.group(&contents)
|
1780
1926
|
end
|
1781
1927
|
|
1782
1928
|
def pretty_print(q)
|
@@ -1786,8 +1932,10 @@ class SyntaxTree < Ripper
|
|
1786
1932
|
q.breakable
|
1787
1933
|
q.pp(key)
|
1788
1934
|
|
1789
|
-
|
1790
|
-
|
1935
|
+
if value
|
1936
|
+
q.breakable
|
1937
|
+
q.pp(value)
|
1938
|
+
end
|
1791
1939
|
|
1792
1940
|
q.pp(Comment::List.new(comments))
|
1793
1941
|
end
|
@@ -1802,12 +1950,32 @@ class SyntaxTree < Ripper
|
|
1802
1950
|
cmts: comments
|
1803
1951
|
}.to_json(*opts)
|
1804
1952
|
end
|
1953
|
+
|
1954
|
+
private
|
1955
|
+
|
1956
|
+
def format_contents(q)
|
1957
|
+
q.parent.format_key(q, key)
|
1958
|
+
return unless value
|
1959
|
+
|
1960
|
+
if key.comments.empty? && AssignFormatting.skip_indent?(value)
|
1961
|
+
q.text(" ")
|
1962
|
+
q.format(value)
|
1963
|
+
else
|
1964
|
+
q.indent do
|
1965
|
+
q.breakable
|
1966
|
+
q.format(value)
|
1967
|
+
end
|
1968
|
+
end
|
1969
|
+
end
|
1805
1970
|
end
|
1806
1971
|
|
1807
1972
|
# :call-seq:
|
1808
1973
|
# on_assoc_new: (untyped key, untyped value) -> Assoc
|
1809
1974
|
def on_assoc_new(key, value)
|
1810
|
-
|
1975
|
+
location = key.location
|
1976
|
+
location = location.to(value.location) if value
|
1977
|
+
|
1978
|
+
Assoc.new(key: key, value: value, location: location)
|
1811
1979
|
end
|
1812
1980
|
|
1813
1981
|
# AssocSplat represents double-splatting a value into a hash (either a hash
|
@@ -1990,25 +2158,8 @@ class SyntaxTree < Ripper
|
|
1990
2158
|
# This module is responsible for formatting the assocs contained within a
|
1991
2159
|
# hash or bare hash. It first determines if every key in the hash can use
|
1992
2160
|
# labels. If it can, it uses labels. Otherwise it uses hash rockets.
|
1993
|
-
module
|
1994
|
-
class
|
1995
|
-
# [HashLiteral | BareAssocHash] the source of the assocs
|
1996
|
-
attr_reader :container
|
1997
|
-
|
1998
|
-
def initialize(container)
|
1999
|
-
@container = container
|
2000
|
-
end
|
2001
|
-
|
2002
|
-
def comments
|
2003
|
-
container.comments
|
2004
|
-
end
|
2005
|
-
|
2006
|
-
def format(q)
|
2007
|
-
q.seplist(container.assocs) { |assoc| q.format(assoc) }
|
2008
|
-
end
|
2009
|
-
end
|
2010
|
-
|
2011
|
-
class Labels < Base
|
2161
|
+
module HashKeyFormatter
|
2162
|
+
class Labels
|
2012
2163
|
def format_key(q, key)
|
2013
2164
|
case key
|
2014
2165
|
when Label
|
@@ -2023,7 +2174,7 @@ class SyntaxTree < Ripper
|
|
2023
2174
|
end
|
2024
2175
|
end
|
2025
2176
|
|
2026
|
-
class Rockets
|
2177
|
+
class Rockets
|
2027
2178
|
def format_key(q, key)
|
2028
2179
|
case key
|
2029
2180
|
when Label
|
@@ -2063,7 +2214,7 @@ class SyntaxTree < Ripper
|
|
2063
2214
|
end
|
2064
2215
|
end
|
2065
2216
|
|
2066
|
-
(labels ? Labels : Rockets).new
|
2217
|
+
(labels ? Labels : Rockets).new
|
2067
2218
|
end
|
2068
2219
|
end
|
2069
2220
|
|
@@ -2094,7 +2245,11 @@ class SyntaxTree < Ripper
|
|
2094
2245
|
end
|
2095
2246
|
|
2096
2247
|
def format(q)
|
2097
|
-
q.
|
2248
|
+
q.seplist(assocs) { |assoc| q.format(assoc) }
|
2249
|
+
end
|
2250
|
+
|
2251
|
+
def format_key(q, key)
|
2252
|
+
(@key_formatter ||= HashKeyFormatter.for(self)).format_key(q, key)
|
2098
2253
|
end
|
2099
2254
|
|
2100
2255
|
def pretty_print(q)
|
@@ -2188,24 +2343,95 @@ class SyntaxTree < Ripper
|
|
2188
2343
|
end
|
2189
2344
|
end
|
2190
2345
|
|
2346
|
+
# PinnedBegin represents a pinning a nested statement within pattern matching.
|
2347
|
+
#
|
2348
|
+
# case value
|
2349
|
+
# in ^(statement)
|
2350
|
+
# end
|
2351
|
+
#
|
2352
|
+
class PinnedBegin
|
2353
|
+
# [untyped] the expression being pinned
|
2354
|
+
attr_reader :statement
|
2355
|
+
|
2356
|
+
# [Location] the location of this node
|
2357
|
+
attr_reader :location
|
2358
|
+
|
2359
|
+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
2360
|
+
attr_reader :comments
|
2361
|
+
|
2362
|
+
def initialize(statement:, location:, comments: [])
|
2363
|
+
@statement = statement
|
2364
|
+
@location = location
|
2365
|
+
@comments = comments
|
2366
|
+
end
|
2367
|
+
|
2368
|
+
def child_nodes
|
2369
|
+
[statement]
|
2370
|
+
end
|
2371
|
+
|
2372
|
+
def format(q)
|
2373
|
+
q.group do
|
2374
|
+
q.text("^(")
|
2375
|
+
q.nest(1) do
|
2376
|
+
q.indent do
|
2377
|
+
q.breakable("")
|
2378
|
+
q.format(statement)
|
2379
|
+
end
|
2380
|
+
q.breakable("")
|
2381
|
+
q.text(")")
|
2382
|
+
end
|
2383
|
+
end
|
2384
|
+
end
|
2385
|
+
|
2386
|
+
def pretty_print(q)
|
2387
|
+
q.group(2, "(", ")") do
|
2388
|
+
q.text("pinned_begin")
|
2389
|
+
|
2390
|
+
q.breakable
|
2391
|
+
q.pp(statement)
|
2392
|
+
|
2393
|
+
q.pp(Comment::List.new(comments))
|
2394
|
+
end
|
2395
|
+
end
|
2396
|
+
|
2397
|
+
def to_json(*opts)
|
2398
|
+
{
|
2399
|
+
type: :pinned_begin,
|
2400
|
+
stmt: statement,
|
2401
|
+
loc: location,
|
2402
|
+
cmts: comments
|
2403
|
+
}.to_json(*opts)
|
2404
|
+
end
|
2405
|
+
end
|
2406
|
+
|
2191
2407
|
# :call-seq:
|
2192
|
-
# on_begin: (
|
2408
|
+
# on_begin: (untyped bodystmt) -> Begin | PinnedBegin
|
2193
2409
|
def on_begin(bodystmt)
|
2194
|
-
|
2195
|
-
end_char =
|
2196
|
-
if bodystmt.rescue_clause || bodystmt.ensure_clause ||
|
2197
|
-
bodystmt.else_clause
|
2198
|
-
bodystmt.location.end_char
|
2199
|
-
else
|
2200
|
-
find_token(Kw, "end").location.end_char
|
2201
|
-
end
|
2410
|
+
pin = find_token(Op, "^", consume: false)
|
2202
2411
|
|
2203
|
-
bodystmt.
|
2412
|
+
if pin && pin.location.start_char < bodystmt.location.start_char
|
2413
|
+
tokens.delete(pin)
|
2414
|
+
find_token(LParen)
|
2204
2415
|
|
2205
|
-
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2416
|
+
rparen = find_token(RParen)
|
2417
|
+
location = pin.location.to(rparen.location)
|
2418
|
+
|
2419
|
+
PinnedBegin.new(statement: bodystmt, location: location)
|
2420
|
+
else
|
2421
|
+
keyword = find_token(Kw, "begin")
|
2422
|
+
end_char =
|
2423
|
+
if bodystmt.rescue_clause || bodystmt.ensure_clause ||
|
2424
|
+
bodystmt.else_clause
|
2425
|
+
bodystmt.location.end_char
|
2426
|
+
else
|
2427
|
+
find_token(Kw, "end").location.end_char
|
2428
|
+
end
|
2429
|
+
|
2430
|
+
bodystmt.bind(keyword.location.end_char, end_char)
|
2431
|
+
location = keyword.location.to(bodystmt.location)
|
2432
|
+
|
2433
|
+
Begin.new(bodystmt: bodystmt, location: location)
|
2434
|
+
end
|
2209
2435
|
end
|
2210
2436
|
|
2211
2437
|
# Binary represents any expression that involves two sub-expressions with an
|
@@ -2252,11 +2478,14 @@ class SyntaxTree < Ripper
|
|
2252
2478
|
q.group do
|
2253
2479
|
q.group { q.format(left) }
|
2254
2480
|
q.text(" ") unless power
|
2255
|
-
q.text(operator)
|
2256
2481
|
|
2257
|
-
q.
|
2258
|
-
q.
|
2259
|
-
|
2482
|
+
q.group do
|
2483
|
+
q.text(operator)
|
2484
|
+
|
2485
|
+
q.indent do
|
2486
|
+
q.breakable(power ? "" : " ")
|
2487
|
+
q.format(right)
|
2488
|
+
end
|
2260
2489
|
end
|
2261
2490
|
end
|
2262
2491
|
end
|
@@ -2293,12 +2522,29 @@ class SyntaxTree < Ripper
|
|
2293
2522
|
# :call-seq:
|
2294
2523
|
# on_binary: (untyped left, (Op | Symbol) operator, untyped right) -> Binary
|
2295
2524
|
def on_binary(left, operator, right)
|
2296
|
-
|
2297
|
-
|
2298
|
-
|
2299
|
-
|
2300
|
-
|
2301
|
-
|
2525
|
+
if operator.is_a?(Symbol)
|
2526
|
+
# Here, we're going to search backward for the token that's between the
|
2527
|
+
# two operands that matches the operator so we can delete it from the
|
2528
|
+
# list.
|
2529
|
+
index =
|
2530
|
+
tokens.rindex do |token|
|
2531
|
+
location = token.location
|
2532
|
+
|
2533
|
+
token.is_a?(Op) &&
|
2534
|
+
token.value == operator.to_s &&
|
2535
|
+
location.start_char > left.location.end_char &&
|
2536
|
+
location.end_char < right.location.start_char
|
2537
|
+
end
|
2538
|
+
|
2539
|
+
tokens.delete_at(index) if index
|
2540
|
+
else
|
2541
|
+
# On most Ruby implementations, operator is a Symbol that represents that
|
2542
|
+
# operation being performed. For instance in the example `1 < 2`, the
|
2543
|
+
# `operator` object would be `:<`. However, on JRuby, it's an `@op` node,
|
2544
|
+
# so here we're going to explicitly convert it into the same normalized
|
2545
|
+
# form.
|
2546
|
+
operator = tokens.delete(operator).value
|
2547
|
+
end
|
2302
2548
|
|
2303
2549
|
Binary.new(
|
2304
2550
|
left: left,
|
@@ -2448,7 +2694,7 @@ class SyntaxTree < Ripper
|
|
2448
2694
|
# def method(&block); end
|
2449
2695
|
#
|
2450
2696
|
class BlockArg
|
2451
|
-
# [Ident] the name of the block argument
|
2697
|
+
# [nil | Ident] the name of the block argument
|
2452
2698
|
attr_reader :name
|
2453
2699
|
|
2454
2700
|
# [Location] the location of this node
|
@@ -2469,15 +2715,17 @@ class SyntaxTree < Ripper
|
|
2469
2715
|
|
2470
2716
|
def format(q)
|
2471
2717
|
q.text("&")
|
2472
|
-
q.format(name)
|
2718
|
+
q.format(name) if name
|
2473
2719
|
end
|
2474
2720
|
|
2475
2721
|
def pretty_print(q)
|
2476
2722
|
q.group(2, "(", ")") do
|
2477
2723
|
q.text("blockarg")
|
2478
2724
|
|
2479
|
-
|
2480
|
-
|
2725
|
+
if name
|
2726
|
+
q.breakable
|
2727
|
+
q.pp(name)
|
2728
|
+
end
|
2481
2729
|
|
2482
2730
|
q.pp(Comment::List.new(comments))
|
2483
2731
|
end
|
@@ -2495,7 +2743,10 @@ class SyntaxTree < Ripper
|
|
2495
2743
|
def on_blockarg(name)
|
2496
2744
|
operator = find_token(Op, "&")
|
2497
2745
|
|
2498
|
-
|
2746
|
+
location = operator.location
|
2747
|
+
location = location.to(name.location) if name
|
2748
|
+
|
2749
|
+
BlockArg.new(name: name, location: location)
|
2499
2750
|
end
|
2500
2751
|
|
2501
2752
|
# bodystmt can't actually determine its bounds appropriately because it
|
@@ -2685,59 +2936,137 @@ class SyntaxTree < Ripper
|
|
2685
2936
|
# [LBrace | Keyword] the node that opens the block
|
2686
2937
|
attr_reader :block_open
|
2687
2938
|
|
2939
|
+
# [String] the string that closes the block
|
2940
|
+
attr_reader :block_close
|
2941
|
+
|
2688
2942
|
# [BodyStmt | Statements] the statements inside the block
|
2689
2943
|
attr_reader :statements
|
2690
2944
|
|
2691
|
-
def initialize(node, block_open, statements)
|
2945
|
+
def initialize(node, block_open, block_close, statements)
|
2692
2946
|
@node = node
|
2693
2947
|
@block_open = block_open
|
2948
|
+
@block_close = block_close
|
2694
2949
|
@statements = statements
|
2695
2950
|
end
|
2696
2951
|
|
2697
2952
|
def format(q)
|
2698
|
-
|
2699
|
-
|
2953
|
+
# If this is nested anywhere inside of a Command or CommandCall node, then
|
2954
|
+
# we can't change which operators we're using for the bounds of the block.
|
2955
|
+
break_opening, break_closing, flat_opening, flat_closing =
|
2956
|
+
if unchangeable_bounds?(q)
|
2957
|
+
[block_open.value, block_close, block_open.value, block_close]
|
2958
|
+
elsif forced_do_end_bounds?(q)
|
2959
|
+
%w[do end do end]
|
2960
|
+
elsif forced_brace_bounds?(q)
|
2961
|
+
%w[{ } { }]
|
2962
|
+
else
|
2963
|
+
%w[do end { }]
|
2964
|
+
end
|
2700
2965
|
|
2701
|
-
|
2702
|
-
|
2966
|
+
# If the receiver of this block a Command or CommandCall node, then there
|
2967
|
+
# are no parentheses around the arguments to that command, so we need to
|
2968
|
+
# break the block.
|
2969
|
+
receiver = q.parent.call
|
2970
|
+
if receiver.is_a?(Command) || receiver.is_a?(CommandCall)
|
2971
|
+
q.break_parent
|
2972
|
+
format_break(q, break_opening, break_closing)
|
2973
|
+
return
|
2974
|
+
end
|
2703
2975
|
|
2704
|
-
|
2705
|
-
|
2706
|
-
|
2707
|
-
|
2976
|
+
q.group do
|
2977
|
+
q.if_break { format_break(q, break_opening, break_closing) }.if_flat do
|
2978
|
+
format_flat(q, flat_opening, flat_closing)
|
2979
|
+
end
|
2980
|
+
end
|
2981
|
+
end
|
2708
2982
|
|
2709
|
-
|
2710
|
-
q.indent do
|
2711
|
-
q.breakable
|
2712
|
-
q.format(statements)
|
2713
|
-
end
|
2714
|
-
end
|
2983
|
+
private
|
2715
2984
|
|
2716
|
-
|
2717
|
-
|
2718
|
-
|
2719
|
-
|
2985
|
+
# If this is nested anywhere inside certain nodes, then we can't change
|
2986
|
+
# which operators/keywords we're using for the bounds of the block.
|
2987
|
+
def unchangeable_bounds?(q)
|
2988
|
+
q.parents.any? do |parent|
|
2989
|
+
# If we hit a statements, then we're safe to use whatever since we
|
2990
|
+
# know for certain we're going to get split over multiple lines
|
2991
|
+
# anyway.
|
2992
|
+
break false if parent.is_a?(Statements)
|
2720
2993
|
|
2721
|
-
|
2722
|
-
|
2723
|
-
|
2724
|
-
q.breakable
|
2725
|
-
end
|
2994
|
+
[Command, CommandCall].include?(parent.class)
|
2995
|
+
end
|
2996
|
+
end
|
2726
2997
|
|
2727
|
-
|
2728
|
-
|
2729
|
-
|
2730
|
-
|
2731
|
-
|
2998
|
+
# If we're a sibling of a control-flow keyword, then we're going to have to
|
2999
|
+
# use the do..end bounds.
|
3000
|
+
def forced_do_end_bounds?(q)
|
3001
|
+
[Break, Next, Return, Super].include?(q.parent.call.class)
|
3002
|
+
end
|
2732
3003
|
|
2733
|
-
|
2734
|
-
|
3004
|
+
# If we're the predicate of a loop or conditional, then we're going to have
|
3005
|
+
# to go with the {..} bounds.
|
3006
|
+
def forced_brace_bounds?(q)
|
3007
|
+
parents = q.parents.to_a
|
3008
|
+
parents.each_with_index.any? do |parent, index|
|
3009
|
+
# If we hit certain breakpoints then we know we're safe.
|
3010
|
+
break false if [Paren, Statements].include?(parent.class)
|
3011
|
+
|
3012
|
+
[
|
3013
|
+
If,
|
3014
|
+
IfMod,
|
3015
|
+
IfOp,
|
3016
|
+
Unless,
|
3017
|
+
UnlessMod,
|
3018
|
+
While,
|
3019
|
+
WhileMod,
|
3020
|
+
Until,
|
3021
|
+
UntilMod
|
3022
|
+
].include?(parent.class) && parent.predicate == parents[index - 1]
|
2735
3023
|
end
|
2736
3024
|
end
|
2737
|
-
end
|
2738
3025
|
|
2739
|
-
|
2740
|
-
|
3026
|
+
def format_break(q, opening, closing)
|
3027
|
+
q.text(" ")
|
3028
|
+
q.format(BlockOpenFormatter.new(opening, block_open), stackable: false)
|
3029
|
+
|
3030
|
+
if node.block_var
|
3031
|
+
q.text(" ")
|
3032
|
+
q.format(node.block_var)
|
3033
|
+
end
|
3034
|
+
|
3035
|
+
unless statements.empty?
|
3036
|
+
q.indent do
|
3037
|
+
q.breakable
|
3038
|
+
q.format(statements)
|
3039
|
+
end
|
3040
|
+
end
|
3041
|
+
|
3042
|
+
q.breakable
|
3043
|
+
q.text(closing)
|
3044
|
+
end
|
3045
|
+
|
3046
|
+
def format_flat(q, opening, closing)
|
3047
|
+
q.text(" ")
|
3048
|
+
q.format(BlockOpenFormatter.new(opening, block_open), stackable: false)
|
3049
|
+
|
3050
|
+
if node.block_var
|
3051
|
+
q.breakable
|
3052
|
+
q.format(node.block_var)
|
3053
|
+
q.breakable
|
3054
|
+
end
|
3055
|
+
|
3056
|
+
if statements.empty?
|
3057
|
+
q.text(" ") if opening == "do"
|
3058
|
+
else
|
3059
|
+
q.breakable unless node.block_var
|
3060
|
+
q.format(statements)
|
3061
|
+
q.breakable
|
3062
|
+
end
|
3063
|
+
|
3064
|
+
q.text(closing)
|
3065
|
+
end
|
3066
|
+
end
|
3067
|
+
|
3068
|
+
# BraceBlock represents passing a block to a method call using the { }
|
3069
|
+
# operators.
|
2741
3070
|
#
|
2742
3071
|
# method { |variable| variable + 1 }
|
2743
3072
|
#
|
@@ -2770,7 +3099,7 @@ class SyntaxTree < Ripper
|
|
2770
3099
|
end
|
2771
3100
|
|
2772
3101
|
def format(q)
|
2773
|
-
BlockFormatter.new(self, lbrace, statements).format(q)
|
3102
|
+
BlockFormatter.new(self, lbrace, "}", statements).format(q)
|
2774
3103
|
end
|
2775
3104
|
|
2776
3105
|
def pretty_print(q)
|
@@ -2851,15 +3180,35 @@ class SyntaxTree < Ripper
|
|
2851
3180
|
q.text(keyword)
|
2852
3181
|
|
2853
3182
|
if arguments.parts.any?
|
2854
|
-
if arguments.parts.length == 1
|
2855
|
-
|
3183
|
+
if arguments.parts.length == 1
|
3184
|
+
part = arguments.parts.first
|
3185
|
+
|
3186
|
+
if part.is_a?(Paren)
|
3187
|
+
q.format(arguments)
|
3188
|
+
elsif part.is_a?(ArrayLiteral)
|
3189
|
+
q.text(" ")
|
3190
|
+
q.format(arguments)
|
3191
|
+
else
|
3192
|
+
format_arguments(q, "(", ")")
|
3193
|
+
end
|
2856
3194
|
else
|
2857
|
-
q
|
2858
|
-
q.nest(keyword.length + 1) { q.format(arguments) }
|
3195
|
+
format_arguments(q, " [", "]")
|
2859
3196
|
end
|
2860
3197
|
end
|
2861
3198
|
end
|
2862
3199
|
end
|
3200
|
+
|
3201
|
+
private
|
3202
|
+
|
3203
|
+
def format_arguments(q, opening, closing)
|
3204
|
+
q.if_break { q.text(opening) }
|
3205
|
+
q.indent do
|
3206
|
+
q.breakable(" ")
|
3207
|
+
q.format(node.arguments)
|
3208
|
+
end
|
3209
|
+
q.breakable("")
|
3210
|
+
q.if_break { q.text(closing) }
|
3211
|
+
end
|
2863
3212
|
end
|
2864
3213
|
|
2865
3214
|
# Break represents using the +break+ keyword.
|
@@ -2946,9 +3295,7 @@ class SyntaxTree < Ripper
|
|
2946
3295
|
end
|
2947
3296
|
end
|
2948
3297
|
|
2949
|
-
# Call represents a method call.
|
2950
|
-
# passed (if arguments are passed, this node will get nested under a
|
2951
|
-
# MethodAddArg node).
|
3298
|
+
# Call represents a method call.
|
2952
3299
|
#
|
2953
3300
|
# receiver.message
|
2954
3301
|
#
|
@@ -2962,32 +3309,64 @@ class SyntaxTree < Ripper
|
|
2962
3309
|
# [:call | Backtick | Const | Ident | Op] the message being sent
|
2963
3310
|
attr_reader :message
|
2964
3311
|
|
3312
|
+
# [nil | ArgParen | Args] the arguments to the method call
|
3313
|
+
attr_reader :arguments
|
3314
|
+
|
2965
3315
|
# [Location] the location of this node
|
2966
3316
|
attr_reader :location
|
2967
3317
|
|
2968
3318
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
2969
3319
|
attr_reader :comments
|
2970
3320
|
|
2971
|
-
def initialize(
|
3321
|
+
def initialize(
|
3322
|
+
receiver:,
|
3323
|
+
operator:,
|
3324
|
+
message:,
|
3325
|
+
arguments:,
|
3326
|
+
location:,
|
3327
|
+
comments: []
|
3328
|
+
)
|
2972
3329
|
@receiver = receiver
|
2973
3330
|
@operator = operator
|
2974
3331
|
@message = message
|
3332
|
+
@arguments = arguments
|
2975
3333
|
@location = location
|
2976
3334
|
@comments = comments
|
2977
3335
|
end
|
2978
3336
|
|
2979
3337
|
def child_nodes
|
2980
|
-
[
|
3338
|
+
[
|
3339
|
+
receiver,
|
3340
|
+
(operator if operator != :"::"),
|
3341
|
+
(message if message != :call),
|
3342
|
+
arguments
|
3343
|
+
]
|
2981
3344
|
end
|
2982
3345
|
|
2983
3346
|
def format(q)
|
3347
|
+
call_operator = CallOperatorFormatter.new(operator)
|
3348
|
+
|
2984
3349
|
q.group do
|
2985
3350
|
q.format(receiver)
|
3351
|
+
|
3352
|
+
# If there are trailing comments on the call operator, then we need to
|
3353
|
+
# use the trailing form as opposed to the leading form.
|
3354
|
+
q.format(call_operator) if call_operator.comments.any?
|
3355
|
+
|
2986
3356
|
q.group do
|
2987
3357
|
q.indent do
|
2988
|
-
|
3358
|
+
if receiver.comments.any? || call_operator.comments.any?
|
3359
|
+
q.breakable(force: true)
|
3360
|
+
end
|
3361
|
+
|
3362
|
+
if call_operator.comments.empty?
|
3363
|
+
q.format(call_operator, stackable: false)
|
3364
|
+
end
|
3365
|
+
|
2989
3366
|
q.format(message) if message != :call
|
2990
3367
|
end
|
3368
|
+
|
3369
|
+
q.format(arguments) if arguments
|
2991
3370
|
end
|
2992
3371
|
end
|
2993
3372
|
end
|
@@ -3005,6 +3384,11 @@ class SyntaxTree < Ripper
|
|
3005
3384
|
q.breakable
|
3006
3385
|
q.pp(message)
|
3007
3386
|
|
3387
|
+
if arguments
|
3388
|
+
q.breakable
|
3389
|
+
q.pp(arguments)
|
3390
|
+
end
|
3391
|
+
|
3008
3392
|
q.pp(Comment::List.new(comments))
|
3009
3393
|
end
|
3010
3394
|
end
|
@@ -3015,6 +3399,7 @@ class SyntaxTree < Ripper
|
|
3015
3399
|
receiver: receiver,
|
3016
3400
|
op: operator,
|
3017
3401
|
message: message,
|
3402
|
+
args: arguments,
|
3018
3403
|
loc: location,
|
3019
3404
|
cmts: comments
|
3020
3405
|
}.to_json(*opts)
|
@@ -3035,13 +3420,8 @@ class SyntaxTree < Ripper
|
|
3035
3420
|
receiver: receiver,
|
3036
3421
|
operator: operator,
|
3037
3422
|
message: message,
|
3038
|
-
|
3039
|
-
|
3040
|
-
start_line: receiver.location.start_line,
|
3041
|
-
start_char: receiver.location.start_char,
|
3042
|
-
end_line: [ending.location.end_line, receiver.location.end_line].max,
|
3043
|
-
end_char: ending.location.end_char
|
3044
|
-
)
|
3423
|
+
arguments: nil,
|
3424
|
+
location: receiver.location.to(ending.location)
|
3045
3425
|
)
|
3046
3426
|
end
|
3047
3427
|
|
@@ -3057,6 +3437,9 @@ class SyntaxTree < Ripper
|
|
3057
3437
|
# end
|
3058
3438
|
#
|
3059
3439
|
class Case
|
3440
|
+
# [Kw] the keyword that opens this expression
|
3441
|
+
attr_reader :keyword
|
3442
|
+
|
3060
3443
|
# [nil | untyped] optional value being switched on
|
3061
3444
|
attr_reader :value
|
3062
3445
|
|
@@ -3069,7 +3452,8 @@ class SyntaxTree < Ripper
|
|
3069
3452
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
3070
3453
|
attr_reader :comments
|
3071
3454
|
|
3072
|
-
def initialize(value:, consequent:, location:, comments: [])
|
3455
|
+
def initialize(keyword:, value:, consequent:, location:, comments: [])
|
3456
|
+
@keyword = keyword
|
3073
3457
|
@value = value
|
3074
3458
|
@consequent = consequent
|
3075
3459
|
@location = location
|
@@ -3077,11 +3461,13 @@ class SyntaxTree < Ripper
|
|
3077
3461
|
end
|
3078
3462
|
|
3079
3463
|
def child_nodes
|
3080
|
-
[value, consequent]
|
3464
|
+
[keyword, value, consequent]
|
3081
3465
|
end
|
3082
3466
|
|
3083
3467
|
def format(q)
|
3084
|
-
q.group
|
3468
|
+
q.group do
|
3469
|
+
q.format(keyword)
|
3470
|
+
|
3085
3471
|
if value
|
3086
3472
|
q.text(" ")
|
3087
3473
|
q.format(value)
|
@@ -3090,6 +3476,8 @@ class SyntaxTree < Ripper
|
|
3090
3476
|
q.breakable(force: true)
|
3091
3477
|
q.format(consequent)
|
3092
3478
|
q.breakable(force: true)
|
3479
|
+
|
3480
|
+
q.text("end")
|
3093
3481
|
end
|
3094
3482
|
end
|
3095
3483
|
|
@@ -3097,6 +3485,9 @@ class SyntaxTree < Ripper
|
|
3097
3485
|
q.group(2, "(", ")") do
|
3098
3486
|
q.text("case")
|
3099
3487
|
|
3488
|
+
q.breakable
|
3489
|
+
q.pp(keyword)
|
3490
|
+
|
3100
3491
|
if value
|
3101
3492
|
q.breakable
|
3102
3493
|
q.pp(value)
|
@@ -3204,6 +3595,7 @@ class SyntaxTree < Ripper
|
|
3204
3595
|
tokens.delete(keyword)
|
3205
3596
|
|
3206
3597
|
Case.new(
|
3598
|
+
keyword: keyword,
|
3207
3599
|
value: value,
|
3208
3600
|
consequent: consequent,
|
3209
3601
|
location: keyword.location.to(consequent.location)
|
@@ -3484,7 +3876,7 @@ class SyntaxTree < Ripper
|
|
3484
3876
|
# [Const | Ident | Op] the message being send
|
3485
3877
|
attr_reader :message
|
3486
3878
|
|
3487
|
-
# [Args] the arguments going along with the message
|
3879
|
+
# [nil | Args] the arguments going along with the message
|
3488
3880
|
attr_reader :arguments
|
3489
3881
|
|
3490
3882
|
# [Location] the location of this node
|
@@ -3515,16 +3907,16 @@ class SyntaxTree < Ripper
|
|
3515
3907
|
|
3516
3908
|
def format(q)
|
3517
3909
|
q.group do
|
3518
|
-
doc =
|
3519
|
-
|
3520
|
-
|
3521
|
-
|
3910
|
+
doc =
|
3911
|
+
q.nest(0) do
|
3912
|
+
q.format(receiver)
|
3913
|
+
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
3914
|
+
q.format(message)
|
3915
|
+
end
|
3522
3916
|
|
3523
|
-
|
3524
|
-
|
3525
|
-
q.
|
3526
|
-
else
|
3527
|
-
q.nest(width) { q.format(arguments) }
|
3917
|
+
if arguments
|
3918
|
+
q.text(" ")
|
3919
|
+
q.nest(argument_alignment(q, doc)) { q.format(arguments) }
|
3528
3920
|
end
|
3529
3921
|
end
|
3530
3922
|
end
|
@@ -3542,8 +3934,10 @@ class SyntaxTree < Ripper
|
|
3542
3934
|
q.breakable
|
3543
3935
|
q.pp(message)
|
3544
3936
|
|
3545
|
-
|
3546
|
-
|
3937
|
+
if arguments
|
3938
|
+
q.breakable
|
3939
|
+
q.pp(arguments)
|
3940
|
+
end
|
3547
3941
|
|
3548
3942
|
q.pp(Comment::List.new(comments))
|
3549
3943
|
end
|
@@ -3577,16 +3971,38 @@ class SyntaxTree < Ripper
|
|
3577
3971
|
when PrettyPrint::Text
|
3578
3972
|
width += doc.width
|
3579
3973
|
when PrettyPrint::Indent, PrettyPrint::Align, PrettyPrint::Group
|
3580
|
-
queue
|
3974
|
+
queue = doc.contents + queue
|
3581
3975
|
when PrettyPrint::IfBreak
|
3582
|
-
queue
|
3976
|
+
queue = doc.break_contents + queue
|
3583
3977
|
when PrettyPrint::Breakable
|
3584
|
-
width =
|
3978
|
+
width = 0
|
3585
3979
|
end
|
3586
3980
|
end
|
3587
3981
|
|
3588
3982
|
width
|
3589
3983
|
end
|
3984
|
+
|
3985
|
+
def argument_alignment(q, doc)
|
3986
|
+
# Very special handling case for rspec matchers. In general with rspec
|
3987
|
+
# matchers you expect to see something like:
|
3988
|
+
#
|
3989
|
+
# expect(foo).to receive(:bar).with(
|
3990
|
+
# 'one',
|
3991
|
+
# 'two',
|
3992
|
+
# 'three',
|
3993
|
+
# 'four',
|
3994
|
+
# 'five'
|
3995
|
+
# )
|
3996
|
+
#
|
3997
|
+
# In this case the arguments are aligned to the left side as opposed to
|
3998
|
+
# being aligned with the `receive` call.
|
3999
|
+
if %w[to not_to to_not].include?(message.value)
|
4000
|
+
0
|
4001
|
+
else
|
4002
|
+
width = doc_width(doc) + 1
|
4003
|
+
width > (q.maxwidth / 2) ? 0 : width
|
4004
|
+
end
|
4005
|
+
end
|
3590
4006
|
end
|
3591
4007
|
|
3592
4008
|
# :call-seq:
|
@@ -3594,7 +4010,7 @@ class SyntaxTree < Ripper
|
|
3594
4010
|
# untyped receiver,
|
3595
4011
|
# (:"::" | Op | Period) operator,
|
3596
4012
|
# (Const | Ident | Op) message,
|
3597
|
-
# Args arguments
|
4013
|
+
# (nil | Args) arguments
|
3598
4014
|
# ) -> CommandCall
|
3599
4015
|
def on_command_call(receiver, operator, message, arguments)
|
3600
4016
|
ending = arguments || message
|
@@ -3665,10 +4081,18 @@ class SyntaxTree < Ripper
|
|
3665
4081
|
@trailing
|
3666
4082
|
end
|
3667
4083
|
|
4084
|
+
def ignore?
|
4085
|
+
value[1..-1].strip == "stree-ignore"
|
4086
|
+
end
|
4087
|
+
|
3668
4088
|
def comments
|
3669
4089
|
[]
|
3670
4090
|
end
|
3671
4091
|
|
4092
|
+
def child_nodes
|
4093
|
+
[]
|
4094
|
+
end
|
4095
|
+
|
3672
4096
|
def format(q)
|
3673
4097
|
q.text(value)
|
3674
4098
|
end
|
@@ -4062,14 +4486,7 @@ class SyntaxTree < Ripper
|
|
4062
4486
|
q.group do
|
4063
4487
|
q.text("def ")
|
4064
4488
|
q.format(name)
|
4065
|
-
|
4066
|
-
if params.is_a?(Params) && !params.empty?
|
4067
|
-
q.text("(")
|
4068
|
-
q.format(params)
|
4069
|
-
q.text(")")
|
4070
|
-
else
|
4071
|
-
q.format(params)
|
4072
|
-
end
|
4489
|
+
q.format(params) if !params.is_a?(Params) || !params.empty?
|
4073
4490
|
end
|
4074
4491
|
|
4075
4492
|
unless bodystmt.empty?
|
@@ -4118,10 +4535,16 @@ class SyntaxTree < Ripper
|
|
4118
4535
|
# def method = result
|
4119
4536
|
#
|
4120
4537
|
class DefEndless
|
4538
|
+
# [untyped] the target where the method is being defined
|
4539
|
+
attr_reader :target
|
4540
|
+
|
4541
|
+
# [Op | Period] the operator being used to declare the method
|
4542
|
+
attr_reader :operator
|
4543
|
+
|
4121
4544
|
# [Backtick | Const | Ident | Kw | Op] the name of the method
|
4122
4545
|
attr_reader :name
|
4123
4546
|
|
4124
|
-
# [nil | Paren] the parameter declaration for the method
|
4547
|
+
# [nil | Params | Paren] the parameter declaration for the method
|
4125
4548
|
attr_reader :paren
|
4126
4549
|
|
4127
4550
|
# [untyped] the expression to be executed by the method
|
@@ -4133,7 +4556,17 @@ class SyntaxTree < Ripper
|
|
4133
4556
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
4134
4557
|
attr_reader :comments
|
4135
4558
|
|
4136
|
-
def initialize(
|
4559
|
+
def initialize(
|
4560
|
+
target:,
|
4561
|
+
operator:,
|
4562
|
+
name:,
|
4563
|
+
paren:,
|
4564
|
+
statement:,
|
4565
|
+
location:,
|
4566
|
+
comments: []
|
4567
|
+
)
|
4568
|
+
@target = target
|
4569
|
+
@operator = operator
|
4137
4570
|
@name = name
|
4138
4571
|
@paren = paren
|
4139
4572
|
@statement = statement
|
@@ -4142,14 +4575,26 @@ class SyntaxTree < Ripper
|
|
4142
4575
|
end
|
4143
4576
|
|
4144
4577
|
def child_nodes
|
4145
|
-
[name, paren, statement]
|
4578
|
+
[target, operator, name, paren, statement]
|
4146
4579
|
end
|
4147
4580
|
|
4148
4581
|
def format(q)
|
4149
4582
|
q.group do
|
4150
4583
|
q.text("def ")
|
4584
|
+
|
4585
|
+
if target
|
4586
|
+
q.format(target)
|
4587
|
+
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
4588
|
+
end
|
4589
|
+
|
4151
4590
|
q.format(name)
|
4152
|
-
|
4591
|
+
|
4592
|
+
if paren
|
4593
|
+
params = paren
|
4594
|
+
params = params.contents if params.is_a?(Paren)
|
4595
|
+
q.format(paren) unless params.empty?
|
4596
|
+
end
|
4597
|
+
|
4153
4598
|
q.text(" =")
|
4154
4599
|
q.group do
|
4155
4600
|
q.indent do
|
@@ -4164,6 +4609,14 @@ class SyntaxTree < Ripper
|
|
4164
4609
|
q.group(2, "(", ")") do
|
4165
4610
|
q.text("def_endless")
|
4166
4611
|
|
4612
|
+
if target
|
4613
|
+
q.breakable
|
4614
|
+
q.pp(target)
|
4615
|
+
|
4616
|
+
q.breakable
|
4617
|
+
q.pp(operator)
|
4618
|
+
end
|
4619
|
+
|
4167
4620
|
q.breakable
|
4168
4621
|
q.pp(name)
|
4169
4622
|
|
@@ -4206,19 +4659,6 @@ class SyntaxTree < Ripper
|
|
4206
4659
|
# and normal method definitions.
|
4207
4660
|
beginning = find_token(Kw, "def")
|
4208
4661
|
|
4209
|
-
# If we don't have a bodystmt node, then we have a single-line method
|
4210
|
-
unless bodystmt.is_a?(BodyStmt)
|
4211
|
-
node =
|
4212
|
-
DefEndless.new(
|
4213
|
-
name: name,
|
4214
|
-
paren: params,
|
4215
|
-
statement: bodystmt,
|
4216
|
-
location: beginning.location.to(bodystmt.location)
|
4217
|
-
)
|
4218
|
-
|
4219
|
-
return node
|
4220
|
-
end
|
4221
|
-
|
4222
4662
|
# If there aren't any params then we need to correct the params node
|
4223
4663
|
# location information
|
4224
4664
|
if params.is_a?(Params) && params.empty?
|
@@ -4234,18 +4674,35 @@ class SyntaxTree < Ripper
|
|
4234
4674
|
params = Params.new(location: location)
|
4235
4675
|
end
|
4236
4676
|
|
4237
|
-
ending = find_token(Kw, "end")
|
4238
|
-
bodystmt.bind(
|
4239
|
-
find_next_statement_start(params.location.end_char),
|
4240
|
-
ending.location.start_char
|
4241
|
-
)
|
4677
|
+
ending = find_token(Kw, "end", consume: false)
|
4242
4678
|
|
4243
|
-
|
4244
|
-
|
4245
|
-
|
4246
|
-
|
4247
|
-
|
4248
|
-
|
4679
|
+
if ending
|
4680
|
+
tokens.delete(ending)
|
4681
|
+
bodystmt.bind(
|
4682
|
+
find_next_statement_start(params.location.end_char),
|
4683
|
+
ending.location.start_char
|
4684
|
+
)
|
4685
|
+
|
4686
|
+
Def.new(
|
4687
|
+
name: name,
|
4688
|
+
params: params,
|
4689
|
+
bodystmt: bodystmt,
|
4690
|
+
location: beginning.location.to(ending.location)
|
4691
|
+
)
|
4692
|
+
else
|
4693
|
+
# In Ruby >= 3.1.0, this is a BodyStmt that wraps a single statement in
|
4694
|
+
# the statements list. Before, it was just the individual statement.
|
4695
|
+
statement = bodystmt.is_a?(BodyStmt) ? bodystmt.statements : bodystmt
|
4696
|
+
|
4697
|
+
DefEndless.new(
|
4698
|
+
target: nil,
|
4699
|
+
operator: nil,
|
4700
|
+
name: name,
|
4701
|
+
paren: params,
|
4702
|
+
statement: statement,
|
4703
|
+
location: beginning.location.to(bodystmt.location)
|
4704
|
+
)
|
4705
|
+
end
|
4249
4706
|
end
|
4250
4707
|
|
4251
4708
|
# Defined represents the use of the +defined?+ operator. It can be used with
|
@@ -4369,16 +4826,9 @@ class SyntaxTree < Ripper
|
|
4369
4826
|
q.group do
|
4370
4827
|
q.text("def ")
|
4371
4828
|
q.format(target)
|
4372
|
-
q.format(CallOperatorFormatter.new(operator))
|
4829
|
+
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
4373
4830
|
q.format(name)
|
4374
|
-
|
4375
|
-
if params.is_a?(Params) && !params.empty?
|
4376
|
-
q.text("(")
|
4377
|
-
q.format(params)
|
4378
|
-
q.text(")")
|
4379
|
-
else
|
4380
|
-
q.format(params)
|
4381
|
-
end
|
4831
|
+
q.format(params) if !params.is_a?(Params) || !params.empty?
|
4382
4832
|
end
|
4383
4833
|
|
4384
4834
|
unless bodystmt.empty?
|
@@ -4460,21 +4910,37 @@ class SyntaxTree < Ripper
|
|
4460
4910
|
end
|
4461
4911
|
|
4462
4912
|
beginning = find_token(Kw, "def")
|
4463
|
-
ending = find_token(Kw, "end")
|
4913
|
+
ending = find_token(Kw, "end", consume: false)
|
4464
4914
|
|
4465
|
-
|
4466
|
-
|
4467
|
-
|
4468
|
-
|
4915
|
+
if ending
|
4916
|
+
tokens.delete(ending)
|
4917
|
+
bodystmt.bind(
|
4918
|
+
find_next_statement_start(params.location.end_char),
|
4919
|
+
ending.location.start_char
|
4920
|
+
)
|
4469
4921
|
|
4470
|
-
|
4471
|
-
|
4472
|
-
|
4473
|
-
|
4474
|
-
|
4475
|
-
|
4476
|
-
|
4477
|
-
|
4922
|
+
Defs.new(
|
4923
|
+
target: target,
|
4924
|
+
operator: operator,
|
4925
|
+
name: name,
|
4926
|
+
params: params,
|
4927
|
+
bodystmt: bodystmt,
|
4928
|
+
location: beginning.location.to(ending.location)
|
4929
|
+
)
|
4930
|
+
else
|
4931
|
+
# In Ruby >= 3.1.0, this is a BodyStmt that wraps a single statement in
|
4932
|
+
# the statements list. Before, it was just the individual statement.
|
4933
|
+
statement = bodystmt.is_a?(BodyStmt) ? bodystmt.statements : bodystmt
|
4934
|
+
|
4935
|
+
DefEndless.new(
|
4936
|
+
target: target,
|
4937
|
+
operator: operator,
|
4938
|
+
name: name,
|
4939
|
+
paren: params,
|
4940
|
+
statement: statement,
|
4941
|
+
location: beginning.location.to(bodystmt.location)
|
4942
|
+
)
|
4943
|
+
end
|
4478
4944
|
end
|
4479
4945
|
|
4480
4946
|
# DoBlock represents passing a block to a method call using the +do+ and +end+
|
@@ -4512,7 +4978,7 @@ class SyntaxTree < Ripper
|
|
4512
4978
|
end
|
4513
4979
|
|
4514
4980
|
def format(q)
|
4515
|
-
BlockFormatter.new(self, keyword, bodystmt).format(q)
|
4981
|
+
BlockFormatter.new(self, keyword, "end", bodystmt).format(q)
|
4516
4982
|
end
|
4517
4983
|
|
4518
4984
|
def pretty_print(q)
|
@@ -4576,8 +5042,7 @@ class SyntaxTree < Ripper
|
|
4576
5042
|
end
|
4577
5043
|
|
4578
5044
|
def format(q)
|
4579
|
-
|
4580
|
-
space = parent.is_a?(If) || parent.is_a?(Unless)
|
5045
|
+
space = [If, IfMod, Unless, UnlessMod].include?(q.parent.class)
|
4581
5046
|
|
4582
5047
|
left = node.left
|
4583
5048
|
right = node.right
|
@@ -4890,9 +5355,9 @@ class SyntaxTree < Ripper
|
|
4890
5355
|
matching = Quotes.matching(quote[2])
|
4891
5356
|
pattern = /[\n#{Regexp.escape(matching)}'"]/
|
4892
5357
|
|
4893
|
-
if parts.any?
|
5358
|
+
if parts.any? { |part|
|
4894
5359
|
part.is_a?(TStringContent) && part.value.match?(pattern)
|
4895
|
-
|
5360
|
+
}
|
4896
5361
|
[quote, matching]
|
4897
5362
|
elsif Quotes.locked?(self)
|
4898
5363
|
["#{":" unless hash_key}'", "'"]
|
@@ -4917,7 +5382,7 @@ class SyntaxTree < Ripper
|
|
4917
5382
|
if find_token(SymBeg, consume: false)
|
4918
5383
|
# A normal dynamic symbol
|
4919
5384
|
symbeg = find_token(SymBeg)
|
4920
|
-
tstring_end = find_token(TStringEnd)
|
5385
|
+
tstring_end = find_token(TStringEnd, location: symbeg.location)
|
4921
5386
|
|
4922
5387
|
DynaSymbol.new(
|
4923
5388
|
quote: symbeg.value,
|
@@ -5009,6 +5474,7 @@ class SyntaxTree < Ripper
|
|
5009
5474
|
|
5010
5475
|
node = tokens[index]
|
5011
5476
|
ending = node.value == "end" ? tokens.delete_at(index) : node
|
5477
|
+
# ending = node
|
5012
5478
|
|
5013
5479
|
statements.bind(beginning.location.end_char, ending.location.start_char)
|
5014
5480
|
|
@@ -5155,6 +5621,10 @@ class SyntaxTree < Ripper
|
|
5155
5621
|
false
|
5156
5622
|
end
|
5157
5623
|
|
5624
|
+
def ignore?
|
5625
|
+
false
|
5626
|
+
end
|
5627
|
+
|
5158
5628
|
def comments
|
5159
5629
|
[]
|
5160
5630
|
end
|
@@ -5480,24 +5950,29 @@ class SyntaxTree < Ripper
|
|
5480
5950
|
# [Const | Ident] the name of the method
|
5481
5951
|
attr_reader :value
|
5482
5952
|
|
5953
|
+
# [nil | ArgParen | Args] the arguments to the method call
|
5954
|
+
attr_reader :arguments
|
5955
|
+
|
5483
5956
|
# [Location] the location of this node
|
5484
5957
|
attr_reader :location
|
5485
5958
|
|
5486
5959
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
5487
5960
|
attr_reader :comments
|
5488
5961
|
|
5489
|
-
def initialize(value:, location:, comments: [])
|
5962
|
+
def initialize(value:, arguments:, location:, comments: [])
|
5490
5963
|
@value = value
|
5964
|
+
@arguments = arguments
|
5491
5965
|
@location = location
|
5492
5966
|
@comments = comments
|
5493
5967
|
end
|
5494
5968
|
|
5495
5969
|
def child_nodes
|
5496
|
-
[value]
|
5970
|
+
[value, arguments]
|
5497
5971
|
end
|
5498
5972
|
|
5499
5973
|
def format(q)
|
5500
5974
|
q.format(value)
|
5975
|
+
q.format(arguments)
|
5501
5976
|
end
|
5502
5977
|
|
5503
5978
|
def pretty_print(q)
|
@@ -5507,21 +5982,30 @@ class SyntaxTree < Ripper
|
|
5507
5982
|
q.breakable
|
5508
5983
|
q.pp(value)
|
5509
5984
|
|
5985
|
+
if arguments
|
5986
|
+
q.breakable
|
5987
|
+
q.pp(arguments)
|
5988
|
+
end
|
5989
|
+
|
5510
5990
|
q.pp(Comment::List.new(comments))
|
5511
5991
|
end
|
5512
5992
|
end
|
5513
5993
|
|
5514
5994
|
def to_json(*opts)
|
5515
|
-
{
|
5516
|
-
|
5517
|
-
|
5995
|
+
{
|
5996
|
+
type: :fcall,
|
5997
|
+
value: value,
|
5998
|
+
args: arguments,
|
5999
|
+
loc: location,
|
6000
|
+
cmts: comments
|
6001
|
+
}.to_json(*opts)
|
5518
6002
|
end
|
5519
6003
|
end
|
5520
6004
|
|
5521
6005
|
# :call-seq:
|
5522
6006
|
# on_fcall: ((Const | Ident) value) -> FCall
|
5523
6007
|
def on_fcall(value)
|
5524
|
-
FCall.new(value: value, location: value.location)
|
6008
|
+
FCall.new(value: value, arguments: nil, location: value.location)
|
5525
6009
|
end
|
5526
6010
|
|
5527
6011
|
# Field is always the child of an assignment. It represents assigning to a
|
@@ -5560,7 +6044,7 @@ class SyntaxTree < Ripper
|
|
5560
6044
|
def format(q)
|
5561
6045
|
q.group do
|
5562
6046
|
q.format(parent)
|
5563
|
-
q.format(CallOperatorFormatter.new(operator))
|
6047
|
+
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
5564
6048
|
q.format(name)
|
5565
6049
|
end
|
5566
6050
|
end
|
@@ -5864,6 +6348,7 @@ class SyntaxTree < Ripper
|
|
5864
6348
|
# ) -> For
|
5865
6349
|
def on_for(index, collection, statements)
|
5866
6350
|
beginning = find_token(Kw, "for")
|
6351
|
+
in_keyword = find_token(Kw, "in")
|
5867
6352
|
ending = find_token(Kw, "end")
|
5868
6353
|
|
5869
6354
|
# Consume the do keyword if it exists so that it doesn't get confused for
|
@@ -5879,6 +6364,11 @@ class SyntaxTree < Ripper
|
|
5879
6364
|
ending.location.start_char
|
5880
6365
|
)
|
5881
6366
|
|
6367
|
+
if index.is_a?(MLHS)
|
6368
|
+
comma_range = index.location.end_char...in_keyword.location.start_char
|
6369
|
+
index.comma = true if source[comma_range].strip.start_with?(",")
|
6370
|
+
end
|
6371
|
+
|
5882
6372
|
For.new(
|
5883
6373
|
index: index,
|
5884
6374
|
collection: collection,
|
@@ -5947,6 +6437,9 @@ class SyntaxTree < Ripper
|
|
5947
6437
|
# { key => value }
|
5948
6438
|
#
|
5949
6439
|
class HashLiteral
|
6440
|
+
# [LBrace] the left brace that opens this hash
|
6441
|
+
attr_reader :lbrace
|
6442
|
+
|
5950
6443
|
# [Array[ AssocNew | AssocSplat ]] the optional contents of the hash
|
5951
6444
|
attr_reader :assocs
|
5952
6445
|
|
@@ -5956,28 +6449,27 @@ class SyntaxTree < Ripper
|
|
5956
6449
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
5957
6450
|
attr_reader :comments
|
5958
6451
|
|
5959
|
-
def initialize(assocs:, location:, comments: [])
|
6452
|
+
def initialize(lbrace:, assocs:, location:, comments: [])
|
6453
|
+
@lbrace = lbrace
|
5960
6454
|
@assocs = assocs
|
5961
6455
|
@location = location
|
5962
6456
|
@comments = comments
|
5963
6457
|
end
|
5964
6458
|
|
5965
6459
|
def child_nodes
|
5966
|
-
assocs
|
6460
|
+
[lbrace] + assocs
|
5967
6461
|
end
|
5968
6462
|
|
5969
6463
|
def format(q)
|
5970
|
-
|
5971
|
-
q
|
5972
|
-
|
5973
|
-
|
5974
|
-
q.format(HashFormatter.for(self))
|
5975
|
-
end
|
5976
|
-
q.breakable
|
5977
|
-
q.text("}")
|
6464
|
+
if q.parent.is_a?(Assoc)
|
6465
|
+
format_contents(q)
|
6466
|
+
else
|
6467
|
+
q.group { format_contents(q) }
|
5978
6468
|
end
|
6469
|
+
end
|
5979
6470
|
|
5980
|
-
|
6471
|
+
def format_key(q, key)
|
6472
|
+
(@key_formatter ||= HashKeyFormatter.for(self)).format_key(q, key)
|
5981
6473
|
end
|
5982
6474
|
|
5983
6475
|
def pretty_print(q)
|
@@ -5998,6 +6490,24 @@ class SyntaxTree < Ripper
|
|
5998
6490
|
*opts
|
5999
6491
|
)
|
6000
6492
|
end
|
6493
|
+
|
6494
|
+
private
|
6495
|
+
|
6496
|
+
def format_contents(q)
|
6497
|
+
q.format(lbrace)
|
6498
|
+
|
6499
|
+
if assocs.empty?
|
6500
|
+
q.breakable("")
|
6501
|
+
else
|
6502
|
+
q.indent do
|
6503
|
+
q.breakable
|
6504
|
+
q.seplist(assocs) { |assoc| q.format(assoc) }
|
6505
|
+
end
|
6506
|
+
q.breakable
|
6507
|
+
end
|
6508
|
+
|
6509
|
+
q.text("}")
|
6510
|
+
end
|
6001
6511
|
end
|
6002
6512
|
|
6003
6513
|
# :call-seq:
|
@@ -6007,6 +6517,7 @@ class SyntaxTree < Ripper
|
|
6007
6517
|
rbrace = find_token(RBrace)
|
6008
6518
|
|
6009
6519
|
HashLiteral.new(
|
6520
|
+
lbrace: lbrace,
|
6010
6521
|
assocs: assocs || [],
|
6011
6522
|
location: lbrace.location.to(rbrace.location)
|
6012
6523
|
)
|
@@ -6059,7 +6570,7 @@ class SyntaxTree < Ripper
|
|
6059
6570
|
q.group do
|
6060
6571
|
q.format(beginning)
|
6061
6572
|
|
6062
|
-
q.line_suffix do
|
6573
|
+
q.line_suffix(priority: Formatter::HEREDOC_PRIORITY) do
|
6063
6574
|
q.group do
|
6064
6575
|
breakable.call
|
6065
6576
|
|
@@ -6282,7 +6793,9 @@ class SyntaxTree < Ripper
|
|
6282
6793
|
def format(q)
|
6283
6794
|
parts = keywords.map { |(key, value)| KeywordFormatter.new(key, value) }
|
6284
6795
|
parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
|
6285
|
-
contents = ->
|
6796
|
+
contents = -> do
|
6797
|
+
q.seplist(parts) { |part| q.format(part, stackable: false) }
|
6798
|
+
end
|
6286
6799
|
|
6287
6800
|
if constant
|
6288
6801
|
q.format(constant)
|
@@ -6293,7 +6806,7 @@ class SyntaxTree < Ripper
|
|
6293
6806
|
end
|
6294
6807
|
|
6295
6808
|
parent = q.parent
|
6296
|
-
if PATTERNS.
|
6809
|
+
if PATTERNS.include?(parent.class)
|
6297
6810
|
q.text("{ ")
|
6298
6811
|
contents.call
|
6299
6812
|
q.text(" }")
|
@@ -6428,6 +6941,23 @@ class SyntaxTree < Ripper
|
|
6428
6941
|
)
|
6429
6942
|
end
|
6430
6943
|
|
6944
|
+
# If the predicate of a conditional or loop contains an assignment (in which
|
6945
|
+
# case we can't know for certain that that assignment doesn't impact the
|
6946
|
+
# statements inside the conditional) then we can't use the modifier form
|
6947
|
+
# and we must use the block form.
|
6948
|
+
module ContainsAssignment
|
6949
|
+
def self.call(parent)
|
6950
|
+
queue = [parent]
|
6951
|
+
|
6952
|
+
while node = queue.shift
|
6953
|
+
return true if [Assign, MAssign, OpAssign].include?(node.class)
|
6954
|
+
queue += node.child_nodes
|
6955
|
+
end
|
6956
|
+
|
6957
|
+
false
|
6958
|
+
end
|
6959
|
+
end
|
6960
|
+
|
6431
6961
|
# Formats an If or Unless node.
|
6432
6962
|
class ConditionalFormatter
|
6433
6963
|
# [String] the keyword associated with this conditional
|
@@ -6442,38 +6972,50 @@ class SyntaxTree < Ripper
|
|
6442
6972
|
end
|
6443
6973
|
|
6444
6974
|
def format(q)
|
6445
|
-
|
6446
|
-
|
6447
|
-
|
6448
|
-
|
6975
|
+
# If the predicate of the conditional contains an assignment (in which
|
6976
|
+
# case we can't know for certain that that assignment doesn't impact the
|
6977
|
+
# statements inside the conditional) then we can't use the modifier form
|
6978
|
+
# and we must use the block form.
|
6979
|
+
if ContainsAssignment.call(node.predicate)
|
6980
|
+
format_break(q, force: true)
|
6981
|
+
return
|
6982
|
+
end
|
6449
6983
|
|
6450
|
-
|
6451
|
-
|
6452
|
-
|
6453
|
-
|
6984
|
+
if node.consequent || node.statements.empty?
|
6985
|
+
q.group { format_break(q, force: true) }
|
6986
|
+
else
|
6987
|
+
q.group do
|
6988
|
+
q.if_break { format_break(q, force: false) }.if_flat do
|
6989
|
+
Parentheses.flat(q) do
|
6990
|
+
q.format(node.statements)
|
6991
|
+
q.text(" #{keyword} ")
|
6992
|
+
q.format(node.predicate)
|
6993
|
+
end
|
6454
6994
|
end
|
6455
6995
|
end
|
6996
|
+
end
|
6997
|
+
end
|
6998
|
+
|
6999
|
+
private
|
7000
|
+
|
7001
|
+
def format_break(q, force:)
|
7002
|
+
q.text("#{keyword} ")
|
7003
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
6456
7004
|
|
6457
|
-
|
7005
|
+
unless node.statements.empty?
|
7006
|
+
q.indent do
|
6458
7007
|
q.breakable(force: force)
|
6459
|
-
q.format(node.
|
7008
|
+
q.format(node.statements)
|
6460
7009
|
end
|
7010
|
+
end
|
6461
7011
|
|
7012
|
+
if node.consequent
|
6462
7013
|
q.breakable(force: force)
|
6463
|
-
q.
|
7014
|
+
q.format(node.consequent)
|
6464
7015
|
end
|
6465
7016
|
|
6466
|
-
|
6467
|
-
|
6468
|
-
else
|
6469
|
-
q.group do
|
6470
|
-
q.if_break { break_format.call(force: false) }.if_flat do
|
6471
|
-
q.format(node.statements)
|
6472
|
-
q.text(" #{keyword} ")
|
6473
|
-
q.format(node.predicate)
|
6474
|
-
end
|
6475
|
-
end
|
6476
|
-
end
|
7017
|
+
q.breakable(force: force)
|
7018
|
+
q.text("end")
|
6477
7019
|
end
|
6478
7020
|
end
|
6479
7021
|
|
@@ -6604,38 +7146,19 @@ class SyntaxTree < Ripper
|
|
6604
7146
|
end
|
6605
7147
|
|
6606
7148
|
def format(q)
|
6607
|
-
|
6608
|
-
|
6609
|
-
|
6610
|
-
|
6611
|
-
|
6612
|
-
|
6613
|
-
q.breakable
|
6614
|
-
q.format(truthy)
|
6615
|
-
end
|
6616
|
-
|
6617
|
-
q.breakable
|
6618
|
-
q.text("else")
|
6619
|
-
|
6620
|
-
q.indent do
|
6621
|
-
q.breakable
|
6622
|
-
q.format(falsy)
|
6623
|
-
end
|
6624
|
-
|
6625
|
-
q.breakable
|
6626
|
-
q.text("end")
|
6627
|
-
end.if_flat do
|
6628
|
-
q.format(predicate)
|
6629
|
-
q.text(" ?")
|
6630
|
-
|
6631
|
-
q.breakable
|
6632
|
-
q.format(truthy)
|
6633
|
-
q.text(" :")
|
7149
|
+
force_flat = [
|
7150
|
+
Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, IfOp,
|
7151
|
+
Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, Super,
|
7152
|
+
Undef, Unless, UnlessMod, UntilMod, VarAlias, VoidStmt, WhileMod, Yield,
|
7153
|
+
Yield0, ZSuper
|
7154
|
+
]
|
6634
7155
|
|
6635
|
-
|
6636
|
-
|
6637
|
-
|
7156
|
+
if force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
7157
|
+
q.group { format_flat(q) }
|
7158
|
+
return
|
6638
7159
|
end
|
7160
|
+
|
7161
|
+
q.group { q.if_break { format_break(q) }.if_flat { format_flat(q) } }
|
6639
7162
|
end
|
6640
7163
|
|
6641
7164
|
def pretty_print(q)
|
@@ -6665,6 +7188,43 @@ class SyntaxTree < Ripper
|
|
6665
7188
|
cmts: comments
|
6666
7189
|
}.to_json(*opts)
|
6667
7190
|
end
|
7191
|
+
|
7192
|
+
private
|
7193
|
+
|
7194
|
+
def format_break(q)
|
7195
|
+
Parentheses.break(q) do
|
7196
|
+
q.text("if ")
|
7197
|
+
q.nest("if ".length) { q.format(predicate) }
|
7198
|
+
|
7199
|
+
q.indent do
|
7200
|
+
q.breakable
|
7201
|
+
q.format(truthy)
|
7202
|
+
end
|
7203
|
+
|
7204
|
+
q.breakable
|
7205
|
+
q.text("else")
|
7206
|
+
|
7207
|
+
q.indent do
|
7208
|
+
q.breakable
|
7209
|
+
q.format(falsy)
|
7210
|
+
end
|
7211
|
+
|
7212
|
+
q.breakable
|
7213
|
+
q.text("end")
|
7214
|
+
end
|
7215
|
+
end
|
7216
|
+
|
7217
|
+
def format_flat(q)
|
7218
|
+
q.format(predicate)
|
7219
|
+
q.text(" ?")
|
7220
|
+
|
7221
|
+
q.breakable
|
7222
|
+
q.format(truthy)
|
7223
|
+
q.text(" :")
|
7224
|
+
|
7225
|
+
q.breakable
|
7226
|
+
q.format(falsy)
|
7227
|
+
end
|
6668
7228
|
end
|
6669
7229
|
|
6670
7230
|
# :call-seq:
|
@@ -6692,21 +7252,31 @@ class SyntaxTree < Ripper
|
|
6692
7252
|
end
|
6693
7253
|
|
6694
7254
|
def format(q)
|
6695
|
-
|
6696
|
-
q.
|
6697
|
-
|
6698
|
-
|
6699
|
-
|
6700
|
-
|
6701
|
-
|
6702
|
-
|
6703
|
-
|
6704
|
-
|
6705
|
-
|
6706
|
-
|
6707
|
-
|
6708
|
-
|
6709
|
-
|
7255
|
+
if ContainsAssignment.call(node.statement)
|
7256
|
+
q.group { format_flat(q) }
|
7257
|
+
else
|
7258
|
+
q.group { q.if_break { format_break(q) }.if_flat { format_flat(q) } }
|
7259
|
+
end
|
7260
|
+
end
|
7261
|
+
|
7262
|
+
private
|
7263
|
+
|
7264
|
+
def format_break(q)
|
7265
|
+
q.text("#{keyword} ")
|
7266
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
7267
|
+
q.indent do
|
7268
|
+
q.breakable
|
7269
|
+
q.format(node.statement)
|
7270
|
+
end
|
7271
|
+
q.breakable
|
7272
|
+
q.text("end")
|
7273
|
+
end
|
7274
|
+
|
7275
|
+
def format_flat(q)
|
7276
|
+
Parentheses.flat(q) do
|
7277
|
+
q.format(node.statement)
|
7278
|
+
q.text(" #{keyword} ")
|
7279
|
+
q.format(node.predicate)
|
6710
7280
|
end
|
6711
7281
|
end
|
6712
7282
|
end
|
@@ -6944,8 +7514,14 @@ class SyntaxTree < Ripper
|
|
6944
7514
|
beginning = find_token(Kw, "in")
|
6945
7515
|
ending = consequent || find_token(Kw, "end")
|
6946
7516
|
|
7517
|
+
statements_start = pattern
|
7518
|
+
if token = find_token(Kw, "then", consume: false)
|
7519
|
+
tokens.delete(token)
|
7520
|
+
statements_start = token
|
7521
|
+
end
|
7522
|
+
|
6947
7523
|
statements.bind(
|
6948
|
-
find_next_statement_start(
|
7524
|
+
find_next_statement_start(statements_start.location.end_char),
|
6949
7525
|
ending.location.start_char
|
6950
7526
|
)
|
6951
7527
|
|
@@ -6982,7 +7558,7 @@ class SyntaxTree < Ripper
|
|
6982
7558
|
end
|
6983
7559
|
|
6984
7560
|
def format(q)
|
6985
|
-
if !value.start_with?(
|
7561
|
+
if !value.start_with?(/\+?0/) && value.length >= 5 && !value.include?("_")
|
6986
7562
|
# If it's a plain integer and it doesn't have any underscores separating
|
6987
7563
|
# the values, then we're going to insert them every 3 characters
|
6988
7564
|
# starting from the right.
|
@@ -7330,9 +7906,11 @@ class SyntaxTree < Ripper
|
|
7330
7906
|
if params.is_a?(Paren)
|
7331
7907
|
q.format(params) unless params.contents.empty?
|
7332
7908
|
elsif !params.empty?
|
7333
|
-
q.
|
7334
|
-
|
7335
|
-
|
7909
|
+
q.group do
|
7910
|
+
q.text("(")
|
7911
|
+
q.format(params)
|
7912
|
+
q.text(")")
|
7913
|
+
end
|
7336
7914
|
end
|
7337
7915
|
|
7338
7916
|
q.text(" ")
|
@@ -7391,8 +7969,11 @@ class SyntaxTree < Ripper
|
|
7391
7969
|
def on_lambda(params, statements)
|
7392
7970
|
beginning = find_token(TLambda)
|
7393
7971
|
|
7394
|
-
if
|
7395
|
-
|
7972
|
+
if tokens.any? { |token|
|
7973
|
+
token.is_a?(TLamBeg) &&
|
7974
|
+
token.location.start_char > beginning.location.start_char
|
7975
|
+
}
|
7976
|
+
opening = find_token(TLamBeg)
|
7396
7977
|
closing = find_token(RBrace)
|
7397
7978
|
else
|
7398
7979
|
opening = find_token(Kw, "do")
|
@@ -7472,9 +8053,38 @@ class SyntaxTree < Ripper
|
|
7472
8053
|
# [Location] the location of this node
|
7473
8054
|
attr_reader :location
|
7474
8055
|
|
7475
|
-
|
8056
|
+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
8057
|
+
attr_reader :comments
|
8058
|
+
|
8059
|
+
def initialize(value:, location:, comments: [])
|
7476
8060
|
@value = value
|
7477
8061
|
@location = location
|
8062
|
+
@comments = comments
|
8063
|
+
end
|
8064
|
+
|
8065
|
+
def child_nodes
|
8066
|
+
[]
|
8067
|
+
end
|
8068
|
+
|
8069
|
+
def format(q)
|
8070
|
+
q.text(value)
|
8071
|
+
end
|
8072
|
+
|
8073
|
+
def pretty_print(q)
|
8074
|
+
q.group(2, "(", ")") do
|
8075
|
+
q.text("lbracket")
|
8076
|
+
|
8077
|
+
q.breakable
|
8078
|
+
q.pp(value)
|
8079
|
+
|
8080
|
+
q.pp(Comment::List.new(comments))
|
8081
|
+
end
|
8082
|
+
end
|
8083
|
+
|
8084
|
+
def to_json(*opts)
|
8085
|
+
{ type: :lbracket, value: value, loc: location, cmts: comments }.to_json(
|
8086
|
+
*opts
|
8087
|
+
)
|
7478
8088
|
end
|
7479
8089
|
end
|
7480
8090
|
|
@@ -7565,104 +8175,12 @@ class SyntaxTree < Ripper
|
|
7565
8175
|
#
|
7566
8176
|
# first, = value
|
7567
8177
|
#
|
7568
|
-
class MAssign
|
7569
|
-
# [MLHS | MLHSParen] the target of the multiple assignment
|
7570
|
-
attr_reader :target
|
7571
|
-
|
7572
|
-
# [untyped] the value being assigned
|
7573
|
-
attr_reader :value
|
7574
|
-
|
7575
|
-
# [Location] the location of this node
|
7576
|
-
attr_reader :location
|
7577
|
-
|
7578
|
-
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
7579
|
-
attr_reader :comments
|
7580
|
-
|
7581
|
-
def initialize(target:, value:, location:, comments: [])
|
7582
|
-
@target = target
|
7583
|
-
@value = value
|
7584
|
-
@location = location
|
7585
|
-
@comments = comments
|
7586
|
-
end
|
7587
|
-
|
7588
|
-
def child_nodes
|
7589
|
-
[target, value]
|
7590
|
-
end
|
7591
|
-
|
7592
|
-
def format(q)
|
7593
|
-
q.group do
|
7594
|
-
q.group do
|
7595
|
-
q.format(target)
|
7596
|
-
q.text(",") if target.is_a?(MLHS) && target.comma
|
7597
|
-
end
|
7598
|
-
|
7599
|
-
q.text(" =")
|
7600
|
-
q.indent do
|
7601
|
-
q.breakable
|
7602
|
-
q.format(value)
|
7603
|
-
end
|
7604
|
-
end
|
7605
|
-
end
|
7606
|
-
|
7607
|
-
def pretty_print(q)
|
7608
|
-
q.group(2, "(", ")") do
|
7609
|
-
q.text("massign")
|
7610
|
-
|
7611
|
-
q.breakable
|
7612
|
-
q.pp(target)
|
7613
|
-
|
7614
|
-
q.breakable
|
7615
|
-
q.pp(value)
|
7616
|
-
|
7617
|
-
q.pp(Comment::List.new(comments))
|
7618
|
-
end
|
7619
|
-
end
|
7620
|
-
|
7621
|
-
def to_json(*opts)
|
7622
|
-
{
|
7623
|
-
type: :massign,
|
7624
|
-
target: target,
|
7625
|
-
value: value,
|
7626
|
-
loc: location,
|
7627
|
-
cmts: comments
|
7628
|
-
}.to_json(*opts)
|
7629
|
-
end
|
7630
|
-
end
|
7631
|
-
|
7632
|
-
# :call-seq:
|
7633
|
-
# on_massign: ((MLHS | MLHSParen) target, untyped value) -> MAssign
|
7634
|
-
def on_massign(target, value)
|
7635
|
-
comma_range = target.location.end_char...value.location.start_char
|
7636
|
-
target.comma = true if source[comma_range].strip.start_with?(",")
|
7637
|
-
|
7638
|
-
MAssign.new(
|
7639
|
-
target: target,
|
7640
|
-
value: value,
|
7641
|
-
location: target.location.to(value.location)
|
7642
|
-
)
|
7643
|
-
end
|
7644
|
-
|
7645
|
-
# MethodAddArg represents a method call with arguments and parentheses.
|
7646
|
-
#
|
7647
|
-
# method(argument)
|
7648
|
-
#
|
7649
|
-
# MethodAddArg can also represent with a method on an object, as in:
|
7650
|
-
#
|
7651
|
-
# object.method(argument)
|
7652
|
-
#
|
7653
|
-
# Finally, MethodAddArg can represent calling a method with no receiver that
|
7654
|
-
# ends in a ?. In this case, the parser knows it's a method call and not a
|
7655
|
-
# local variable, so it uses a MethodAddArg node as opposed to a VCall node,
|
7656
|
-
# as in:
|
7657
|
-
#
|
7658
|
-
# method?
|
7659
|
-
#
|
7660
|
-
class MethodAddArg
|
7661
|
-
# [Call | FCall] the method call
|
7662
|
-
attr_reader :call
|
8178
|
+
class MAssign
|
8179
|
+
# [MLHS | MLHSParen] the target of the multiple assignment
|
8180
|
+
attr_reader :target
|
7663
8181
|
|
7664
|
-
# [
|
7665
|
-
attr_reader :
|
8182
|
+
# [untyped] the value being assigned
|
8183
|
+
attr_reader :value
|
7666
8184
|
|
7667
8185
|
# [Location] the location of this node
|
7668
8186
|
attr_reader :location
|
@@ -7670,32 +8188,37 @@ class SyntaxTree < Ripper
|
|
7670
8188
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
7671
8189
|
attr_reader :comments
|
7672
8190
|
|
7673
|
-
def initialize(
|
7674
|
-
@
|
7675
|
-
@
|
8191
|
+
def initialize(target:, value:, location:, comments: [])
|
8192
|
+
@target = target
|
8193
|
+
@value = value
|
7676
8194
|
@location = location
|
7677
8195
|
@comments = comments
|
7678
8196
|
end
|
7679
8197
|
|
7680
8198
|
def child_nodes
|
7681
|
-
[
|
8199
|
+
[target, value]
|
7682
8200
|
end
|
7683
8201
|
|
7684
8202
|
def format(q)
|
7685
|
-
q.
|
7686
|
-
|
7687
|
-
|
8203
|
+
q.group do
|
8204
|
+
q.group { q.format(target) }
|
8205
|
+
q.text(" =")
|
8206
|
+
q.indent do
|
8207
|
+
q.breakable
|
8208
|
+
q.format(value)
|
8209
|
+
end
|
8210
|
+
end
|
7688
8211
|
end
|
7689
8212
|
|
7690
8213
|
def pretty_print(q)
|
7691
8214
|
q.group(2, "(", ")") do
|
7692
|
-
q.text("
|
8215
|
+
q.text("massign")
|
7693
8216
|
|
7694
8217
|
q.breakable
|
7695
|
-
q.pp(
|
8218
|
+
q.pp(target)
|
7696
8219
|
|
7697
8220
|
q.breakable
|
7698
|
-
q.pp(
|
8221
|
+
q.pp(value)
|
7699
8222
|
|
7700
8223
|
q.pp(Comment::List.new(comments))
|
7701
8224
|
end
|
@@ -7703,25 +8226,48 @@ class SyntaxTree < Ripper
|
|
7703
8226
|
|
7704
8227
|
def to_json(*opts)
|
7705
8228
|
{
|
7706
|
-
type: :
|
7707
|
-
|
7708
|
-
|
8229
|
+
type: :massign,
|
8230
|
+
target: target,
|
8231
|
+
value: value,
|
7709
8232
|
loc: location,
|
7710
8233
|
cmts: comments
|
7711
8234
|
}.to_json(*opts)
|
7712
8235
|
end
|
7713
8236
|
end
|
7714
8237
|
|
8238
|
+
# :call-seq:
|
8239
|
+
# on_massign: ((MLHS | MLHSParen) target, untyped value) -> MAssign
|
8240
|
+
def on_massign(target, value)
|
8241
|
+
comma_range = target.location.end_char...value.location.start_char
|
8242
|
+
target.comma = true if source[comma_range].strip.start_with?(",")
|
8243
|
+
|
8244
|
+
MAssign.new(
|
8245
|
+
target: target,
|
8246
|
+
value: value,
|
8247
|
+
location: target.location.to(value.location)
|
8248
|
+
)
|
8249
|
+
end
|
8250
|
+
|
7715
8251
|
# :call-seq:
|
7716
8252
|
# on_method_add_arg: (
|
7717
8253
|
# (Call | FCall) call,
|
7718
8254
|
# (ArgParen | Args) arguments
|
7719
|
-
# ) ->
|
8255
|
+
# ) -> Call | FCall
|
7720
8256
|
def on_method_add_arg(call, arguments)
|
7721
8257
|
location = call.location
|
7722
|
-
location = location.to(arguments.location)
|
8258
|
+
location = location.to(arguments.location) if arguments.is_a?(ArgParen)
|
7723
8259
|
|
7724
|
-
|
8260
|
+
if call.is_a?(FCall)
|
8261
|
+
FCall.new(value: call.value, arguments: arguments, location: location)
|
8262
|
+
else
|
8263
|
+
Call.new(
|
8264
|
+
receiver: call.receiver,
|
8265
|
+
operator: call.operator,
|
8266
|
+
message: call.message,
|
8267
|
+
arguments: arguments,
|
8268
|
+
location: location
|
8269
|
+
)
|
8270
|
+
end
|
7725
8271
|
end
|
7726
8272
|
|
7727
8273
|
# MethodAddBlock represents a method call with a block argument.
|
@@ -7729,7 +8275,7 @@ class SyntaxTree < Ripper
|
|
7729
8275
|
# method {}
|
7730
8276
|
#
|
7731
8277
|
class MethodAddBlock
|
7732
|
-
# [Call | Command | CommandCall | FCall
|
8278
|
+
# [Call | Command | CommandCall | FCall] the method call
|
7733
8279
|
attr_reader :call
|
7734
8280
|
|
7735
8281
|
# [BraceBlock | DoBlock] the block being sent with the method call
|
@@ -7784,7 +8330,7 @@ class SyntaxTree < Ripper
|
|
7784
8330
|
|
7785
8331
|
# :call-seq:
|
7786
8332
|
# on_method_add_block: (
|
7787
|
-
# (Call | Command | CommandCall | FCall
|
8333
|
+
# (Call | Command | CommandCall | FCall) call,
|
7788
8334
|
# (BraceBlock | DoBlock) block
|
7789
8335
|
# ) -> MethodAddBlock
|
7790
8336
|
def on_method_add_block(call, block)
|
@@ -7809,7 +8355,6 @@ class SyntaxTree < Ripper
|
|
7809
8355
|
# list, which impacts destructuring. It's an attr_accessor so that while
|
7810
8356
|
# the syntax tree is being built it can be set by its parent node
|
7811
8357
|
attr_accessor :comma
|
7812
|
-
alias comma? comma
|
7813
8358
|
|
7814
8359
|
# [Location] the location of this node
|
7815
8360
|
attr_reader :location
|
@@ -7830,6 +8375,7 @@ class SyntaxTree < Ripper
|
|
7830
8375
|
|
7831
8376
|
def format(q)
|
7832
8377
|
q.seplist(parts) { |part| q.format(part) }
|
8378
|
+
q.text(",") if comma
|
7833
8379
|
end
|
7834
8380
|
|
7835
8381
|
def pretty_print(q)
|
@@ -7932,7 +8478,6 @@ class SyntaxTree < Ripper
|
|
7932
8478
|
q.indent do
|
7933
8479
|
q.breakable("")
|
7934
8480
|
q.format(contents)
|
7935
|
-
q.text(",") if contents.is_a?(MLHS) && contents.comma?
|
7936
8481
|
end
|
7937
8482
|
|
7938
8483
|
q.breakable("")
|
@@ -8400,6 +8945,61 @@ class SyntaxTree < Ripper
|
|
8400
8945
|
)
|
8401
8946
|
end
|
8402
8947
|
|
8948
|
+
# If you have a modifier statement (for instance a modifier if statement or a
|
8949
|
+
# modifier while loop) there are times when you need to wrap the entire
|
8950
|
+
# statement in parentheses. This occurs when you have something like:
|
8951
|
+
#
|
8952
|
+
# foo[:foo] =
|
8953
|
+
# if bar?
|
8954
|
+
# baz
|
8955
|
+
# end
|
8956
|
+
#
|
8957
|
+
# Normally we would shorten this to an inline version, which would result in:
|
8958
|
+
#
|
8959
|
+
# foo[:foo] = baz if bar?
|
8960
|
+
#
|
8961
|
+
# but this actually has different semantic meaning. The first example will
|
8962
|
+
# result in a nil being inserted into the hash for the :foo key, whereas the
|
8963
|
+
# second example will result in an empty hash because the if statement applies
|
8964
|
+
# to the entire assignment.
|
8965
|
+
#
|
8966
|
+
# We can fix this in a couple of ways. We can use the then keyword, as in:
|
8967
|
+
#
|
8968
|
+
# foo[:foo] = if bar? then baz end
|
8969
|
+
#
|
8970
|
+
# But this isn't used very often. We can also just leave it as is with the
|
8971
|
+
# multi-line version, but for a short predicate and short value it looks
|
8972
|
+
# verbose. The last option and the one used here is to add parentheses on
|
8973
|
+
# both sides of the expression, as in:
|
8974
|
+
#
|
8975
|
+
# foo[:foo] = (baz if bar?)
|
8976
|
+
#
|
8977
|
+
# This approach maintains the nice conciseness of the inline version, while
|
8978
|
+
# keeping the correct semantic meaning.
|
8979
|
+
module Parentheses
|
8980
|
+
NODES = [Args, Assign, Assoc, Binary, Call, Defined, MAssign, OpAssign]
|
8981
|
+
|
8982
|
+
def self.flat(q)
|
8983
|
+
return yield unless NODES.include?(q.parent.class)
|
8984
|
+
|
8985
|
+
q.text("(")
|
8986
|
+
yield
|
8987
|
+
q.text(")")
|
8988
|
+
end
|
8989
|
+
|
8990
|
+
def self.break(q)
|
8991
|
+
return yield unless NODES.include?(q.parent.class)
|
8992
|
+
|
8993
|
+
q.text("(")
|
8994
|
+
q.indent do
|
8995
|
+
q.breakable("")
|
8996
|
+
yield
|
8997
|
+
end
|
8998
|
+
q.breakable("")
|
8999
|
+
q.text(")")
|
9000
|
+
end
|
9001
|
+
end
|
9002
|
+
|
8403
9003
|
# def on_operator_ambiguous(value)
|
8404
9004
|
# value
|
8405
9005
|
# end
|
@@ -8459,7 +9059,7 @@ class SyntaxTree < Ripper
|
|
8459
9059
|
end
|
8460
9060
|
|
8461
9061
|
class KeywordRestFormatter
|
8462
|
-
# [:nil | KwRestParam] the value of the parameter
|
9062
|
+
# [:nil | ArgsForward | KwRestParam] the value of the parameter
|
8463
9063
|
attr_reader :value
|
8464
9064
|
|
8465
9065
|
def initialize(value)
|
@@ -8538,9 +9138,7 @@ class SyntaxTree < Ripper
|
|
8538
9138
|
# it's missing.
|
8539
9139
|
def empty?
|
8540
9140
|
requireds.empty? && optionals.empty? && !rest && posts.empty? &&
|
8541
|
-
keywords.empty? &&
|
8542
|
-
!keyword_rest &&
|
8543
|
-
!block
|
9141
|
+
keywords.empty? && !keyword_rest && !block
|
8544
9142
|
end
|
8545
9143
|
|
8546
9144
|
def child_nodes
|
@@ -8571,10 +9169,22 @@ class SyntaxTree < Ripper
|
|
8571
9169
|
parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
|
8572
9170
|
parts << block if block
|
8573
9171
|
|
8574
|
-
|
9172
|
+
contents = -> do
|
8575
9173
|
q.seplist(parts) { |part| q.format(part) }
|
8576
9174
|
q.format(rest) if rest && rest.is_a?(ExcessedComma)
|
8577
9175
|
end
|
9176
|
+
|
9177
|
+
if [Def, Defs, DefEndless].include?(q.parent.class)
|
9178
|
+
q.group(0, "(", ")") do
|
9179
|
+
q.indent do
|
9180
|
+
q.breakable("")
|
9181
|
+
contents.call
|
9182
|
+
end
|
9183
|
+
q.breakable("")
|
9184
|
+
end
|
9185
|
+
else
|
9186
|
+
q.nest(0, &contents)
|
9187
|
+
end
|
8578
9188
|
end
|
8579
9189
|
|
8580
9190
|
def pretty_print(q)
|
@@ -8664,8 +9274,8 @@ class SyntaxTree < Ripper
|
|
8664
9274
|
# (nil | ArgsForward | ExcessedComma | RestParam) rest,
|
8665
9275
|
# (nil | Array[Ident]) posts,
|
8666
9276
|
# (nil | Array[[Ident, nil | untyped]]) keywords,
|
8667
|
-
# (nil | :nil | KwRestParam) keyword_rest,
|
8668
|
-
# (nil | BlockArg) block
|
9277
|
+
# (nil | :nil | ArgsForward | KwRestParam) keyword_rest,
|
9278
|
+
# (nil | :& | BlockArg) block
|
8669
9279
|
# ) -> Params
|
8670
9280
|
def on_params(
|
8671
9281
|
requireds,
|
@@ -8676,16 +9286,15 @@ class SyntaxTree < Ripper
|
|
8676
9286
|
keyword_rest,
|
8677
9287
|
block
|
8678
9288
|
)
|
8679
|
-
parts =
|
8680
|
-
|
8681
|
-
|
8682
|
-
|
8683
|
-
|
8684
|
-
|
8685
|
-
|
8686
|
-
|
8687
|
-
|
8688
|
-
].compact
|
9289
|
+
parts = [
|
9290
|
+
*requireds,
|
9291
|
+
*optionals&.flatten(1),
|
9292
|
+
rest,
|
9293
|
+
*posts,
|
9294
|
+
*keywords&.flat_map { |(key, value)| [key, value || nil] },
|
9295
|
+
(keyword_rest if keyword_rest != :nil),
|
9296
|
+
(block if block != :&)
|
9297
|
+
].compact
|
8689
9298
|
|
8690
9299
|
location =
|
8691
9300
|
if parts.any?
|
@@ -8701,7 +9310,7 @@ class SyntaxTree < Ripper
|
|
8701
9310
|
posts: posts || [],
|
8702
9311
|
keywords: keywords || [],
|
8703
9312
|
keyword_rest: keyword_rest,
|
8704
|
-
block: block,
|
9313
|
+
block: (block if block != :&),
|
8705
9314
|
location: location
|
8706
9315
|
)
|
8707
9316
|
end
|
@@ -8716,7 +9325,7 @@ class SyntaxTree < Ripper
|
|
8716
9325
|
# [LParen] the left parenthesis that opened this statement
|
8717
9326
|
attr_reader :lparen
|
8718
9327
|
|
8719
|
-
# [untyped] the expression inside the parentheses
|
9328
|
+
# [nil | untyped] the expression inside the parentheses
|
8720
9329
|
attr_reader :contents
|
8721
9330
|
|
8722
9331
|
# [Location] the location of this node
|
@@ -8740,7 +9349,7 @@ class SyntaxTree < Ripper
|
|
8740
9349
|
q.group do
|
8741
9350
|
q.format(lparen)
|
8742
9351
|
|
8743
|
-
if !contents.is_a?(Params) || !contents.empty?
|
9352
|
+
if contents && (!contents.is_a?(Params) || !contents.empty?)
|
8744
9353
|
q.indent do
|
8745
9354
|
q.breakable("")
|
8746
9355
|
q.format(contents)
|
@@ -8805,7 +9414,7 @@ class SyntaxTree < Ripper
|
|
8805
9414
|
|
8806
9415
|
Paren.new(
|
8807
9416
|
lparen: lparen,
|
8808
|
-
contents: contents,
|
9417
|
+
contents: contents || nil,
|
8809
9418
|
location: lparen.location.to(rparen.location)
|
8810
9419
|
)
|
8811
9420
|
end
|
@@ -8896,7 +9505,11 @@ class SyntaxTree < Ripper
|
|
8896
9505
|
|
8897
9506
|
def format(q)
|
8898
9507
|
q.format(statements)
|
8899
|
-
|
9508
|
+
|
9509
|
+
# We're going to put a newline on the end so that it always has one unless
|
9510
|
+
# it ends with the special __END__ syntax. In that case we want to
|
9511
|
+
# replicate the text exactly so we will just let it be.
|
9512
|
+
q.breakable(force: true) unless statements.body.last.is_a?(EndContent)
|
8900
9513
|
end
|
8901
9514
|
|
8902
9515
|
def pretty_print(q)
|
@@ -9035,6 +9648,9 @@ class SyntaxTree < Ripper
|
|
9035
9648
|
# %i[one two three]
|
9036
9649
|
#
|
9037
9650
|
class QSymbols
|
9651
|
+
# [QSymbolsBeg] the token that opens this array literal
|
9652
|
+
attr_reader :beginning
|
9653
|
+
|
9038
9654
|
# [Array[ TStringContent ]] the elements of the array
|
9039
9655
|
attr_reader :elements
|
9040
9656
|
|
@@ -9044,7 +9660,8 @@ class SyntaxTree < Ripper
|
|
9044
9660
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
9045
9661
|
attr_reader :comments
|
9046
9662
|
|
9047
|
-
def initialize(elements:, location:, comments: [])
|
9663
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
9664
|
+
@beginning = beginning
|
9048
9665
|
@elements = elements
|
9049
9666
|
@location = location
|
9050
9667
|
@comments = comments
|
@@ -9055,7 +9672,14 @@ class SyntaxTree < Ripper
|
|
9055
9672
|
end
|
9056
9673
|
|
9057
9674
|
def format(q)
|
9058
|
-
|
9675
|
+
opening, closing = "%i[", "]"
|
9676
|
+
|
9677
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
9678
|
+
opening = beginning.value
|
9679
|
+
closing = Quotes.matching(opening[2])
|
9680
|
+
end
|
9681
|
+
|
9682
|
+
q.group(0, opening, closing) do
|
9059
9683
|
q.indent do
|
9060
9684
|
q.breakable("")
|
9061
9685
|
q.seplist(elements, -> { q.breakable }) do |element|
|
@@ -9091,6 +9715,7 @@ class SyntaxTree < Ripper
|
|
9091
9715
|
# on_qsymbols_add: (QSymbols qsymbols, TStringContent element) -> QSymbols
|
9092
9716
|
def on_qsymbols_add(qsymbols, element)
|
9093
9717
|
QSymbols.new(
|
9718
|
+
beginning: qsymbols.beginning,
|
9094
9719
|
elements: qsymbols.elements << element,
|
9095
9720
|
location: qsymbols.location.to(element.location)
|
9096
9721
|
)
|
@@ -9132,9 +9757,13 @@ class SyntaxTree < Ripper
|
|
9132
9757
|
# :call-seq:
|
9133
9758
|
# on_qsymbols_new: () -> QSymbols
|
9134
9759
|
def on_qsymbols_new
|
9135
|
-
|
9760
|
+
beginning = find_token(QSymbolsBeg)
|
9136
9761
|
|
9137
|
-
QSymbols.new(
|
9762
|
+
QSymbols.new(
|
9763
|
+
beginning: beginning,
|
9764
|
+
elements: [],
|
9765
|
+
location: beginning.location
|
9766
|
+
)
|
9138
9767
|
end
|
9139
9768
|
|
9140
9769
|
# QWords represents a string literal array without interpolation.
|
@@ -9142,6 +9771,9 @@ class SyntaxTree < Ripper
|
|
9142
9771
|
# %w[one two three]
|
9143
9772
|
#
|
9144
9773
|
class QWords
|
9774
|
+
# [QWordsBeg] the token that opens this array literal
|
9775
|
+
attr_reader :beginning
|
9776
|
+
|
9145
9777
|
# [Array[ TStringContent ]] the elements of the array
|
9146
9778
|
attr_reader :elements
|
9147
9779
|
|
@@ -9151,7 +9783,8 @@ class SyntaxTree < Ripper
|
|
9151
9783
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
9152
9784
|
attr_reader :comments
|
9153
9785
|
|
9154
|
-
def initialize(elements:, location:, comments: [])
|
9786
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
9787
|
+
@beginning = beginning
|
9155
9788
|
@elements = elements
|
9156
9789
|
@location = location
|
9157
9790
|
@comments = comments
|
@@ -9162,7 +9795,14 @@ class SyntaxTree < Ripper
|
|
9162
9795
|
end
|
9163
9796
|
|
9164
9797
|
def format(q)
|
9165
|
-
|
9798
|
+
opening, closing = "%w[", "]"
|
9799
|
+
|
9800
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
9801
|
+
opening = beginning.value
|
9802
|
+
closing = Quotes.matching(opening[2])
|
9803
|
+
end
|
9804
|
+
|
9805
|
+
q.group(0, opening, closing) do
|
9166
9806
|
q.indent do
|
9167
9807
|
q.breakable("")
|
9168
9808
|
q.seplist(elements, -> { q.breakable }) do |element|
|
@@ -9195,6 +9835,7 @@ class SyntaxTree < Ripper
|
|
9195
9835
|
# on_qwords_add: (QWords qwords, TStringContent element) -> QWords
|
9196
9836
|
def on_qwords_add(qwords, element)
|
9197
9837
|
QWords.new(
|
9838
|
+
beginning: qwords.beginning,
|
9198
9839
|
elements: qwords.elements << element,
|
9199
9840
|
location: qwords.location.to(element.location)
|
9200
9841
|
)
|
@@ -9236,9 +9877,9 @@ class SyntaxTree < Ripper
|
|
9236
9877
|
# :call-seq:
|
9237
9878
|
# on_qwords_new: () -> QWords
|
9238
9879
|
def on_qwords_new
|
9239
|
-
|
9880
|
+
beginning = find_token(QWordsBeg)
|
9240
9881
|
|
9241
|
-
QWords.new(elements: [], location:
|
9882
|
+
QWords.new(beginning: beginning, elements: [], location: beginning.location)
|
9242
9883
|
end
|
9243
9884
|
|
9244
9885
|
# RationalLiteral represents the use of a rational number literal.
|
@@ -9550,11 +10191,32 @@ class SyntaxTree < Ripper
|
|
9550
10191
|
q.format_each(parts)
|
9551
10192
|
q.text(ending)
|
9552
10193
|
end
|
10194
|
+
elsif braces
|
10195
|
+
q.group do
|
10196
|
+
q.text("%r{")
|
10197
|
+
|
10198
|
+
if beginning == "/"
|
10199
|
+
# If we're changing from a forward slash to a %r{, then we can
|
10200
|
+
# replace any escaped forward slashes with regular forward slashes.
|
10201
|
+
parts.each do |part|
|
10202
|
+
if part.is_a?(TStringContent)
|
10203
|
+
q.text(part.value.gsub("\\/", "/"))
|
10204
|
+
else
|
10205
|
+
q.format(part)
|
10206
|
+
end
|
10207
|
+
end
|
10208
|
+
else
|
10209
|
+
q.format_each(parts)
|
10210
|
+
end
|
10211
|
+
|
10212
|
+
q.text("}")
|
10213
|
+
q.text(ending[1..-1])
|
10214
|
+
end
|
9553
10215
|
else
|
9554
10216
|
q.group do
|
9555
|
-
q.text(
|
10217
|
+
q.text("/")
|
9556
10218
|
q.format_each(parts)
|
9557
|
-
q.text(
|
10219
|
+
q.text("/")
|
9558
10220
|
q.text(ending[1..-1])
|
9559
10221
|
end
|
9560
10222
|
end
|
@@ -10285,21 +10947,6 @@ class SyntaxTree < Ripper
|
|
10285
10947
|
# value
|
10286
10948
|
# end
|
10287
10949
|
|
10288
|
-
# stmts_add is a parser event that represents a single statement inside a
|
10289
|
-
# list of statements within any lexical block. It accepts as arguments the
|
10290
|
-
# parent stmts node as well as an stmt which can be any expression in
|
10291
|
-
# Ruby.
|
10292
|
-
def on_stmts_add(statements, statement)
|
10293
|
-
location =
|
10294
|
-
if statements.body.empty?
|
10295
|
-
statement.location
|
10296
|
-
else
|
10297
|
-
statements.location.to(statement.location)
|
10298
|
-
end
|
10299
|
-
|
10300
|
-
Statements.new(self, body: statements.body << statement, location: location)
|
10301
|
-
end
|
10302
|
-
|
10303
10950
|
# Everything that has a block of code inside of it has a list of statements.
|
10304
10951
|
# Normally we would just track those as a node that has an array body, but we
|
10305
10952
|
# have some special handling in order to handle empty statement lists. They
|
@@ -10379,12 +11026,22 @@ class SyntaxTree < Ripper
|
|
10379
11026
|
# the only value is a comment. In that case a lot of nodes like
|
10380
11027
|
# brace_block will attempt to format as a single line, but since that
|
10381
11028
|
# wouldn't work with a comment, we intentionally break the parent group.
|
10382
|
-
if body.length == 2
|
10383
|
-
|
10384
|
-
|
10385
|
-
|
11029
|
+
if body.length == 2
|
11030
|
+
void_stmt, comment = body
|
11031
|
+
|
11032
|
+
if void_stmt.is_a?(VoidStmt) && comment.is_a?(Comment)
|
11033
|
+
q.format(comment)
|
11034
|
+
q.break_parent
|
11035
|
+
return
|
11036
|
+
end
|
10386
11037
|
end
|
10387
11038
|
|
11039
|
+
access_controls =
|
11040
|
+
Hash.new do |hash, node|
|
11041
|
+
hash[node] = node.is_a?(VCall) &&
|
11042
|
+
%w[private protected public].include?(node.value.value)
|
11043
|
+
end
|
11044
|
+
|
10388
11045
|
body.each_with_index do |statement, index|
|
10389
11046
|
next if statement.is_a?(VoidStmt)
|
10390
11047
|
|
@@ -10394,7 +11051,7 @@ class SyntaxTree < Ripper
|
|
10394
11051
|
q.breakable(force: true)
|
10395
11052
|
q.breakable(force: true)
|
10396
11053
|
q.format(statement)
|
10397
|
-
elsif statement
|
11054
|
+
elsif access_controls[statement] || access_controls[body[index - 1]]
|
10398
11055
|
q.breakable(force: true)
|
10399
11056
|
q.breakable(force: true)
|
10400
11057
|
q.format(statement)
|
@@ -10446,7 +11103,7 @@ class SyntaxTree < Ripper
|
|
10446
11103
|
location = comment.location
|
10447
11104
|
|
10448
11105
|
if !comment.inline? && (start_char <= location.start_char) &&
|
10449
|
-
(end_char >= location.end_char)
|
11106
|
+
(end_char >= location.end_char) && !comment.ignore?
|
10450
11107
|
parser_comments.delete_at(comment_index)
|
10451
11108
|
|
10452
11109
|
while (node = body[body_index]) &&
|
@@ -10465,6 +11122,21 @@ class SyntaxTree < Ripper
|
|
10465
11122
|
end
|
10466
11123
|
end
|
10467
11124
|
|
11125
|
+
# stmts_add is a parser event that represents a single statement inside a
|
11126
|
+
# list of statements within any lexical block. It accepts as arguments the
|
11127
|
+
# parent stmts node as well as an stmt which can be any expression in
|
11128
|
+
# Ruby.
|
11129
|
+
def on_stmts_add(statements, statement)
|
11130
|
+
location =
|
11131
|
+
if statements.body.empty?
|
11132
|
+
statement.location
|
11133
|
+
else
|
11134
|
+
statements.location.to(statement.location)
|
11135
|
+
end
|
11136
|
+
|
11137
|
+
Statements.new(self, body: statements.body << statement, location: location)
|
11138
|
+
end
|
11139
|
+
|
10468
11140
|
# :call-seq:
|
10469
11141
|
# on_stmts_new: () -> Statements
|
10470
11142
|
def on_stmts_new
|
@@ -10736,10 +11408,18 @@ class SyntaxTree < Ripper
|
|
10736
11408
|
embexpr_end.location.start_char
|
10737
11409
|
)
|
10738
11410
|
|
10739
|
-
|
10740
|
-
|
10741
|
-
|
10742
|
-
|
11411
|
+
location =
|
11412
|
+
Location.new(
|
11413
|
+
start_line: embexpr_beg.location.start_line,
|
11414
|
+
start_char: embexpr_beg.location.start_char,
|
11415
|
+
end_line: [
|
11416
|
+
embexpr_end.location.end_line,
|
11417
|
+
statements.location.end_line
|
11418
|
+
].max,
|
11419
|
+
end_char: embexpr_end.location.end_char
|
11420
|
+
)
|
11421
|
+
|
11422
|
+
StringEmbExpr.new(statements: statements, location: location)
|
10743
11423
|
end
|
10744
11424
|
|
10745
11425
|
# StringLiteral represents a string literal.
|
@@ -10839,12 +11519,23 @@ class SyntaxTree < Ripper
|
|
10839
11519
|
)
|
10840
11520
|
else
|
10841
11521
|
tstring_beg = find_token(TStringBeg)
|
10842
|
-
tstring_end = find_token(TStringEnd)
|
11522
|
+
tstring_end = find_token(TStringEnd, location: tstring_beg.location)
|
11523
|
+
|
11524
|
+
location =
|
11525
|
+
Location.new(
|
11526
|
+
start_line: tstring_beg.location.start_line,
|
11527
|
+
start_char: tstring_beg.location.start_char,
|
11528
|
+
end_line: [
|
11529
|
+
tstring_end.location.end_line,
|
11530
|
+
string.location.end_line
|
11531
|
+
].max,
|
11532
|
+
end_char: tstring_end.location.end_char
|
11533
|
+
)
|
10843
11534
|
|
10844
11535
|
StringLiteral.new(
|
10845
11536
|
parts: string.parts,
|
10846
11537
|
quote: tstring_beg.value,
|
10847
|
-
location:
|
11538
|
+
location: location
|
10848
11539
|
)
|
10849
11540
|
end
|
10850
11541
|
end
|
@@ -11066,6 +11757,9 @@ class SyntaxTree < Ripper
|
|
11066
11757
|
# %I[one two three]
|
11067
11758
|
#
|
11068
11759
|
class Symbols
|
11760
|
+
# [SymbolsBeg] the token that opens this array literal
|
11761
|
+
attr_reader :beginning
|
11762
|
+
|
11069
11763
|
# [Array[ Word ]] the words in the symbol array literal
|
11070
11764
|
attr_reader :elements
|
11071
11765
|
|
@@ -11075,7 +11769,8 @@ class SyntaxTree < Ripper
|
|
11075
11769
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
11076
11770
|
attr_reader :comments
|
11077
11771
|
|
11078
|
-
def initialize(elements:, location:, comments: [])
|
11772
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
11773
|
+
@beginning = beginning
|
11079
11774
|
@elements = elements
|
11080
11775
|
@location = location
|
11081
11776
|
@comments = comments
|
@@ -11086,7 +11781,14 @@ class SyntaxTree < Ripper
|
|
11086
11781
|
end
|
11087
11782
|
|
11088
11783
|
def format(q)
|
11089
|
-
|
11784
|
+
opening, closing = "%I[", "]"
|
11785
|
+
|
11786
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
11787
|
+
opening = beginning.value
|
11788
|
+
closing = Quotes.matching(opening[2])
|
11789
|
+
end
|
11790
|
+
|
11791
|
+
q.group(0, opening, closing) do
|
11090
11792
|
q.indent do
|
11091
11793
|
q.breakable("")
|
11092
11794
|
q.seplist(elements, -> { q.breakable }) do |element|
|
@@ -11122,6 +11824,7 @@ class SyntaxTree < Ripper
|
|
11122
11824
|
# on_symbols_add: (Symbols symbols, Word word) -> Symbols
|
11123
11825
|
def on_symbols_add(symbols, word)
|
11124
11826
|
Symbols.new(
|
11827
|
+
beginning: symbols.beginning,
|
11125
11828
|
elements: symbols.elements << word,
|
11126
11829
|
location: symbols.location.to(word.location)
|
11127
11830
|
)
|
@@ -11164,9 +11867,13 @@ class SyntaxTree < Ripper
|
|
11164
11867
|
# :call-seq:
|
11165
11868
|
# on_symbols_new: () -> Symbols
|
11166
11869
|
def on_symbols_new
|
11167
|
-
|
11870
|
+
beginning = find_token(SymbolsBeg)
|
11168
11871
|
|
11169
|
-
Symbols.new(
|
11872
|
+
Symbols.new(
|
11873
|
+
beginning: beginning,
|
11874
|
+
elements: [],
|
11875
|
+
location: beginning.location
|
11876
|
+
)
|
11170
11877
|
end
|
11171
11878
|
|
11172
11879
|
# TLambda represents the beginning of a lambda literal.
|
@@ -11258,6 +11965,11 @@ class SyntaxTree < Ripper
|
|
11258
11965
|
[constant]
|
11259
11966
|
end
|
11260
11967
|
|
11968
|
+
def format(q)
|
11969
|
+
q.text("::")
|
11970
|
+
q.format(constant)
|
11971
|
+
end
|
11972
|
+
|
11261
11973
|
def pretty_print(q)
|
11262
11974
|
q.group(2, "(", ")") do
|
11263
11975
|
q.text("top_const_field")
|
@@ -11412,6 +12124,10 @@ class SyntaxTree < Ripper
|
|
11412
12124
|
@comments = comments
|
11413
12125
|
end
|
11414
12126
|
|
12127
|
+
def match?(pattern)
|
12128
|
+
value.match?(pattern)
|
12129
|
+
end
|
12130
|
+
|
11415
12131
|
def child_nodes
|
11416
12132
|
[]
|
11417
12133
|
end
|
@@ -11914,23 +12630,35 @@ class SyntaxTree < Ripper
|
|
11914
12630
|
end
|
11915
12631
|
|
11916
12632
|
def format(q)
|
12633
|
+
if ContainsAssignment.call(node.predicate)
|
12634
|
+
format_break(q)
|
12635
|
+
q.break_parent
|
12636
|
+
return
|
12637
|
+
end
|
12638
|
+
|
11917
12639
|
q.group do
|
11918
|
-
q.if_break do
|
11919
|
-
|
11920
|
-
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
11921
|
-
q.indent do
|
11922
|
-
q.breakable("")
|
12640
|
+
q.if_break { format_break(q) }.if_flat do
|
12641
|
+
Parentheses.flat(q) do
|
11923
12642
|
q.format(statements)
|
12643
|
+
q.text(" #{keyword} ")
|
12644
|
+
q.format(node.predicate)
|
11924
12645
|
end
|
11925
|
-
q.breakable("")
|
11926
|
-
q.text("end")
|
11927
|
-
end.if_flat do
|
11928
|
-
q.format(statements)
|
11929
|
-
q.text(" #{keyword} ")
|
11930
|
-
q.format(node.predicate)
|
11931
12646
|
end
|
11932
12647
|
end
|
11933
12648
|
end
|
12649
|
+
|
12650
|
+
private
|
12651
|
+
|
12652
|
+
def format_break(q)
|
12653
|
+
q.text("#{keyword} ")
|
12654
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
12655
|
+
q.indent do
|
12656
|
+
q.breakable("")
|
12657
|
+
q.format(statements)
|
12658
|
+
end
|
12659
|
+
q.breakable("")
|
12660
|
+
q.text("end")
|
12661
|
+
end
|
11934
12662
|
end
|
11935
12663
|
|
11936
12664
|
# Until represents an +until+ loop.
|
@@ -12055,7 +12783,27 @@ class SyntaxTree < Ripper
|
|
12055
12783
|
end
|
12056
12784
|
|
12057
12785
|
def format(q)
|
12058
|
-
|
12786
|
+
# If we're in the modifier form and we're modifying a `begin`, then this
|
12787
|
+
# is a special case where we need to explicitly use the modifier form
|
12788
|
+
# because otherwise the semantic meaning changes. This looks like:
|
12789
|
+
#
|
12790
|
+
# begin
|
12791
|
+
# foo
|
12792
|
+
# end until bar
|
12793
|
+
#
|
12794
|
+
# Also, if the statement of the modifier includes an assignment, then we
|
12795
|
+
# can't know for certain that it won't impact the predicate, so we need to
|
12796
|
+
# force it to stay as it is. This looks like:
|
12797
|
+
#
|
12798
|
+
# foo = bar until foo
|
12799
|
+
#
|
12800
|
+
if statement.is_a?(Begin) || ContainsAssignment.call(statement)
|
12801
|
+
q.format(statement)
|
12802
|
+
q.text(" until ")
|
12803
|
+
q.format(predicate)
|
12804
|
+
else
|
12805
|
+
LoopFormatter.new("until", self, statement).format(q)
|
12806
|
+
end
|
12059
12807
|
end
|
12060
12808
|
|
12061
12809
|
def pretty_print(q)
|
@@ -12285,19 +13033,17 @@ class SyntaxTree < Ripper
|
|
12285
13033
|
end
|
12286
13034
|
end
|
12287
13035
|
|
12288
|
-
#
|
12289
|
-
#
|
12290
|
-
def on_var_ref(value)
|
12291
|
-
VarRef.new(value: value, location: value.location)
|
12292
|
-
end
|
12293
|
-
|
12294
|
-
# AccessCtrl represents a call to a method visibility control, i.e., +public+,
|
12295
|
-
# +protected+, or +private+.
|
13036
|
+
# PinnedVarRef represents a pinned variable reference within a pattern
|
13037
|
+
# matching pattern.
|
12296
13038
|
#
|
12297
|
-
#
|
13039
|
+
# case value
|
13040
|
+
# in ^variable
|
13041
|
+
# end
|
12298
13042
|
#
|
12299
|
-
|
12300
|
-
|
13043
|
+
# This can be a plain local variable like the example above. It can also be a
|
13044
|
+
# a class variable, a global variable, or an instance variable.
|
13045
|
+
class PinnedVarRef
|
13046
|
+
# [VarRef] the value of this node
|
12301
13047
|
attr_reader :value
|
12302
13048
|
|
12303
13049
|
# [Location] the location of this node
|
@@ -12317,12 +13063,15 @@ class SyntaxTree < Ripper
|
|
12317
13063
|
end
|
12318
13064
|
|
12319
13065
|
def format(q)
|
12320
|
-
q.
|
13066
|
+
q.group do
|
13067
|
+
q.text("^")
|
13068
|
+
q.format(value)
|
13069
|
+
end
|
12321
13070
|
end
|
12322
13071
|
|
12323
13072
|
def pretty_print(q)
|
12324
13073
|
q.group(2, "(", ")") do
|
12325
|
-
q.text("
|
13074
|
+
q.text("pinned_var_ref")
|
12326
13075
|
|
12327
13076
|
q.breakable
|
12328
13077
|
q.pp(value)
|
@@ -12332,12 +13081,21 @@ class SyntaxTree < Ripper
|
|
12332
13081
|
end
|
12333
13082
|
|
12334
13083
|
def to_json(*opts)
|
12335
|
-
{
|
12336
|
-
|
12337
|
-
|
12338
|
-
|
12339
|
-
|
12340
|
-
|
13084
|
+
{ type: :pinned_var_ref, value: value, loc: location, cmts: comments }
|
13085
|
+
.to_json(*opts)
|
13086
|
+
end
|
13087
|
+
end
|
13088
|
+
|
13089
|
+
# :call-seq:
|
13090
|
+
# on_var_ref: ((Const | CVar | GVar | Ident | IVar | Kw) value) -> VarRef
|
13091
|
+
def on_var_ref(value)
|
13092
|
+
pin = find_token(Op, "^", consume: false)
|
13093
|
+
|
13094
|
+
if pin && pin.location.start_char == value.location.start_char - 1
|
13095
|
+
tokens.delete(pin)
|
13096
|
+
PinnedVarRef.new(value: value, location: pin.location.to(value.location))
|
13097
|
+
else
|
13098
|
+
VarRef.new(value: value, location: value.location)
|
12341
13099
|
end
|
12342
13100
|
end
|
12343
13101
|
|
@@ -12389,19 +13147,9 @@ class SyntaxTree < Ripper
|
|
12389
13147
|
end
|
12390
13148
|
|
12391
13149
|
# :call-seq:
|
12392
|
-
# on_vcall: (Ident ident) ->
|
13150
|
+
# on_vcall: (Ident ident) -> VCall
|
12393
13151
|
def on_vcall(ident)
|
12394
|
-
|
12395
|
-
|
12396
|
-
if @controls.include?(ident.value) && ident.value == lines[lineno - 1].strip
|
12397
|
-
# Access controls like private, protected, and public are reported as
|
12398
|
-
# vcall nodes since they're technically method calls. We want to be able
|
12399
|
-
# add new lines around them as necessary, so here we're going to
|
12400
|
-
# explicitly track those as a different node type.
|
12401
|
-
AccessCtrl.new(value: ident, location: ident.location)
|
12402
|
-
else
|
12403
|
-
VCall.new(value: ident, location: ident.location)
|
12404
|
-
end
|
13152
|
+
VCall.new(value: ident, location: ident.location)
|
12405
13153
|
end
|
12406
13154
|
|
12407
13155
|
# VoidStmt represents an empty lexical block of code.
|
@@ -12420,6 +13168,10 @@ class SyntaxTree < Ripper
|
|
12420
13168
|
@comments = comments
|
12421
13169
|
end
|
12422
13170
|
|
13171
|
+
def child_nodes
|
13172
|
+
[]
|
13173
|
+
end
|
13174
|
+
|
12423
13175
|
def format(q)
|
12424
13176
|
end
|
12425
13177
|
|
@@ -12494,6 +13246,14 @@ class SyntaxTree < Ripper
|
|
12494
13246
|
separator = -> { q.group { q.comma_breakable } }
|
12495
13247
|
q.seplist(arguments.parts, separator) { |part| q.format(part) }
|
12496
13248
|
end
|
13249
|
+
|
13250
|
+
# Very special case here. If you're inside of a when clause and the
|
13251
|
+
# last argument to the predicate is and endless range, then you are
|
13252
|
+
# forced to use the "then" keyword to make it parse properly.
|
13253
|
+
last = arguments.parts.last
|
13254
|
+
if (last.is_a?(Dot2) || last.is_a?(Dot3)) && !last.right
|
13255
|
+
q.text(" then")
|
13256
|
+
end
|
12497
13257
|
end
|
12498
13258
|
end
|
12499
13259
|
|
@@ -12552,7 +13312,16 @@ class SyntaxTree < Ripper
|
|
12552
13312
|
beginning = find_token(Kw, "when")
|
12553
13313
|
ending = consequent || find_token(Kw, "end")
|
12554
13314
|
|
12555
|
-
|
13315
|
+
statements_start = arguments
|
13316
|
+
if token = find_token(Kw, "then", consume: false)
|
13317
|
+
tokens.delete(token)
|
13318
|
+
statements_start = token
|
13319
|
+
end
|
13320
|
+
|
13321
|
+
statements.bind(
|
13322
|
+
find_next_statement_start(statements_start.location.end_char),
|
13323
|
+
ending.location.start_char
|
13324
|
+
)
|
12556
13325
|
|
12557
13326
|
When.new(
|
12558
13327
|
arguments: arguments,
|
@@ -12684,7 +13453,27 @@ class SyntaxTree < Ripper
|
|
12684
13453
|
end
|
12685
13454
|
|
12686
13455
|
def format(q)
|
12687
|
-
|
13456
|
+
# If we're in the modifier form and we're modifying a `begin`, then this
|
13457
|
+
# is a special case where we need to explicitly use the modifier form
|
13458
|
+
# because otherwise the semantic meaning changes. This looks like:
|
13459
|
+
#
|
13460
|
+
# begin
|
13461
|
+
# foo
|
13462
|
+
# end while bar
|
13463
|
+
#
|
13464
|
+
# Also, if the statement of the modifier includes an assignment, then we
|
13465
|
+
# can't know for certain that it won't impact the predicate, so we need to
|
13466
|
+
# force it to stay as it is. This looks like:
|
13467
|
+
#
|
13468
|
+
# foo = bar while foo
|
13469
|
+
#
|
13470
|
+
if statement.is_a?(Begin) || ContainsAssignment.call(statement)
|
13471
|
+
q.format(statement)
|
13472
|
+
q.text(" while ")
|
13473
|
+
q.format(predicate)
|
13474
|
+
else
|
13475
|
+
LoopFormatter.new("while", self, statement).format(q)
|
13476
|
+
end
|
12688
13477
|
end
|
12689
13478
|
|
12690
13479
|
def pretty_print(q)
|
@@ -12748,6 +13537,10 @@ class SyntaxTree < Ripper
|
|
12748
13537
|
@comments = comments
|
12749
13538
|
end
|
12750
13539
|
|
13540
|
+
def match?(pattern)
|
13541
|
+
parts.any? { |part| part.is_a?(TStringContent) && part.match?(pattern) }
|
13542
|
+
end
|
13543
|
+
|
12751
13544
|
def child_nodes
|
12752
13545
|
parts
|
12753
13546
|
end
|
@@ -12797,6 +13590,9 @@ class SyntaxTree < Ripper
|
|
12797
13590
|
# %W[one two three]
|
12798
13591
|
#
|
12799
13592
|
class Words
|
13593
|
+
# [WordsBeg] the token that opens this array literal
|
13594
|
+
attr_reader :beginning
|
13595
|
+
|
12800
13596
|
# [Array[ Word ]] the elements of this array
|
12801
13597
|
attr_reader :elements
|
12802
13598
|
|
@@ -12806,7 +13602,8 @@ class SyntaxTree < Ripper
|
|
12806
13602
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
12807
13603
|
attr_reader :comments
|
12808
13604
|
|
12809
|
-
def initialize(elements:, location:, comments: [])
|
13605
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
13606
|
+
@beginning = beginning
|
12810
13607
|
@elements = elements
|
12811
13608
|
@location = location
|
12812
13609
|
@comments = comments
|
@@ -12817,7 +13614,14 @@ class SyntaxTree < Ripper
|
|
12817
13614
|
end
|
12818
13615
|
|
12819
13616
|
def format(q)
|
12820
|
-
|
13617
|
+
opening, closing = "%W[", "]"
|
13618
|
+
|
13619
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
13620
|
+
opening = beginning.value
|
13621
|
+
closing = Quotes.matching(opening[2])
|
13622
|
+
end
|
13623
|
+
|
13624
|
+
q.group(0, opening, closing) do
|
12821
13625
|
q.indent do
|
12822
13626
|
q.breakable("")
|
12823
13627
|
q.seplist(elements, -> { q.breakable }) do |element|
|
@@ -12850,6 +13654,7 @@ class SyntaxTree < Ripper
|
|
12850
13654
|
# on_words_add: (Words words, Word word) -> Words
|
12851
13655
|
def on_words_add(words, word)
|
12852
13656
|
Words.new(
|
13657
|
+
beginning: words.beginning,
|
12853
13658
|
elements: words.elements << word,
|
12854
13659
|
location: words.location.to(word.location)
|
12855
13660
|
)
|
@@ -12892,9 +13697,9 @@ class SyntaxTree < Ripper
|
|
12892
13697
|
# :call-seq:
|
12893
13698
|
# on_words_new: () -> Words
|
12894
13699
|
def on_words_new
|
12895
|
-
|
13700
|
+
beginning = find_token(WordsBeg)
|
12896
13701
|
|
12897
|
-
Words.new(elements: [], location:
|
13702
|
+
Words.new(beginning: beginning, elements: [], location: beginning.location)
|
12898
13703
|
end
|
12899
13704
|
|
12900
13705
|
# def on_words_sep(value)
|
@@ -13011,7 +13816,7 @@ class SyntaxTree < Ripper
|
|
13011
13816
|
location: heredoc.location
|
13012
13817
|
)
|
13013
13818
|
else
|
13014
|
-
ending = find_token(TStringEnd)
|
13819
|
+
ending = find_token(TStringEnd, location: xstring.location)
|
13015
13820
|
|
13016
13821
|
XStringLiteral.new(
|
13017
13822
|
parts: xstring.parts,
|
@@ -13047,8 +13852,18 @@ class SyntaxTree < Ripper
|
|
13047
13852
|
def format(q)
|
13048
13853
|
q.group do
|
13049
13854
|
q.text("yield")
|
13050
|
-
|
13051
|
-
|
13855
|
+
|
13856
|
+
if arguments.is_a?(Paren)
|
13857
|
+
q.format(arguments)
|
13858
|
+
else
|
13859
|
+
q.if_break { q.text("(") }.if_flat { q.text(" ") }
|
13860
|
+
q.indent do
|
13861
|
+
q.breakable("")
|
13862
|
+
q.format(arguments)
|
13863
|
+
end
|
13864
|
+
q.breakable("")
|
13865
|
+
q.if_break { q.text(")") }
|
13866
|
+
end
|
13052
13867
|
end
|
13053
13868
|
end
|
13054
13869
|
|