syntax_tree 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -1
- data/Gemfile.lock +2 -2
- data/README.md +7 -0
- data/exe/stree +4 -80
- data/lib/syntax_tree/cli.rb +237 -0
- data/lib/syntax_tree/prettyprint.rb +19 -9
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree.rb +950 -430
- metadata +3 -2
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,10 +120,15 @@ 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
|
|
@@ -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
|
|
@@ -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,7 +287,7 @@ 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
|
|
@@ -280,7 +308,7 @@ class SyntaxTree < Ripper
|
|
|
280
308
|
# this line, then we add the number of columns into this line that we've gone
|
|
281
309
|
# through.
|
|
282
310
|
def char_pos
|
|
283
|
-
|
|
311
|
+
line_counts[lineno - 1][column]
|
|
284
312
|
end
|
|
285
313
|
|
|
286
314
|
# As we build up a list of tokens, we'll periodically need to go backwards and
|
|
@@ -294,7 +322,7 @@ class SyntaxTree < Ripper
|
|
|
294
322
|
# (which would happen to be the innermost keyword). Then the outer one would
|
|
295
323
|
# only be able to grab the first one. In this way all of the tokens act as
|
|
296
324
|
# their own stack.
|
|
297
|
-
def find_token(type, value = :any, consume: true)
|
|
325
|
+
def find_token(type, value = :any, consume: true, location: nil)
|
|
298
326
|
index =
|
|
299
327
|
tokens.rindex do |token|
|
|
300
328
|
token.is_a?(type) && (value == :any || (token.value == value))
|
|
@@ -307,8 +335,16 @@ class SyntaxTree < Ripper
|
|
|
307
335
|
# could also be caused by accidentally attempting to consume a token twice
|
|
308
336
|
# by two different parser event handlers.
|
|
309
337
|
unless index
|
|
310
|
-
|
|
311
|
-
|
|
338
|
+
token = value == :any ? type.name.split("::", 2).last : value
|
|
339
|
+
message = "Cannot find expected #{token}"
|
|
340
|
+
|
|
341
|
+
if location
|
|
342
|
+
lineno = location.start_line
|
|
343
|
+
column = location.start_char - line_counts[lineno - 1].start
|
|
344
|
+
raise ParseError.new(message, lineno, column)
|
|
345
|
+
else
|
|
346
|
+
raise ParseError.new(message, lineno, column)
|
|
347
|
+
end
|
|
312
348
|
end
|
|
313
349
|
|
|
314
350
|
tokens.delete_at(index)
|
|
@@ -476,7 +512,7 @@ class SyntaxTree < Ripper
|
|
|
476
512
|
q.text(value)
|
|
477
513
|
else
|
|
478
514
|
q.text(q.quote)
|
|
479
|
-
q.text(value[1])
|
|
515
|
+
q.text(value[1] == "\"" ? "\\\"" : value[1])
|
|
480
516
|
q.text(q.quote)
|
|
481
517
|
end
|
|
482
518
|
end
|
|
@@ -628,7 +664,9 @@ class SyntaxTree < Ripper
|
|
|
628
664
|
def format(q)
|
|
629
665
|
q.text("__END__")
|
|
630
666
|
q.breakable(force: true)
|
|
631
|
-
|
|
667
|
+
|
|
668
|
+
separator = -> { q.breakable(indent: false, force: true) }
|
|
669
|
+
q.seplist(value.split(/\r?\n/, -1), separator) { |line| q.text(line) }
|
|
632
670
|
end
|
|
633
671
|
|
|
634
672
|
def pretty_print(q)
|
|
@@ -654,7 +692,7 @@ class SyntaxTree < Ripper
|
|
|
654
692
|
def on___end__(value)
|
|
655
693
|
@__end__ =
|
|
656
694
|
EndContent.new(
|
|
657
|
-
value:
|
|
695
|
+
value: source[(char_pos + value.length)..-1],
|
|
658
696
|
location: Location.token(line: lineno, char: char_pos, size: value.size)
|
|
659
697
|
)
|
|
660
698
|
end
|
|
@@ -1349,7 +1387,11 @@ class SyntaxTree < Ripper
|
|
|
1349
1387
|
q.indent do
|
|
1350
1388
|
q.breakable("")
|
|
1351
1389
|
q.seplist(contents.parts, -> { q.breakable }) do |part|
|
|
1352
|
-
|
|
1390
|
+
if part.is_a?(StringLiteral)
|
|
1391
|
+
q.format(part.parts.first)
|
|
1392
|
+
else
|
|
1393
|
+
q.text(part.value[1..-1])
|
|
1394
|
+
end
|
|
1353
1395
|
end
|
|
1354
1396
|
end
|
|
1355
1397
|
q.breakable("")
|
|
@@ -1373,10 +1415,39 @@ class SyntaxTree < Ripper
|
|
|
1373
1415
|
q.format(part.value)
|
|
1374
1416
|
end
|
|
1375
1417
|
end
|
|
1418
|
+
q.breakable("")
|
|
1419
|
+
end
|
|
1420
|
+
end
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
class VarRefsFormatter
|
|
1424
|
+
# [Args] the contents of the array
|
|
1425
|
+
attr_reader :contents
|
|
1426
|
+
|
|
1427
|
+
def initialize(contents)
|
|
1428
|
+
@contents = contents
|
|
1429
|
+
end
|
|
1430
|
+
|
|
1431
|
+
def format(q)
|
|
1432
|
+
q.group(0, "[", "]") do
|
|
1433
|
+
q.indent do
|
|
1434
|
+
q.breakable("")
|
|
1435
|
+
|
|
1436
|
+
separator = -> do
|
|
1437
|
+
q.text(",")
|
|
1438
|
+
q.fill_breakable
|
|
1439
|
+
end
|
|
1440
|
+
|
|
1441
|
+
q.seplist(contents.parts, separator) { |part| q.format(part) }
|
|
1442
|
+
end
|
|
1443
|
+
q.breakable("")
|
|
1376
1444
|
end
|
|
1377
1445
|
end
|
|
1378
1446
|
end
|
|
1379
1447
|
|
|
1448
|
+
# [LBracket] the bracket that opens this array
|
|
1449
|
+
attr_reader :lbracket
|
|
1450
|
+
|
|
1380
1451
|
# [nil | Args] the contents of the array
|
|
1381
1452
|
attr_reader :contents
|
|
1382
1453
|
|
|
@@ -1386,22 +1457,18 @@ class SyntaxTree < Ripper
|
|
|
1386
1457
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
1387
1458
|
attr_reader :comments
|
|
1388
1459
|
|
|
1389
|
-
def initialize(contents:, location:, comments: [])
|
|
1460
|
+
def initialize(lbracket:, contents:, location:, comments: [])
|
|
1461
|
+
@lbracket = lbracket
|
|
1390
1462
|
@contents = contents
|
|
1391
1463
|
@location = location
|
|
1392
1464
|
@comments = comments
|
|
1393
1465
|
end
|
|
1394
1466
|
|
|
1395
1467
|
def child_nodes
|
|
1396
|
-
[contents]
|
|
1468
|
+
[lbracket, contents]
|
|
1397
1469
|
end
|
|
1398
1470
|
|
|
1399
1471
|
def format(q)
|
|
1400
|
-
unless contents
|
|
1401
|
-
q.text("[]")
|
|
1402
|
-
return
|
|
1403
|
-
end
|
|
1404
|
-
|
|
1405
1472
|
if qwords?
|
|
1406
1473
|
QWordsFormatter.new(contents).format(q)
|
|
1407
1474
|
return
|
|
@@ -1412,12 +1479,23 @@ class SyntaxTree < Ripper
|
|
|
1412
1479
|
return
|
|
1413
1480
|
end
|
|
1414
1481
|
|
|
1415
|
-
q
|
|
1416
|
-
q
|
|
1417
|
-
|
|
1418
|
-
|
|
1482
|
+
if var_refs?(q)
|
|
1483
|
+
VarRefsFormatter.new(contents).format(q)
|
|
1484
|
+
return
|
|
1485
|
+
end
|
|
1486
|
+
|
|
1487
|
+
q.group do
|
|
1488
|
+
q.format(lbracket)
|
|
1489
|
+
|
|
1490
|
+
if contents
|
|
1491
|
+
q.indent do
|
|
1492
|
+
q.breakable("")
|
|
1493
|
+
q.format(contents)
|
|
1494
|
+
end
|
|
1419
1495
|
end
|
|
1496
|
+
|
|
1420
1497
|
q.breakable("")
|
|
1498
|
+
q.text("]")
|
|
1421
1499
|
end
|
|
1422
1500
|
end
|
|
1423
1501
|
|
|
@@ -1441,21 +1519,40 @@ class SyntaxTree < Ripper
|
|
|
1441
1519
|
private
|
|
1442
1520
|
|
|
1443
1521
|
def qwords?
|
|
1444
|
-
|
|
1522
|
+
lbracket.comments.empty? && contents && contents.comments.empty? &&
|
|
1523
|
+
contents.parts.length > 1 &&
|
|
1445
1524
|
contents.parts.all? do |part|
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
part.
|
|
1449
|
-
|
|
1525
|
+
case part
|
|
1526
|
+
when StringLiteral
|
|
1527
|
+
part.comments.empty? && part.parts.length == 1 &&
|
|
1528
|
+
part.parts.first.is_a?(TStringContent) &&
|
|
1529
|
+
!part.parts.first.value.match?(/[\s\[\]\\]/)
|
|
1530
|
+
when CHAR
|
|
1531
|
+
!part.value.match?(/[\[\]\\]/)
|
|
1532
|
+
else
|
|
1533
|
+
false
|
|
1534
|
+
end
|
|
1450
1535
|
end
|
|
1451
1536
|
end
|
|
1452
1537
|
|
|
1453
1538
|
def qsymbols?
|
|
1454
|
-
|
|
1539
|
+
lbracket.comments.empty? && contents && contents.comments.empty? &&
|
|
1540
|
+
contents.parts.length > 1 &&
|
|
1455
1541
|
contents.parts.all? do |part|
|
|
1456
1542
|
part.is_a?(SymbolLiteral) && part.comments.empty?
|
|
1457
1543
|
end
|
|
1458
1544
|
end
|
|
1545
|
+
|
|
1546
|
+
def var_refs?(q)
|
|
1547
|
+
lbracket.comments.empty? && contents && contents.comments.empty? &&
|
|
1548
|
+
contents.parts.all? do |part|
|
|
1549
|
+
part.is_a?(VarRef) && part.comments.empty?
|
|
1550
|
+
end &&
|
|
1551
|
+
(
|
|
1552
|
+
contents.parts.sum { |part| part.value.value.length + 2 } >
|
|
1553
|
+
q.maxwidth * 2
|
|
1554
|
+
)
|
|
1555
|
+
end
|
|
1459
1556
|
end
|
|
1460
1557
|
|
|
1461
1558
|
# :call-seq:
|
|
@@ -1467,13 +1564,16 @@ class SyntaxTree < Ripper
|
|
|
1467
1564
|
rbracket = find_token(RBracket)
|
|
1468
1565
|
|
|
1469
1566
|
ArrayLiteral.new(
|
|
1567
|
+
lbracket: lbracket,
|
|
1470
1568
|
contents: contents,
|
|
1471
1569
|
location: lbracket.location.to(rbracket.location)
|
|
1472
1570
|
)
|
|
1473
1571
|
else
|
|
1474
|
-
tstring_end =
|
|
1572
|
+
tstring_end =
|
|
1573
|
+
find_token(TStringEnd, location: contents.beginning.location)
|
|
1475
1574
|
|
|
1476
1575
|
contents.class.new(
|
|
1576
|
+
beginning: contents.beginning,
|
|
1477
1577
|
elements: contents.elements,
|
|
1478
1578
|
location: contents.location.to(tstring_end.location)
|
|
1479
1579
|
)
|
|
@@ -1571,7 +1671,7 @@ class SyntaxTree < Ripper
|
|
|
1571
1671
|
end
|
|
1572
1672
|
|
|
1573
1673
|
parent = q.parent
|
|
1574
|
-
if parts.length == 1 || PATTERNS.
|
|
1674
|
+
if parts.length == 1 || PATTERNS.include?(parent.class)
|
|
1575
1675
|
q.text("[")
|
|
1576
1676
|
q.seplist(parts) { |part| q.format(part) }
|
|
1577
1677
|
q.text("]")
|
|
@@ -1678,7 +1778,7 @@ class SyntaxTree < Ripper
|
|
|
1678
1778
|
q.format(target)
|
|
1679
1779
|
q.text(" =")
|
|
1680
1780
|
|
|
1681
|
-
if
|
|
1781
|
+
if target.comments.empty? && (skip_indent_target? || skip_indent_value?)
|
|
1682
1782
|
q.text(" ")
|
|
1683
1783
|
q.format(value)
|
|
1684
1784
|
else
|
|
@@ -1716,11 +1816,21 @@ class SyntaxTree < Ripper
|
|
|
1716
1816
|
|
|
1717
1817
|
private
|
|
1718
1818
|
|
|
1719
|
-
def
|
|
1720
|
-
target.is_a?(ARefField)
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1819
|
+
def skip_indent_target?
|
|
1820
|
+
target.is_a?(ARefField)
|
|
1821
|
+
end
|
|
1822
|
+
|
|
1823
|
+
def skip_indent_value?
|
|
1824
|
+
[
|
|
1825
|
+
ArrayLiteral,
|
|
1826
|
+
HashLiteral,
|
|
1827
|
+
Heredoc,
|
|
1828
|
+
Lambda,
|
|
1829
|
+
QSymbols,
|
|
1830
|
+
QWords,
|
|
1831
|
+
Symbols,
|
|
1832
|
+
Words
|
|
1833
|
+
].any? { |type| value.is_a?(type) }
|
|
1724
1834
|
end
|
|
1725
1835
|
end
|
|
1726
1836
|
|
|
@@ -2252,11 +2362,14 @@ class SyntaxTree < Ripper
|
|
|
2252
2362
|
q.group do
|
|
2253
2363
|
q.group { q.format(left) }
|
|
2254
2364
|
q.text(" ") unless power
|
|
2255
|
-
q.text(operator)
|
|
2256
2365
|
|
|
2257
|
-
q.
|
|
2258
|
-
q.
|
|
2259
|
-
|
|
2366
|
+
q.group do
|
|
2367
|
+
q.text(operator)
|
|
2368
|
+
|
|
2369
|
+
q.indent do
|
|
2370
|
+
q.breakable(power ? "" : " ")
|
|
2371
|
+
q.format(right)
|
|
2372
|
+
end
|
|
2260
2373
|
end
|
|
2261
2374
|
end
|
|
2262
2375
|
end
|
|
@@ -2685,54 +2798,132 @@ class SyntaxTree < Ripper
|
|
|
2685
2798
|
# [LBrace | Keyword] the node that opens the block
|
|
2686
2799
|
attr_reader :block_open
|
|
2687
2800
|
|
|
2801
|
+
# [String] the string that closes the block
|
|
2802
|
+
attr_reader :block_close
|
|
2803
|
+
|
|
2688
2804
|
# [BodyStmt | Statements] the statements inside the block
|
|
2689
2805
|
attr_reader :statements
|
|
2690
2806
|
|
|
2691
|
-
def initialize(node, block_open, statements)
|
|
2807
|
+
def initialize(node, block_open, block_close, statements)
|
|
2692
2808
|
@node = node
|
|
2693
2809
|
@block_open = block_open
|
|
2810
|
+
@block_close = block_close
|
|
2694
2811
|
@statements = statements
|
|
2695
2812
|
end
|
|
2696
2813
|
|
|
2697
2814
|
def format(q)
|
|
2815
|
+
# If this is nested anywhere inside of a Command or CommandCall node, then
|
|
2816
|
+
# we can't change which operators we're using for the bounds of the block.
|
|
2817
|
+
break_opening, break_closing, flat_opening, flat_closing =
|
|
2818
|
+
if unchangeable_bounds?(q)
|
|
2819
|
+
[block_open.value, block_close, block_open.value, block_close]
|
|
2820
|
+
elsif forced_do_end_bounds?(q)
|
|
2821
|
+
%w[do end do end]
|
|
2822
|
+
elsif forced_brace_bounds?(q)
|
|
2823
|
+
%w[{ } { }]
|
|
2824
|
+
else
|
|
2825
|
+
%w[do end { }]
|
|
2826
|
+
end
|
|
2827
|
+
|
|
2828
|
+
# If the receiver of this block a Command or CommandCall node, then there
|
|
2829
|
+
# are no parentheses around the arguments to that command, so we need to
|
|
2830
|
+
# break the block.
|
|
2831
|
+
receiver = q.parent.call
|
|
2832
|
+
if receiver.is_a?(Command) || receiver.is_a?(CommandCall)
|
|
2833
|
+
q.break_parent
|
|
2834
|
+
format_break(q, break_opening, break_closing)
|
|
2835
|
+
return
|
|
2836
|
+
end
|
|
2837
|
+
|
|
2698
2838
|
q.group do
|
|
2699
|
-
q.
|
|
2839
|
+
q.if_break { format_break(q, break_opening, break_closing) }.if_flat do
|
|
2840
|
+
format_flat(q, flat_opening, flat_closing)
|
|
2841
|
+
end
|
|
2842
|
+
end
|
|
2843
|
+
end
|
|
2700
2844
|
|
|
2701
|
-
|
|
2702
|
-
q.format(BlockOpenFormatter.new("do", block_open))
|
|
2845
|
+
private
|
|
2703
2846
|
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2847
|
+
# If this is nested anywhere inside certain nodes, then we can't change
|
|
2848
|
+
# which operators/keywords we're using for the bounds of the block.
|
|
2849
|
+
def unchangeable_bounds?(q)
|
|
2850
|
+
q.parents.any? do |parent|
|
|
2851
|
+
# If we hit a statements, then we're safe to use whatever since we
|
|
2852
|
+
# know for certain we're going to get split over multiple lines
|
|
2853
|
+
# anyway.
|
|
2854
|
+
break false if parent.is_a?(Statements)
|
|
2708
2855
|
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
q.format(statements)
|
|
2713
|
-
end
|
|
2714
|
-
end
|
|
2856
|
+
[Command, CommandCall].include?(parent.class)
|
|
2857
|
+
end
|
|
2858
|
+
end
|
|
2715
2859
|
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2860
|
+
# If we're a sibling of a control-flow keyword, then we're going to have to
|
|
2861
|
+
# use the do..end bounds.
|
|
2862
|
+
def forced_do_end_bounds?(q)
|
|
2863
|
+
[Break, Next, Return, Super].include?(q.parent.call.class)
|
|
2864
|
+
end
|
|
2720
2865
|
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2866
|
+
# If we're the predicate of a loop or conditional, then we're going to have
|
|
2867
|
+
# to go with the {..} bounds.
|
|
2868
|
+
def forced_brace_bounds?(q)
|
|
2869
|
+
parents = q.parents.to_a
|
|
2870
|
+
parents.each_with_index.any? do |parent, index|
|
|
2871
|
+
# If we hit certain breakpoints then we know we're safe.
|
|
2872
|
+
break false if [Paren, Statements].include?(parent.class)
|
|
2726
2873
|
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2874
|
+
[
|
|
2875
|
+
If,
|
|
2876
|
+
IfMod,
|
|
2877
|
+
IfOp,
|
|
2878
|
+
Unless,
|
|
2879
|
+
UnlessMod,
|
|
2880
|
+
While,
|
|
2881
|
+
WhileMod,
|
|
2882
|
+
Until,
|
|
2883
|
+
UntilMod
|
|
2884
|
+
].include?(parent.class) && parent.predicate == parents[index - 1]
|
|
2885
|
+
end
|
|
2886
|
+
end
|
|
2732
2887
|
|
|
2733
|
-
|
|
2888
|
+
def format_break(q, opening, closing)
|
|
2889
|
+
q.text(" ")
|
|
2890
|
+
q.format(BlockOpenFormatter.new(opening, block_open))
|
|
2891
|
+
|
|
2892
|
+
if node.block_var
|
|
2893
|
+
q.text(" ")
|
|
2894
|
+
q.format(node.block_var)
|
|
2895
|
+
end
|
|
2896
|
+
|
|
2897
|
+
unless statements.empty?
|
|
2898
|
+
q.indent do
|
|
2899
|
+
q.breakable
|
|
2900
|
+
q.format(statements)
|
|
2734
2901
|
end
|
|
2735
2902
|
end
|
|
2903
|
+
|
|
2904
|
+
q.breakable
|
|
2905
|
+
q.text(closing)
|
|
2906
|
+
end
|
|
2907
|
+
|
|
2908
|
+
def format_flat(q, opening, closing)
|
|
2909
|
+
q.text(" ")
|
|
2910
|
+
q.format(BlockOpenFormatter.new(opening, block_open))
|
|
2911
|
+
|
|
2912
|
+
if node.block_var
|
|
2913
|
+
q.breakable
|
|
2914
|
+
q.format(node.block_var)
|
|
2915
|
+
q.breakable
|
|
2916
|
+
end
|
|
2917
|
+
|
|
2918
|
+
if statements.empty?
|
|
2919
|
+
q.text(" ") if opening == "do"
|
|
2920
|
+
else
|
|
2921
|
+
q.breakable unless node.block_var
|
|
2922
|
+
q.format(statements)
|
|
2923
|
+
q.breakable
|
|
2924
|
+
end
|
|
2925
|
+
|
|
2926
|
+
q.text(closing)
|
|
2736
2927
|
end
|
|
2737
2928
|
end
|
|
2738
2929
|
|
|
@@ -2770,7 +2961,7 @@ class SyntaxTree < Ripper
|
|
|
2770
2961
|
end
|
|
2771
2962
|
|
|
2772
2963
|
def format(q)
|
|
2773
|
-
BlockFormatter.new(self, lbrace, statements).format(q)
|
|
2964
|
+
BlockFormatter.new(self, lbrace, "}", statements).format(q)
|
|
2774
2965
|
end
|
|
2775
2966
|
|
|
2776
2967
|
def pretty_print(q)
|
|
@@ -2851,15 +3042,35 @@ class SyntaxTree < Ripper
|
|
|
2851
3042
|
q.text(keyword)
|
|
2852
3043
|
|
|
2853
3044
|
if arguments.parts.any?
|
|
2854
|
-
if arguments.parts.length == 1
|
|
2855
|
-
|
|
3045
|
+
if arguments.parts.length == 1
|
|
3046
|
+
part = arguments.parts.first
|
|
3047
|
+
|
|
3048
|
+
if part.is_a?(Paren)
|
|
3049
|
+
q.format(arguments)
|
|
3050
|
+
elsif part.is_a?(ArrayLiteral)
|
|
3051
|
+
q.text(" ")
|
|
3052
|
+
q.format(arguments)
|
|
3053
|
+
else
|
|
3054
|
+
format_arguments(q, "(", ")")
|
|
3055
|
+
end
|
|
2856
3056
|
else
|
|
2857
|
-
q
|
|
2858
|
-
q.nest(keyword.length + 1) { q.format(arguments) }
|
|
3057
|
+
format_arguments(q, " [", "]")
|
|
2859
3058
|
end
|
|
2860
3059
|
end
|
|
2861
3060
|
end
|
|
2862
3061
|
end
|
|
3062
|
+
|
|
3063
|
+
private
|
|
3064
|
+
|
|
3065
|
+
def format_arguments(q, opening, closing)
|
|
3066
|
+
q.if_break { q.text(opening) }
|
|
3067
|
+
q.indent do
|
|
3068
|
+
q.breakable(" ")
|
|
3069
|
+
q.format(node.arguments)
|
|
3070
|
+
end
|
|
3071
|
+
q.breakable("")
|
|
3072
|
+
q.if_break { q.text(closing) }
|
|
3073
|
+
end
|
|
2863
3074
|
end
|
|
2864
3075
|
|
|
2865
3076
|
# Break represents using the +break+ keyword.
|
|
@@ -2946,9 +3157,7 @@ class SyntaxTree < Ripper
|
|
|
2946
3157
|
end
|
|
2947
3158
|
end
|
|
2948
3159
|
|
|
2949
|
-
# Call represents a method call.
|
|
2950
|
-
# passed (if arguments are passed, this node will get nested under a
|
|
2951
|
-
# MethodAddArg node).
|
|
3160
|
+
# Call represents a method call.
|
|
2952
3161
|
#
|
|
2953
3162
|
# receiver.message
|
|
2954
3163
|
#
|
|
@@ -2962,32 +3171,60 @@ class SyntaxTree < Ripper
|
|
|
2962
3171
|
# [:call | Backtick | Const | Ident | Op] the message being sent
|
|
2963
3172
|
attr_reader :message
|
|
2964
3173
|
|
|
3174
|
+
# [nil | ArgParen | Args] the arguments to the method call
|
|
3175
|
+
attr_reader :arguments
|
|
3176
|
+
|
|
2965
3177
|
# [Location] the location of this node
|
|
2966
3178
|
attr_reader :location
|
|
2967
3179
|
|
|
2968
3180
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
2969
3181
|
attr_reader :comments
|
|
2970
3182
|
|
|
2971
|
-
def initialize(
|
|
3183
|
+
def initialize(
|
|
3184
|
+
receiver:,
|
|
3185
|
+
operator:,
|
|
3186
|
+
message:,
|
|
3187
|
+
arguments:,
|
|
3188
|
+
location:,
|
|
3189
|
+
comments: []
|
|
3190
|
+
)
|
|
2972
3191
|
@receiver = receiver
|
|
2973
3192
|
@operator = operator
|
|
2974
3193
|
@message = message
|
|
3194
|
+
@arguments = arguments
|
|
2975
3195
|
@location = location
|
|
2976
3196
|
@comments = comments
|
|
2977
3197
|
end
|
|
2978
3198
|
|
|
2979
3199
|
def child_nodes
|
|
2980
|
-
[
|
|
3200
|
+
[
|
|
3201
|
+
receiver,
|
|
3202
|
+
(operator if operator != :"::"),
|
|
3203
|
+
(message if message != :call),
|
|
3204
|
+
arguments
|
|
3205
|
+
]
|
|
2981
3206
|
end
|
|
2982
3207
|
|
|
2983
3208
|
def format(q)
|
|
3209
|
+
call_operator = CallOperatorFormatter.new(operator)
|
|
3210
|
+
|
|
2984
3211
|
q.group do
|
|
2985
3212
|
q.format(receiver)
|
|
3213
|
+
|
|
3214
|
+
# If there are trailing comments on the call operator, then we need to
|
|
3215
|
+
# use the trailing form as opposed to the leading form.
|
|
3216
|
+
q.format(call_operator) if call_operator.comments.any?
|
|
3217
|
+
|
|
2986
3218
|
q.group do
|
|
2987
3219
|
q.indent do
|
|
2988
|
-
|
|
3220
|
+
if receiver.comments.any? || call_operator.comments.any?
|
|
3221
|
+
q.breakable(force: true)
|
|
3222
|
+
end
|
|
3223
|
+
q.format(call_operator) if call_operator.comments.empty?
|
|
2989
3224
|
q.format(message) if message != :call
|
|
2990
3225
|
end
|
|
3226
|
+
|
|
3227
|
+
q.format(arguments) if arguments
|
|
2991
3228
|
end
|
|
2992
3229
|
end
|
|
2993
3230
|
end
|
|
@@ -3005,6 +3242,11 @@ class SyntaxTree < Ripper
|
|
|
3005
3242
|
q.breakable
|
|
3006
3243
|
q.pp(message)
|
|
3007
3244
|
|
|
3245
|
+
if arguments
|
|
3246
|
+
q.breakable
|
|
3247
|
+
q.pp(arguments)
|
|
3248
|
+
end
|
|
3249
|
+
|
|
3008
3250
|
q.pp(Comment::List.new(comments))
|
|
3009
3251
|
end
|
|
3010
3252
|
end
|
|
@@ -3015,6 +3257,7 @@ class SyntaxTree < Ripper
|
|
|
3015
3257
|
receiver: receiver,
|
|
3016
3258
|
op: operator,
|
|
3017
3259
|
message: message,
|
|
3260
|
+
args: arguments,
|
|
3018
3261
|
loc: location,
|
|
3019
3262
|
cmts: comments
|
|
3020
3263
|
}.to_json(*opts)
|
|
@@ -3035,13 +3278,8 @@ class SyntaxTree < Ripper
|
|
|
3035
3278
|
receiver: receiver,
|
|
3036
3279
|
operator: operator,
|
|
3037
3280
|
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
|
-
)
|
|
3281
|
+
arguments: nil,
|
|
3282
|
+
location: receiver.location.to(ending.location)
|
|
3045
3283
|
)
|
|
3046
3284
|
end
|
|
3047
3285
|
|
|
@@ -3057,6 +3295,9 @@ class SyntaxTree < Ripper
|
|
|
3057
3295
|
# end
|
|
3058
3296
|
#
|
|
3059
3297
|
class Case
|
|
3298
|
+
# [Kw] the keyword that opens this expression
|
|
3299
|
+
attr_reader :keyword
|
|
3300
|
+
|
|
3060
3301
|
# [nil | untyped] optional value being switched on
|
|
3061
3302
|
attr_reader :value
|
|
3062
3303
|
|
|
@@ -3069,7 +3310,8 @@ class SyntaxTree < Ripper
|
|
|
3069
3310
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
3070
3311
|
attr_reader :comments
|
|
3071
3312
|
|
|
3072
|
-
def initialize(value:, consequent:, location:, comments: [])
|
|
3313
|
+
def initialize(keyword:, value:, consequent:, location:, comments: [])
|
|
3314
|
+
@keyword = keyword
|
|
3073
3315
|
@value = value
|
|
3074
3316
|
@consequent = consequent
|
|
3075
3317
|
@location = location
|
|
@@ -3077,11 +3319,13 @@ class SyntaxTree < Ripper
|
|
|
3077
3319
|
end
|
|
3078
3320
|
|
|
3079
3321
|
def child_nodes
|
|
3080
|
-
[value, consequent]
|
|
3322
|
+
[keyword, value, consequent]
|
|
3081
3323
|
end
|
|
3082
3324
|
|
|
3083
3325
|
def format(q)
|
|
3084
|
-
q.group
|
|
3326
|
+
q.group do
|
|
3327
|
+
q.format(keyword)
|
|
3328
|
+
|
|
3085
3329
|
if value
|
|
3086
3330
|
q.text(" ")
|
|
3087
3331
|
q.format(value)
|
|
@@ -3090,6 +3334,8 @@ class SyntaxTree < Ripper
|
|
|
3090
3334
|
q.breakable(force: true)
|
|
3091
3335
|
q.format(consequent)
|
|
3092
3336
|
q.breakable(force: true)
|
|
3337
|
+
|
|
3338
|
+
q.text("end")
|
|
3093
3339
|
end
|
|
3094
3340
|
end
|
|
3095
3341
|
|
|
@@ -3097,6 +3343,9 @@ class SyntaxTree < Ripper
|
|
|
3097
3343
|
q.group(2, "(", ")") do
|
|
3098
3344
|
q.text("case")
|
|
3099
3345
|
|
|
3346
|
+
q.breakable
|
|
3347
|
+
q.pp(keyword)
|
|
3348
|
+
|
|
3100
3349
|
if value
|
|
3101
3350
|
q.breakable
|
|
3102
3351
|
q.pp(value)
|
|
@@ -3204,6 +3453,7 @@ class SyntaxTree < Ripper
|
|
|
3204
3453
|
tokens.delete(keyword)
|
|
3205
3454
|
|
|
3206
3455
|
Case.new(
|
|
3456
|
+
keyword: keyword,
|
|
3207
3457
|
value: value,
|
|
3208
3458
|
consequent: consequent,
|
|
3209
3459
|
location: keyword.location.to(consequent.location)
|
|
@@ -3484,7 +3734,7 @@ class SyntaxTree < Ripper
|
|
|
3484
3734
|
# [Const | Ident | Op] the message being send
|
|
3485
3735
|
attr_reader :message
|
|
3486
3736
|
|
|
3487
|
-
# [Args] the arguments going along with the message
|
|
3737
|
+
# [nil | Args] the arguments going along with the message
|
|
3488
3738
|
attr_reader :arguments
|
|
3489
3739
|
|
|
3490
3740
|
# [Location] the location of this node
|
|
@@ -3515,16 +3765,22 @@ class SyntaxTree < Ripper
|
|
|
3515
3765
|
|
|
3516
3766
|
def format(q)
|
|
3517
3767
|
q.group do
|
|
3518
|
-
doc =
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3768
|
+
doc =
|
|
3769
|
+
q.nest(0) do
|
|
3770
|
+
q.format(receiver)
|
|
3771
|
+
q.format(CallOperatorFormatter.new(operator))
|
|
3772
|
+
q.format(message)
|
|
3773
|
+
end
|
|
3522
3774
|
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
q.
|
|
3526
|
-
|
|
3527
|
-
|
|
3775
|
+
if arguments
|
|
3776
|
+
width = doc_width(doc) + 1
|
|
3777
|
+
q.text(" ")
|
|
3778
|
+
|
|
3779
|
+
if width > (q.maxwidth / 2)
|
|
3780
|
+
q.format(arguments)
|
|
3781
|
+
else
|
|
3782
|
+
q.nest(width) { q.format(arguments) }
|
|
3783
|
+
end
|
|
3528
3784
|
end
|
|
3529
3785
|
end
|
|
3530
3786
|
end
|
|
@@ -3542,8 +3798,10 @@ class SyntaxTree < Ripper
|
|
|
3542
3798
|
q.breakable
|
|
3543
3799
|
q.pp(message)
|
|
3544
3800
|
|
|
3545
|
-
|
|
3546
|
-
|
|
3801
|
+
if arguments
|
|
3802
|
+
q.breakable
|
|
3803
|
+
q.pp(arguments)
|
|
3804
|
+
end
|
|
3547
3805
|
|
|
3548
3806
|
q.pp(Comment::List.new(comments))
|
|
3549
3807
|
end
|
|
@@ -3577,11 +3835,11 @@ class SyntaxTree < Ripper
|
|
|
3577
3835
|
when PrettyPrint::Text
|
|
3578
3836
|
width += doc.width
|
|
3579
3837
|
when PrettyPrint::Indent, PrettyPrint::Align, PrettyPrint::Group
|
|
3580
|
-
queue
|
|
3838
|
+
queue = doc.contents + queue
|
|
3581
3839
|
when PrettyPrint::IfBreak
|
|
3582
|
-
queue
|
|
3840
|
+
queue = doc.break_contents + queue
|
|
3583
3841
|
when PrettyPrint::Breakable
|
|
3584
|
-
width =
|
|
3842
|
+
width = 0
|
|
3585
3843
|
end
|
|
3586
3844
|
end
|
|
3587
3845
|
|
|
@@ -3594,7 +3852,7 @@ class SyntaxTree < Ripper
|
|
|
3594
3852
|
# untyped receiver,
|
|
3595
3853
|
# (:"::" | Op | Period) operator,
|
|
3596
3854
|
# (Const | Ident | Op) message,
|
|
3597
|
-
# Args arguments
|
|
3855
|
+
# (nil | Args) arguments
|
|
3598
3856
|
# ) -> CommandCall
|
|
3599
3857
|
def on_command_call(receiver, operator, message, arguments)
|
|
3600
3858
|
ending = arguments || message
|
|
@@ -3665,6 +3923,10 @@ class SyntaxTree < Ripper
|
|
|
3665
3923
|
@trailing
|
|
3666
3924
|
end
|
|
3667
3925
|
|
|
3926
|
+
def ignore?
|
|
3927
|
+
value[1..-1].strip == "stree-ignore"
|
|
3928
|
+
end
|
|
3929
|
+
|
|
3668
3930
|
def comments
|
|
3669
3931
|
[]
|
|
3670
3932
|
end
|
|
@@ -4062,14 +4324,7 @@ class SyntaxTree < Ripper
|
|
|
4062
4324
|
q.group do
|
|
4063
4325
|
q.text("def ")
|
|
4064
4326
|
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
|
|
4327
|
+
q.format(params) if !params.is_a?(Params) || !params.empty?
|
|
4073
4328
|
end
|
|
4074
4329
|
|
|
4075
4330
|
unless bodystmt.empty?
|
|
@@ -4118,6 +4373,12 @@ class SyntaxTree < Ripper
|
|
|
4118
4373
|
# def method = result
|
|
4119
4374
|
#
|
|
4120
4375
|
class DefEndless
|
|
4376
|
+
# [untyped] the target where the method is being defined
|
|
4377
|
+
attr_reader :target
|
|
4378
|
+
|
|
4379
|
+
# [Op | Period] the operator being used to declare the method
|
|
4380
|
+
attr_reader :operator
|
|
4381
|
+
|
|
4121
4382
|
# [Backtick | Const | Ident | Kw | Op] the name of the method
|
|
4122
4383
|
attr_reader :name
|
|
4123
4384
|
|
|
@@ -4133,7 +4394,17 @@ class SyntaxTree < Ripper
|
|
|
4133
4394
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
4134
4395
|
attr_reader :comments
|
|
4135
4396
|
|
|
4136
|
-
def initialize(
|
|
4397
|
+
def initialize(
|
|
4398
|
+
target:,
|
|
4399
|
+
operator:,
|
|
4400
|
+
name:,
|
|
4401
|
+
paren:,
|
|
4402
|
+
statement:,
|
|
4403
|
+
location:,
|
|
4404
|
+
comments: []
|
|
4405
|
+
)
|
|
4406
|
+
@target = target
|
|
4407
|
+
@operator = operator
|
|
4137
4408
|
@name = name
|
|
4138
4409
|
@paren = paren
|
|
4139
4410
|
@statement = statement
|
|
@@ -4142,14 +4413,21 @@ class SyntaxTree < Ripper
|
|
|
4142
4413
|
end
|
|
4143
4414
|
|
|
4144
4415
|
def child_nodes
|
|
4145
|
-
[name, paren, statement]
|
|
4416
|
+
[target, operator, name, paren, statement]
|
|
4146
4417
|
end
|
|
4147
4418
|
|
|
4148
4419
|
def format(q)
|
|
4149
4420
|
q.group do
|
|
4150
4421
|
q.text("def ")
|
|
4422
|
+
|
|
4423
|
+
if target
|
|
4424
|
+
q.format(target)
|
|
4425
|
+
q.format(CallOperatorFormatter.new(operator))
|
|
4426
|
+
end
|
|
4427
|
+
|
|
4151
4428
|
q.format(name)
|
|
4152
4429
|
q.format(paren) if paren && !paren.contents.empty?
|
|
4430
|
+
|
|
4153
4431
|
q.text(" =")
|
|
4154
4432
|
q.group do
|
|
4155
4433
|
q.indent do
|
|
@@ -4164,6 +4442,14 @@ class SyntaxTree < Ripper
|
|
|
4164
4442
|
q.group(2, "(", ")") do
|
|
4165
4443
|
q.text("def_endless")
|
|
4166
4444
|
|
|
4445
|
+
if target
|
|
4446
|
+
q.breakable
|
|
4447
|
+
q.pp(target)
|
|
4448
|
+
|
|
4449
|
+
q.breakable
|
|
4450
|
+
q.pp(operator)
|
|
4451
|
+
end
|
|
4452
|
+
|
|
4167
4453
|
q.breakable
|
|
4168
4454
|
q.pp(name)
|
|
4169
4455
|
|
|
@@ -4210,6 +4496,8 @@ class SyntaxTree < Ripper
|
|
|
4210
4496
|
unless bodystmt.is_a?(BodyStmt)
|
|
4211
4497
|
node =
|
|
4212
4498
|
DefEndless.new(
|
|
4499
|
+
target: nil,
|
|
4500
|
+
operator: nil,
|
|
4213
4501
|
name: name,
|
|
4214
4502
|
paren: params,
|
|
4215
4503
|
statement: bodystmt,
|
|
@@ -4371,14 +4659,7 @@ class SyntaxTree < Ripper
|
|
|
4371
4659
|
q.format(target)
|
|
4372
4660
|
q.format(CallOperatorFormatter.new(operator))
|
|
4373
4661
|
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
|
|
4662
|
+
q.format(params) if !params.is_a?(Params) || !params.empty?
|
|
4382
4663
|
end
|
|
4383
4664
|
|
|
4384
4665
|
unless bodystmt.empty?
|
|
@@ -4460,6 +4741,22 @@ class SyntaxTree < Ripper
|
|
|
4460
4741
|
end
|
|
4461
4742
|
|
|
4462
4743
|
beginning = find_token(Kw, "def")
|
|
4744
|
+
|
|
4745
|
+
# If we don't have a bodystmt node, then we have a single-line method
|
|
4746
|
+
unless bodystmt.is_a?(BodyStmt)
|
|
4747
|
+
node =
|
|
4748
|
+
DefEndless.new(
|
|
4749
|
+
target: target,
|
|
4750
|
+
operator: operator,
|
|
4751
|
+
name: name,
|
|
4752
|
+
paren: params,
|
|
4753
|
+
statement: bodystmt,
|
|
4754
|
+
location: beginning.location.to(bodystmt.location)
|
|
4755
|
+
)
|
|
4756
|
+
|
|
4757
|
+
return node
|
|
4758
|
+
end
|
|
4759
|
+
|
|
4463
4760
|
ending = find_token(Kw, "end")
|
|
4464
4761
|
|
|
4465
4762
|
bodystmt.bind(
|
|
@@ -4512,7 +4809,7 @@ class SyntaxTree < Ripper
|
|
|
4512
4809
|
end
|
|
4513
4810
|
|
|
4514
4811
|
def format(q)
|
|
4515
|
-
BlockFormatter.new(self, keyword, bodystmt).format(q)
|
|
4812
|
+
BlockFormatter.new(self, keyword, "end", bodystmt).format(q)
|
|
4516
4813
|
end
|
|
4517
4814
|
|
|
4518
4815
|
def pretty_print(q)
|
|
@@ -4576,8 +4873,7 @@ class SyntaxTree < Ripper
|
|
|
4576
4873
|
end
|
|
4577
4874
|
|
|
4578
4875
|
def format(q)
|
|
4579
|
-
|
|
4580
|
-
space = parent.is_a?(If) || parent.is_a?(Unless)
|
|
4876
|
+
space = [If, IfMod, Unless, UnlessMod].include?(q.parent.class)
|
|
4581
4877
|
|
|
4582
4878
|
left = node.left
|
|
4583
4879
|
right = node.right
|
|
@@ -4890,9 +5186,9 @@ class SyntaxTree < Ripper
|
|
|
4890
5186
|
matching = Quotes.matching(quote[2])
|
|
4891
5187
|
pattern = /[\n#{Regexp.escape(matching)}'"]/
|
|
4892
5188
|
|
|
4893
|
-
if parts.any?
|
|
5189
|
+
if parts.any? { |part|
|
|
4894
5190
|
part.is_a?(TStringContent) && part.value.match?(pattern)
|
|
4895
|
-
|
|
5191
|
+
}
|
|
4896
5192
|
[quote, matching]
|
|
4897
5193
|
elsif Quotes.locked?(self)
|
|
4898
5194
|
["#{":" unless hash_key}'", "'"]
|
|
@@ -4917,7 +5213,7 @@ class SyntaxTree < Ripper
|
|
|
4917
5213
|
if find_token(SymBeg, consume: false)
|
|
4918
5214
|
# A normal dynamic symbol
|
|
4919
5215
|
symbeg = find_token(SymBeg)
|
|
4920
|
-
tstring_end = find_token(TStringEnd)
|
|
5216
|
+
tstring_end = find_token(TStringEnd, location: symbeg.location)
|
|
4921
5217
|
|
|
4922
5218
|
DynaSymbol.new(
|
|
4923
5219
|
quote: symbeg.value,
|
|
@@ -5009,6 +5305,7 @@ class SyntaxTree < Ripper
|
|
|
5009
5305
|
|
|
5010
5306
|
node = tokens[index]
|
|
5011
5307
|
ending = node.value == "end" ? tokens.delete_at(index) : node
|
|
5308
|
+
# ending = node
|
|
5012
5309
|
|
|
5013
5310
|
statements.bind(beginning.location.end_char, ending.location.start_char)
|
|
5014
5311
|
|
|
@@ -5155,6 +5452,10 @@ class SyntaxTree < Ripper
|
|
|
5155
5452
|
false
|
|
5156
5453
|
end
|
|
5157
5454
|
|
|
5455
|
+
def ignore?
|
|
5456
|
+
false
|
|
5457
|
+
end
|
|
5458
|
+
|
|
5158
5459
|
def comments
|
|
5159
5460
|
[]
|
|
5160
5461
|
end
|
|
@@ -5480,24 +5781,29 @@ class SyntaxTree < Ripper
|
|
|
5480
5781
|
# [Const | Ident] the name of the method
|
|
5481
5782
|
attr_reader :value
|
|
5482
5783
|
|
|
5784
|
+
# [nil | ArgParen | Args] the arguments to the method call
|
|
5785
|
+
attr_reader :arguments
|
|
5786
|
+
|
|
5483
5787
|
# [Location] the location of this node
|
|
5484
5788
|
attr_reader :location
|
|
5485
5789
|
|
|
5486
5790
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
5487
5791
|
attr_reader :comments
|
|
5488
5792
|
|
|
5489
|
-
def initialize(value:, location:, comments: [])
|
|
5793
|
+
def initialize(value:, arguments:, location:, comments: [])
|
|
5490
5794
|
@value = value
|
|
5795
|
+
@arguments = arguments
|
|
5491
5796
|
@location = location
|
|
5492
5797
|
@comments = comments
|
|
5493
5798
|
end
|
|
5494
5799
|
|
|
5495
5800
|
def child_nodes
|
|
5496
|
-
[value]
|
|
5801
|
+
[value, arguments]
|
|
5497
5802
|
end
|
|
5498
5803
|
|
|
5499
5804
|
def format(q)
|
|
5500
5805
|
q.format(value)
|
|
5806
|
+
q.format(arguments)
|
|
5501
5807
|
end
|
|
5502
5808
|
|
|
5503
5809
|
def pretty_print(q)
|
|
@@ -5507,21 +5813,30 @@ class SyntaxTree < Ripper
|
|
|
5507
5813
|
q.breakable
|
|
5508
5814
|
q.pp(value)
|
|
5509
5815
|
|
|
5816
|
+
if arguments
|
|
5817
|
+
q.breakable
|
|
5818
|
+
q.pp(arguments)
|
|
5819
|
+
end
|
|
5820
|
+
|
|
5510
5821
|
q.pp(Comment::List.new(comments))
|
|
5511
5822
|
end
|
|
5512
5823
|
end
|
|
5513
5824
|
|
|
5514
5825
|
def to_json(*opts)
|
|
5515
|
-
{
|
|
5516
|
-
|
|
5517
|
-
|
|
5826
|
+
{
|
|
5827
|
+
type: :fcall,
|
|
5828
|
+
value: value,
|
|
5829
|
+
args: arguments,
|
|
5830
|
+
loc: location,
|
|
5831
|
+
cmts: comments
|
|
5832
|
+
}.to_json(*opts)
|
|
5518
5833
|
end
|
|
5519
5834
|
end
|
|
5520
5835
|
|
|
5521
5836
|
# :call-seq:
|
|
5522
5837
|
# on_fcall: ((Const | Ident) value) -> FCall
|
|
5523
5838
|
def on_fcall(value)
|
|
5524
|
-
FCall.new(value: value, location: value.location)
|
|
5839
|
+
FCall.new(value: value, arguments: nil, location: value.location)
|
|
5525
5840
|
end
|
|
5526
5841
|
|
|
5527
5842
|
# Field is always the child of an assignment. It represents assigning to a
|
|
@@ -5864,6 +6179,7 @@ class SyntaxTree < Ripper
|
|
|
5864
6179
|
# ) -> For
|
|
5865
6180
|
def on_for(index, collection, statements)
|
|
5866
6181
|
beginning = find_token(Kw, "for")
|
|
6182
|
+
in_keyword = find_token(Kw, "in")
|
|
5867
6183
|
ending = find_token(Kw, "end")
|
|
5868
6184
|
|
|
5869
6185
|
# Consume the do keyword if it exists so that it doesn't get confused for
|
|
@@ -5879,6 +6195,11 @@ class SyntaxTree < Ripper
|
|
|
5879
6195
|
ending.location.start_char
|
|
5880
6196
|
)
|
|
5881
6197
|
|
|
6198
|
+
if index.is_a?(MLHS)
|
|
6199
|
+
comma_range = index.location.end_char...in_keyword.location.start_char
|
|
6200
|
+
index.comma = true if source[comma_range].strip.start_with?(",")
|
|
6201
|
+
end
|
|
6202
|
+
|
|
5882
6203
|
For.new(
|
|
5883
6204
|
index: index,
|
|
5884
6205
|
collection: collection,
|
|
@@ -5947,6 +6268,9 @@ class SyntaxTree < Ripper
|
|
|
5947
6268
|
# { key => value }
|
|
5948
6269
|
#
|
|
5949
6270
|
class HashLiteral
|
|
6271
|
+
# [LBrace] the left brace that opens this hash
|
|
6272
|
+
attr_reader :lbrace
|
|
6273
|
+
|
|
5950
6274
|
# [Array[ AssocNew | AssocSplat ]] the optional contents of the hash
|
|
5951
6275
|
attr_reader :assocs
|
|
5952
6276
|
|
|
@@ -5956,24 +6280,31 @@ class SyntaxTree < Ripper
|
|
|
5956
6280
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
5957
6281
|
attr_reader :comments
|
|
5958
6282
|
|
|
5959
|
-
def initialize(assocs:, location:, comments: [])
|
|
6283
|
+
def initialize(lbrace:, assocs:, location:, comments: [])
|
|
6284
|
+
@lbrace = lbrace
|
|
5960
6285
|
@assocs = assocs
|
|
5961
6286
|
@location = location
|
|
5962
6287
|
@comments = comments
|
|
5963
6288
|
end
|
|
5964
6289
|
|
|
5965
6290
|
def child_nodes
|
|
5966
|
-
assocs
|
|
6291
|
+
[lbrace] + assocs
|
|
5967
6292
|
end
|
|
5968
6293
|
|
|
5969
6294
|
def format(q)
|
|
5970
6295
|
contents = -> do
|
|
5971
|
-
q.
|
|
5972
|
-
|
|
6296
|
+
q.format(lbrace)
|
|
6297
|
+
|
|
6298
|
+
if assocs.empty?
|
|
6299
|
+
q.breakable("")
|
|
6300
|
+
else
|
|
6301
|
+
q.indent do
|
|
6302
|
+
q.breakable
|
|
6303
|
+
q.format(HashFormatter.for(self))
|
|
6304
|
+
end
|
|
5973
6305
|
q.breakable
|
|
5974
|
-
q.format(HashFormatter.for(self))
|
|
5975
6306
|
end
|
|
5976
|
-
|
|
6307
|
+
|
|
5977
6308
|
q.text("}")
|
|
5978
6309
|
end
|
|
5979
6310
|
|
|
@@ -6007,6 +6338,7 @@ class SyntaxTree < Ripper
|
|
|
6007
6338
|
rbrace = find_token(RBrace)
|
|
6008
6339
|
|
|
6009
6340
|
HashLiteral.new(
|
|
6341
|
+
lbrace: lbrace,
|
|
6010
6342
|
assocs: assocs || [],
|
|
6011
6343
|
location: lbrace.location.to(rbrace.location)
|
|
6012
6344
|
)
|
|
@@ -6059,7 +6391,7 @@ class SyntaxTree < Ripper
|
|
|
6059
6391
|
q.group do
|
|
6060
6392
|
q.format(beginning)
|
|
6061
6393
|
|
|
6062
|
-
q.line_suffix do
|
|
6394
|
+
q.line_suffix(priority: Formatter::HEREDOC_PRIORITY) do
|
|
6063
6395
|
q.group do
|
|
6064
6396
|
breakable.call
|
|
6065
6397
|
|
|
@@ -6219,6 +6551,12 @@ class SyntaxTree < Ripper
|
|
|
6219
6551
|
@value = value
|
|
6220
6552
|
end
|
|
6221
6553
|
|
|
6554
|
+
# This is here so that when checking if its contained within a parent
|
|
6555
|
+
# pattern that it will return true.
|
|
6556
|
+
def class
|
|
6557
|
+
HshPtn
|
|
6558
|
+
end
|
|
6559
|
+
|
|
6222
6560
|
def comments
|
|
6223
6561
|
[]
|
|
6224
6562
|
end
|
|
@@ -6293,7 +6631,7 @@ class SyntaxTree < Ripper
|
|
|
6293
6631
|
end
|
|
6294
6632
|
|
|
6295
6633
|
parent = q.parent
|
|
6296
|
-
if PATTERNS.
|
|
6634
|
+
if PATTERNS.include?(parent.class)
|
|
6297
6635
|
q.text("{ ")
|
|
6298
6636
|
contents.call
|
|
6299
6637
|
q.text(" }")
|
|
@@ -6442,38 +6780,50 @@ class SyntaxTree < Ripper
|
|
|
6442
6780
|
end
|
|
6443
6781
|
|
|
6444
6782
|
def format(q)
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6783
|
+
# If the predicate of the conditional contains an assignment (in which
|
|
6784
|
+
# case we can't know for certain that that assignment doesn't impact the
|
|
6785
|
+
# statements inside the conditional) then we can't use the modifier form
|
|
6786
|
+
# and we must use the block form.
|
|
6787
|
+
if [Assign, MAssign, OpAssign].include?(node.predicate.class)
|
|
6788
|
+
format_break(q, force: true)
|
|
6789
|
+
return
|
|
6790
|
+
end
|
|
6449
6791
|
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6792
|
+
if node.consequent || node.statements.empty?
|
|
6793
|
+
q.group { format_break(q, force: true) }
|
|
6794
|
+
else
|
|
6795
|
+
q.group do
|
|
6796
|
+
q.if_break { format_break(q, force: false) }.if_flat do
|
|
6797
|
+
Parentheses.flat(q) do
|
|
6798
|
+
q.format(node.statements)
|
|
6799
|
+
q.text(" #{keyword} ")
|
|
6800
|
+
q.format(node.predicate)
|
|
6801
|
+
end
|
|
6454
6802
|
end
|
|
6455
6803
|
end
|
|
6804
|
+
end
|
|
6805
|
+
end
|
|
6806
|
+
|
|
6807
|
+
private
|
|
6456
6808
|
|
|
6457
|
-
|
|
6809
|
+
def format_break(q, force:)
|
|
6810
|
+
q.text("#{keyword} ")
|
|
6811
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
|
6812
|
+
|
|
6813
|
+
unless node.statements.empty?
|
|
6814
|
+
q.indent do
|
|
6458
6815
|
q.breakable(force: force)
|
|
6459
|
-
q.format(node.
|
|
6816
|
+
q.format(node.statements)
|
|
6460
6817
|
end
|
|
6818
|
+
end
|
|
6461
6819
|
|
|
6820
|
+
if node.consequent
|
|
6462
6821
|
q.breakable(force: force)
|
|
6463
|
-
q.
|
|
6822
|
+
q.format(node.consequent)
|
|
6464
6823
|
end
|
|
6465
6824
|
|
|
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
|
|
6825
|
+
q.breakable(force: force)
|
|
6826
|
+
q.text("end")
|
|
6477
6827
|
end
|
|
6478
6828
|
end
|
|
6479
6829
|
|
|
@@ -6604,38 +6954,19 @@ class SyntaxTree < Ripper
|
|
|
6604
6954
|
end
|
|
6605
6955
|
|
|
6606
6956
|
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(" :")
|
|
6957
|
+
force_flat = [
|
|
6958
|
+
Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, IfOp,
|
|
6959
|
+
Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, Super,
|
|
6960
|
+
Undef, Unless, UnlessMod, UntilMod, VarAlias, VoidStmt, WhileMod, Yield,
|
|
6961
|
+
Yield0, ZSuper
|
|
6962
|
+
]
|
|
6634
6963
|
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6964
|
+
if force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
|
6965
|
+
q.group { format_flat(q) }
|
|
6966
|
+
return
|
|
6638
6967
|
end
|
|
6968
|
+
|
|
6969
|
+
q.group { q.if_break { format_break(q) }.if_flat { format_flat(q) } }
|
|
6639
6970
|
end
|
|
6640
6971
|
|
|
6641
6972
|
def pretty_print(q)
|
|
@@ -6665,6 +6996,43 @@ class SyntaxTree < Ripper
|
|
|
6665
6996
|
cmts: comments
|
|
6666
6997
|
}.to_json(*opts)
|
|
6667
6998
|
end
|
|
6999
|
+
|
|
7000
|
+
private
|
|
7001
|
+
|
|
7002
|
+
def format_break(q)
|
|
7003
|
+
Parentheses.break(q) do
|
|
7004
|
+
q.text("if ")
|
|
7005
|
+
q.nest("if ".length) { q.format(predicate) }
|
|
7006
|
+
|
|
7007
|
+
q.indent do
|
|
7008
|
+
q.breakable
|
|
7009
|
+
q.format(truthy)
|
|
7010
|
+
end
|
|
7011
|
+
|
|
7012
|
+
q.breakable
|
|
7013
|
+
q.text("else")
|
|
7014
|
+
|
|
7015
|
+
q.indent do
|
|
7016
|
+
q.breakable
|
|
7017
|
+
q.format(falsy)
|
|
7018
|
+
end
|
|
7019
|
+
|
|
7020
|
+
q.breakable
|
|
7021
|
+
q.text("end")
|
|
7022
|
+
end
|
|
7023
|
+
end
|
|
7024
|
+
|
|
7025
|
+
def format_flat(q)
|
|
7026
|
+
q.format(predicate)
|
|
7027
|
+
q.text(" ?")
|
|
7028
|
+
|
|
7029
|
+
q.breakable
|
|
7030
|
+
q.format(truthy)
|
|
7031
|
+
q.text(" :")
|
|
7032
|
+
|
|
7033
|
+
q.breakable
|
|
7034
|
+
q.format(falsy)
|
|
7035
|
+
end
|
|
6668
7036
|
end
|
|
6669
7037
|
|
|
6670
7038
|
# :call-seq:
|
|
@@ -6703,9 +7071,11 @@ class SyntaxTree < Ripper
|
|
|
6703
7071
|
q.breakable
|
|
6704
7072
|
q.text("end")
|
|
6705
7073
|
end.if_flat do
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
7074
|
+
Parentheses.flat(q) do
|
|
7075
|
+
q.format(node.statement)
|
|
7076
|
+
q.text(" #{keyword} ")
|
|
7077
|
+
q.format(node.predicate)
|
|
7078
|
+
end
|
|
6709
7079
|
end
|
|
6710
7080
|
end
|
|
6711
7081
|
end
|
|
@@ -6944,8 +7314,9 @@ class SyntaxTree < Ripper
|
|
|
6944
7314
|
beginning = find_token(Kw, "in")
|
|
6945
7315
|
ending = consequent || find_token(Kw, "end")
|
|
6946
7316
|
|
|
7317
|
+
statements_start = find_token(Kw, "then", consume: false) || pattern
|
|
6947
7318
|
statements.bind(
|
|
6948
|
-
find_next_statement_start(
|
|
7319
|
+
find_next_statement_start(statements_start.location.end_char),
|
|
6949
7320
|
ending.location.start_char
|
|
6950
7321
|
)
|
|
6951
7322
|
|
|
@@ -6982,7 +7353,7 @@ class SyntaxTree < Ripper
|
|
|
6982
7353
|
end
|
|
6983
7354
|
|
|
6984
7355
|
def format(q)
|
|
6985
|
-
if !value.start_with?(
|
|
7356
|
+
if !value.start_with?(/\+?0/) && value.length >= 5 && !value.include?("_")
|
|
6986
7357
|
# If it's a plain integer and it doesn't have any underscores separating
|
|
6987
7358
|
# the values, then we're going to insert them every 3 characters
|
|
6988
7359
|
# starting from the right.
|
|
@@ -7330,9 +7701,11 @@ class SyntaxTree < Ripper
|
|
|
7330
7701
|
if params.is_a?(Paren)
|
|
7331
7702
|
q.format(params) unless params.contents.empty?
|
|
7332
7703
|
elsif !params.empty?
|
|
7333
|
-
q.
|
|
7334
|
-
|
|
7335
|
-
|
|
7704
|
+
q.group do
|
|
7705
|
+
q.text("(")
|
|
7706
|
+
q.format(params)
|
|
7707
|
+
q.text(")")
|
|
7708
|
+
end
|
|
7336
7709
|
end
|
|
7337
7710
|
|
|
7338
7711
|
q.text(" ")
|
|
@@ -7391,8 +7764,11 @@ class SyntaxTree < Ripper
|
|
|
7391
7764
|
def on_lambda(params, statements)
|
|
7392
7765
|
beginning = find_token(TLambda)
|
|
7393
7766
|
|
|
7394
|
-
if
|
|
7395
|
-
|
|
7767
|
+
if tokens.any? { |token|
|
|
7768
|
+
token.is_a?(TLamBeg) &&
|
|
7769
|
+
token.location.start_char > beginning.location.start_char
|
|
7770
|
+
}
|
|
7771
|
+
opening = find_token(TLamBeg)
|
|
7396
7772
|
closing = find_token(RBrace)
|
|
7397
7773
|
else
|
|
7398
7774
|
opening = find_token(Kw, "do")
|
|
@@ -7472,9 +7848,34 @@ class SyntaxTree < Ripper
|
|
|
7472
7848
|
# [Location] the location of this node
|
|
7473
7849
|
attr_reader :location
|
|
7474
7850
|
|
|
7475
|
-
|
|
7851
|
+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
7852
|
+
attr_reader :comments
|
|
7853
|
+
|
|
7854
|
+
def initialize(value:, location:, comments: [])
|
|
7476
7855
|
@value = value
|
|
7477
7856
|
@location = location
|
|
7857
|
+
@comments = comments
|
|
7858
|
+
end
|
|
7859
|
+
|
|
7860
|
+
def format(q)
|
|
7861
|
+
q.text(value)
|
|
7862
|
+
end
|
|
7863
|
+
|
|
7864
|
+
def pretty_print(q)
|
|
7865
|
+
q.group(2, "(", ")") do
|
|
7866
|
+
q.text("lbracket")
|
|
7867
|
+
|
|
7868
|
+
q.breakable
|
|
7869
|
+
q.pp(value)
|
|
7870
|
+
|
|
7871
|
+
q.pp(Comment::List.new(comments))
|
|
7872
|
+
end
|
|
7873
|
+
end
|
|
7874
|
+
|
|
7875
|
+
def to_json(*opts)
|
|
7876
|
+
{ type: :lbracket, value: value, loc: location, cmts: comments }.to_json(
|
|
7877
|
+
*opts
|
|
7878
|
+
)
|
|
7478
7879
|
end
|
|
7479
7880
|
end
|
|
7480
7881
|
|
|
@@ -7591,11 +7992,7 @@ class SyntaxTree < Ripper
|
|
|
7591
7992
|
|
|
7592
7993
|
def format(q)
|
|
7593
7994
|
q.group do
|
|
7594
|
-
q.group
|
|
7595
|
-
q.format(target)
|
|
7596
|
-
q.text(",") if target.is_a?(MLHS) && target.comma
|
|
7597
|
-
end
|
|
7598
|
-
|
|
7995
|
+
q.group { q.format(target) }
|
|
7599
7996
|
q.text(" =")
|
|
7600
7997
|
q.indent do
|
|
7601
7998
|
q.breakable
|
|
@@ -7642,86 +8039,26 @@ class SyntaxTree < Ripper
|
|
|
7642
8039
|
)
|
|
7643
8040
|
end
|
|
7644
8041
|
|
|
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
|
|
7663
|
-
|
|
7664
|
-
# [ArgParen | Args] the arguments to the method call
|
|
7665
|
-
attr_reader :arguments
|
|
7666
|
-
|
|
7667
|
-
# [Location] the location of this node
|
|
7668
|
-
attr_reader :location
|
|
7669
|
-
|
|
7670
|
-
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
7671
|
-
attr_reader :comments
|
|
7672
|
-
|
|
7673
|
-
def initialize(call:, arguments:, location:, comments: [])
|
|
7674
|
-
@call = call
|
|
7675
|
-
@arguments = arguments
|
|
7676
|
-
@location = location
|
|
7677
|
-
@comments = comments
|
|
7678
|
-
end
|
|
7679
|
-
|
|
7680
|
-
def child_nodes
|
|
7681
|
-
[call, arguments]
|
|
7682
|
-
end
|
|
7683
|
-
|
|
7684
|
-
def format(q)
|
|
7685
|
-
q.format(call)
|
|
7686
|
-
q.text(" ") if !arguments.is_a?(ArgParen) && arguments.parts.any?
|
|
7687
|
-
q.format(arguments)
|
|
7688
|
-
end
|
|
7689
|
-
|
|
7690
|
-
def pretty_print(q)
|
|
7691
|
-
q.group(2, "(", ")") do
|
|
7692
|
-
q.text("method_add_arg")
|
|
7693
|
-
|
|
7694
|
-
q.breakable
|
|
7695
|
-
q.pp(call)
|
|
7696
|
-
|
|
7697
|
-
q.breakable
|
|
7698
|
-
q.pp(arguments)
|
|
7699
|
-
|
|
7700
|
-
q.pp(Comment::List.new(comments))
|
|
7701
|
-
end
|
|
7702
|
-
end
|
|
7703
|
-
|
|
7704
|
-
def to_json(*opts)
|
|
7705
|
-
{
|
|
7706
|
-
type: :method_add_arg,
|
|
7707
|
-
call: call,
|
|
7708
|
-
args: arguments,
|
|
7709
|
-
loc: location,
|
|
7710
|
-
cmts: comments
|
|
7711
|
-
}.to_json(*opts)
|
|
7712
|
-
end
|
|
7713
|
-
end
|
|
7714
|
-
|
|
7715
8042
|
# :call-seq:
|
|
7716
8043
|
# on_method_add_arg: (
|
|
7717
8044
|
# (Call | FCall) call,
|
|
7718
8045
|
# (ArgParen | Args) arguments
|
|
7719
|
-
# ) ->
|
|
8046
|
+
# ) -> Call | FCall
|
|
7720
8047
|
def on_method_add_arg(call, arguments)
|
|
7721
8048
|
location = call.location
|
|
7722
|
-
location = location.to(arguments.location)
|
|
8049
|
+
location = location.to(arguments.location) if arguments.is_a?(ArgParen)
|
|
7723
8050
|
|
|
7724
|
-
|
|
8051
|
+
if call.is_a?(FCall)
|
|
8052
|
+
FCall.new(value: call.value, arguments: arguments, location: location)
|
|
8053
|
+
else
|
|
8054
|
+
Call.new(
|
|
8055
|
+
receiver: call.receiver,
|
|
8056
|
+
operator: call.operator,
|
|
8057
|
+
message: call.message,
|
|
8058
|
+
arguments: arguments,
|
|
8059
|
+
location: location
|
|
8060
|
+
)
|
|
8061
|
+
end
|
|
7725
8062
|
end
|
|
7726
8063
|
|
|
7727
8064
|
# MethodAddBlock represents a method call with a block argument.
|
|
@@ -7729,7 +8066,7 @@ class SyntaxTree < Ripper
|
|
|
7729
8066
|
# method {}
|
|
7730
8067
|
#
|
|
7731
8068
|
class MethodAddBlock
|
|
7732
|
-
# [Call | Command | CommandCall | FCall
|
|
8069
|
+
# [Call | Command | CommandCall | FCall] the method call
|
|
7733
8070
|
attr_reader :call
|
|
7734
8071
|
|
|
7735
8072
|
# [BraceBlock | DoBlock] the block being sent with the method call
|
|
@@ -7784,7 +8121,7 @@ class SyntaxTree < Ripper
|
|
|
7784
8121
|
|
|
7785
8122
|
# :call-seq:
|
|
7786
8123
|
# on_method_add_block: (
|
|
7787
|
-
# (Call | Command | CommandCall | FCall
|
|
8124
|
+
# (Call | Command | CommandCall | FCall) call,
|
|
7788
8125
|
# (BraceBlock | DoBlock) block
|
|
7789
8126
|
# ) -> MethodAddBlock
|
|
7790
8127
|
def on_method_add_block(call, block)
|
|
@@ -7809,7 +8146,6 @@ class SyntaxTree < Ripper
|
|
|
7809
8146
|
# list, which impacts destructuring. It's an attr_accessor so that while
|
|
7810
8147
|
# the syntax tree is being built it can be set by its parent node
|
|
7811
8148
|
attr_accessor :comma
|
|
7812
|
-
alias comma? comma
|
|
7813
8149
|
|
|
7814
8150
|
# [Location] the location of this node
|
|
7815
8151
|
attr_reader :location
|
|
@@ -7830,6 +8166,7 @@ class SyntaxTree < Ripper
|
|
|
7830
8166
|
|
|
7831
8167
|
def format(q)
|
|
7832
8168
|
q.seplist(parts) { |part| q.format(part) }
|
|
8169
|
+
q.text(",") if comma
|
|
7833
8170
|
end
|
|
7834
8171
|
|
|
7835
8172
|
def pretty_print(q)
|
|
@@ -7932,7 +8269,6 @@ class SyntaxTree < Ripper
|
|
|
7932
8269
|
q.indent do
|
|
7933
8270
|
q.breakable("")
|
|
7934
8271
|
q.format(contents)
|
|
7935
|
-
q.text(",") if contents.is_a?(MLHS) && contents.comma?
|
|
7936
8272
|
end
|
|
7937
8273
|
|
|
7938
8274
|
q.breakable("")
|
|
@@ -8400,6 +8736,61 @@ class SyntaxTree < Ripper
|
|
|
8400
8736
|
)
|
|
8401
8737
|
end
|
|
8402
8738
|
|
|
8739
|
+
# If you have a modifier statement (for instance a modifier if statement or a
|
|
8740
|
+
# modifier while loop) there are times when you need to wrap the entire
|
|
8741
|
+
# statement in parentheses. This occurs when you have something like:
|
|
8742
|
+
#
|
|
8743
|
+
# foo[:foo] =
|
|
8744
|
+
# if bar?
|
|
8745
|
+
# baz
|
|
8746
|
+
# end
|
|
8747
|
+
#
|
|
8748
|
+
# Normally we would shorten this to an inline version, which would result in:
|
|
8749
|
+
#
|
|
8750
|
+
# foo[:foo] = baz if bar?
|
|
8751
|
+
#
|
|
8752
|
+
# but this actually has different semantic meaning. The first example will
|
|
8753
|
+
# result in a nil being inserted into the hash for the :foo key, whereas the
|
|
8754
|
+
# second example will result in an empty hash because the if statement applies
|
|
8755
|
+
# to the entire assignment.
|
|
8756
|
+
#
|
|
8757
|
+
# We can fix this in a couple of ways. We can use the then keyword, as in:
|
|
8758
|
+
#
|
|
8759
|
+
# foo[:foo] = if bar? then baz end
|
|
8760
|
+
#
|
|
8761
|
+
# But this isn't used very often. We can also just leave it as is with the
|
|
8762
|
+
# multi-line version, but for a short predicate and short value it looks
|
|
8763
|
+
# verbose. The last option and the one used here is to add parentheses on
|
|
8764
|
+
# both sides of the expression, as in:
|
|
8765
|
+
#
|
|
8766
|
+
# foo[:foo] = (baz if bar?)
|
|
8767
|
+
#
|
|
8768
|
+
# This approach maintains the nice conciseness of the inline version, while
|
|
8769
|
+
# keeping the correct semantic meaning.
|
|
8770
|
+
module Parentheses
|
|
8771
|
+
NODES = [Args, Assign, Assoc, Binary, Call, Defined, MAssign, OpAssign]
|
|
8772
|
+
|
|
8773
|
+
def self.flat(q)
|
|
8774
|
+
return yield unless NODES.include?(q.parent.class)
|
|
8775
|
+
|
|
8776
|
+
q.text("(")
|
|
8777
|
+
yield
|
|
8778
|
+
q.text(")")
|
|
8779
|
+
end
|
|
8780
|
+
|
|
8781
|
+
def self.break(q)
|
|
8782
|
+
return yield unless NODES.include?(q.parent.class)
|
|
8783
|
+
|
|
8784
|
+
q.text("(")
|
|
8785
|
+
q.indent do
|
|
8786
|
+
q.breakable("")
|
|
8787
|
+
yield
|
|
8788
|
+
end
|
|
8789
|
+
q.breakable("")
|
|
8790
|
+
q.text(")")
|
|
8791
|
+
end
|
|
8792
|
+
end
|
|
8793
|
+
|
|
8403
8794
|
# def on_operator_ambiguous(value)
|
|
8404
8795
|
# value
|
|
8405
8796
|
# end
|
|
@@ -8538,9 +8929,7 @@ class SyntaxTree < Ripper
|
|
|
8538
8929
|
# it's missing.
|
|
8539
8930
|
def empty?
|
|
8540
8931
|
requireds.empty? && optionals.empty? && !rest && posts.empty? &&
|
|
8541
|
-
keywords.empty? &&
|
|
8542
|
-
!keyword_rest &&
|
|
8543
|
-
!block
|
|
8932
|
+
keywords.empty? && !keyword_rest && !block
|
|
8544
8933
|
end
|
|
8545
8934
|
|
|
8546
8935
|
def child_nodes
|
|
@@ -8571,10 +8960,22 @@ class SyntaxTree < Ripper
|
|
|
8571
8960
|
parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
|
|
8572
8961
|
parts << block if block
|
|
8573
8962
|
|
|
8574
|
-
|
|
8963
|
+
contents = -> do
|
|
8575
8964
|
q.seplist(parts) { |part| q.format(part) }
|
|
8576
8965
|
q.format(rest) if rest && rest.is_a?(ExcessedComma)
|
|
8577
8966
|
end
|
|
8967
|
+
|
|
8968
|
+
if [Def, Defs].include?(q.parent.class)
|
|
8969
|
+
q.group(0, "(", ")") do
|
|
8970
|
+
q.indent do
|
|
8971
|
+
q.breakable("")
|
|
8972
|
+
contents.call
|
|
8973
|
+
end
|
|
8974
|
+
q.breakable("")
|
|
8975
|
+
end
|
|
8976
|
+
else
|
|
8977
|
+
q.nest(0, &contents)
|
|
8978
|
+
end
|
|
8578
8979
|
end
|
|
8579
8980
|
|
|
8580
8981
|
def pretty_print(q)
|
|
@@ -8716,7 +9117,7 @@ class SyntaxTree < Ripper
|
|
|
8716
9117
|
# [LParen] the left parenthesis that opened this statement
|
|
8717
9118
|
attr_reader :lparen
|
|
8718
9119
|
|
|
8719
|
-
# [untyped] the expression inside the parentheses
|
|
9120
|
+
# [nil | untyped] the expression inside the parentheses
|
|
8720
9121
|
attr_reader :contents
|
|
8721
9122
|
|
|
8722
9123
|
# [Location] the location of this node
|
|
@@ -8740,7 +9141,7 @@ class SyntaxTree < Ripper
|
|
|
8740
9141
|
q.group do
|
|
8741
9142
|
q.format(lparen)
|
|
8742
9143
|
|
|
8743
|
-
if !contents.is_a?(Params) || !contents.empty?
|
|
9144
|
+
if contents && (!contents.is_a?(Params) || !contents.empty?)
|
|
8744
9145
|
q.indent do
|
|
8745
9146
|
q.breakable("")
|
|
8746
9147
|
q.format(contents)
|
|
@@ -8805,7 +9206,7 @@ class SyntaxTree < Ripper
|
|
|
8805
9206
|
|
|
8806
9207
|
Paren.new(
|
|
8807
9208
|
lparen: lparen,
|
|
8808
|
-
contents: contents,
|
|
9209
|
+
contents: contents || nil,
|
|
8809
9210
|
location: lparen.location.to(rparen.location)
|
|
8810
9211
|
)
|
|
8811
9212
|
end
|
|
@@ -8896,7 +9297,11 @@ class SyntaxTree < Ripper
|
|
|
8896
9297
|
|
|
8897
9298
|
def format(q)
|
|
8898
9299
|
q.format(statements)
|
|
8899
|
-
|
|
9300
|
+
|
|
9301
|
+
# We're going to put a newline on the end so that it always has one unless
|
|
9302
|
+
# it ends with the special __END__ syntax. In that case we want to
|
|
9303
|
+
# replicate the text exactly so we will just let it be.
|
|
9304
|
+
q.breakable(force: true) unless statements.body.last.is_a?(EndContent)
|
|
8900
9305
|
end
|
|
8901
9306
|
|
|
8902
9307
|
def pretty_print(q)
|
|
@@ -9035,6 +9440,9 @@ class SyntaxTree < Ripper
|
|
|
9035
9440
|
# %i[one two three]
|
|
9036
9441
|
#
|
|
9037
9442
|
class QSymbols
|
|
9443
|
+
# [QSymbolsBeg] the token that opens this array literal
|
|
9444
|
+
attr_reader :beginning
|
|
9445
|
+
|
|
9038
9446
|
# [Array[ TStringContent ]] the elements of the array
|
|
9039
9447
|
attr_reader :elements
|
|
9040
9448
|
|
|
@@ -9044,7 +9452,8 @@ class SyntaxTree < Ripper
|
|
|
9044
9452
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
9045
9453
|
attr_reader :comments
|
|
9046
9454
|
|
|
9047
|
-
def initialize(elements:, location:, comments: [])
|
|
9455
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
|
9456
|
+
@beginning = beginning
|
|
9048
9457
|
@elements = elements
|
|
9049
9458
|
@location = location
|
|
9050
9459
|
@comments = comments
|
|
@@ -9055,7 +9464,14 @@ class SyntaxTree < Ripper
|
|
|
9055
9464
|
end
|
|
9056
9465
|
|
|
9057
9466
|
def format(q)
|
|
9058
|
-
|
|
9467
|
+
opening, closing = "%i[", "]"
|
|
9468
|
+
|
|
9469
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
|
9470
|
+
opening = beginning.value
|
|
9471
|
+
closing = Quotes.matching(opening[2])
|
|
9472
|
+
end
|
|
9473
|
+
|
|
9474
|
+
q.group(0, opening, closing) do
|
|
9059
9475
|
q.indent do
|
|
9060
9476
|
q.breakable("")
|
|
9061
9477
|
q.seplist(elements, -> { q.breakable }) do |element|
|
|
@@ -9091,6 +9507,7 @@ class SyntaxTree < Ripper
|
|
|
9091
9507
|
# on_qsymbols_add: (QSymbols qsymbols, TStringContent element) -> QSymbols
|
|
9092
9508
|
def on_qsymbols_add(qsymbols, element)
|
|
9093
9509
|
QSymbols.new(
|
|
9510
|
+
beginning: qsymbols.beginning,
|
|
9094
9511
|
elements: qsymbols.elements << element,
|
|
9095
9512
|
location: qsymbols.location.to(element.location)
|
|
9096
9513
|
)
|
|
@@ -9132,9 +9549,13 @@ class SyntaxTree < Ripper
|
|
|
9132
9549
|
# :call-seq:
|
|
9133
9550
|
# on_qsymbols_new: () -> QSymbols
|
|
9134
9551
|
def on_qsymbols_new
|
|
9135
|
-
|
|
9552
|
+
beginning = find_token(QSymbolsBeg)
|
|
9136
9553
|
|
|
9137
|
-
QSymbols.new(
|
|
9554
|
+
QSymbols.new(
|
|
9555
|
+
beginning: beginning,
|
|
9556
|
+
elements: [],
|
|
9557
|
+
location: beginning.location
|
|
9558
|
+
)
|
|
9138
9559
|
end
|
|
9139
9560
|
|
|
9140
9561
|
# QWords represents a string literal array without interpolation.
|
|
@@ -9142,6 +9563,9 @@ class SyntaxTree < Ripper
|
|
|
9142
9563
|
# %w[one two three]
|
|
9143
9564
|
#
|
|
9144
9565
|
class QWords
|
|
9566
|
+
# [QWordsBeg] the token that opens this array literal
|
|
9567
|
+
attr_reader :beginning
|
|
9568
|
+
|
|
9145
9569
|
# [Array[ TStringContent ]] the elements of the array
|
|
9146
9570
|
attr_reader :elements
|
|
9147
9571
|
|
|
@@ -9151,7 +9575,8 @@ class SyntaxTree < Ripper
|
|
|
9151
9575
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
9152
9576
|
attr_reader :comments
|
|
9153
9577
|
|
|
9154
|
-
def initialize(elements:, location:, comments: [])
|
|
9578
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
|
9579
|
+
@beginning = beginning
|
|
9155
9580
|
@elements = elements
|
|
9156
9581
|
@location = location
|
|
9157
9582
|
@comments = comments
|
|
@@ -9162,7 +9587,14 @@ class SyntaxTree < Ripper
|
|
|
9162
9587
|
end
|
|
9163
9588
|
|
|
9164
9589
|
def format(q)
|
|
9165
|
-
|
|
9590
|
+
opening, closing = "%w[", "]"
|
|
9591
|
+
|
|
9592
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
|
9593
|
+
opening = beginning.value
|
|
9594
|
+
closing = Quotes.matching(opening[2])
|
|
9595
|
+
end
|
|
9596
|
+
|
|
9597
|
+
q.group(0, opening, closing) do
|
|
9166
9598
|
q.indent do
|
|
9167
9599
|
q.breakable("")
|
|
9168
9600
|
q.seplist(elements, -> { q.breakable }) do |element|
|
|
@@ -9195,6 +9627,7 @@ class SyntaxTree < Ripper
|
|
|
9195
9627
|
# on_qwords_add: (QWords qwords, TStringContent element) -> QWords
|
|
9196
9628
|
def on_qwords_add(qwords, element)
|
|
9197
9629
|
QWords.new(
|
|
9630
|
+
beginning: qwords.beginning,
|
|
9198
9631
|
elements: qwords.elements << element,
|
|
9199
9632
|
location: qwords.location.to(element.location)
|
|
9200
9633
|
)
|
|
@@ -9236,9 +9669,9 @@ class SyntaxTree < Ripper
|
|
|
9236
9669
|
# :call-seq:
|
|
9237
9670
|
# on_qwords_new: () -> QWords
|
|
9238
9671
|
def on_qwords_new
|
|
9239
|
-
|
|
9672
|
+
beginning = find_token(QWordsBeg)
|
|
9240
9673
|
|
|
9241
|
-
QWords.new(elements: [], location:
|
|
9674
|
+
QWords.new(beginning: beginning, elements: [], location: beginning.location)
|
|
9242
9675
|
end
|
|
9243
9676
|
|
|
9244
9677
|
# RationalLiteral represents the use of a rational number literal.
|
|
@@ -9550,11 +9983,32 @@ class SyntaxTree < Ripper
|
|
|
9550
9983
|
q.format_each(parts)
|
|
9551
9984
|
q.text(ending)
|
|
9552
9985
|
end
|
|
9986
|
+
elsif braces
|
|
9987
|
+
q.group do
|
|
9988
|
+
q.text("%r{")
|
|
9989
|
+
|
|
9990
|
+
if beginning == "/"
|
|
9991
|
+
# If we're changing from a forward slash to a %r{, then we can
|
|
9992
|
+
# replace any escaped forward slashes with regular forward slashes.
|
|
9993
|
+
parts.each do |part|
|
|
9994
|
+
if part.is_a?(TStringContent)
|
|
9995
|
+
q.text(part.value.gsub("\\/", "/"))
|
|
9996
|
+
else
|
|
9997
|
+
q.format(part)
|
|
9998
|
+
end
|
|
9999
|
+
end
|
|
10000
|
+
else
|
|
10001
|
+
q.format_each(parts)
|
|
10002
|
+
end
|
|
10003
|
+
|
|
10004
|
+
q.text("}")
|
|
10005
|
+
q.text(ending[1..-1])
|
|
10006
|
+
end
|
|
9553
10007
|
else
|
|
9554
10008
|
q.group do
|
|
9555
|
-
q.text(
|
|
10009
|
+
q.text("/")
|
|
9556
10010
|
q.format_each(parts)
|
|
9557
|
-
q.text(
|
|
10011
|
+
q.text("/")
|
|
9558
10012
|
q.text(ending[1..-1])
|
|
9559
10013
|
end
|
|
9560
10014
|
end
|
|
@@ -10285,21 +10739,6 @@ class SyntaxTree < Ripper
|
|
|
10285
10739
|
# value
|
|
10286
10740
|
# end
|
|
10287
10741
|
|
|
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
10742
|
# Everything that has a block of code inside of it has a list of statements.
|
|
10304
10743
|
# Normally we would just track those as a node that has an array body, but we
|
|
10305
10744
|
# have some special handling in order to handle empty statement lists. They
|
|
@@ -10379,12 +10818,22 @@ class SyntaxTree < Ripper
|
|
|
10379
10818
|
# the only value is a comment. In that case a lot of nodes like
|
|
10380
10819
|
# brace_block will attempt to format as a single line, but since that
|
|
10381
10820
|
# wouldn't work with a comment, we intentionally break the parent group.
|
|
10382
|
-
if body.length == 2
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10821
|
+
if body.length == 2
|
|
10822
|
+
void_stmt, comment = body
|
|
10823
|
+
|
|
10824
|
+
if void_stmt.is_a?(VoidStmt) && comment.is_a?(Comment)
|
|
10825
|
+
q.format(comment)
|
|
10826
|
+
q.break_parent
|
|
10827
|
+
return
|
|
10828
|
+
end
|
|
10386
10829
|
end
|
|
10387
10830
|
|
|
10831
|
+
access_controls =
|
|
10832
|
+
Hash.new do |hash, node|
|
|
10833
|
+
hash[node] = node.is_a?(VCall) &&
|
|
10834
|
+
%w[private protected public].include?(node.value.value)
|
|
10835
|
+
end
|
|
10836
|
+
|
|
10388
10837
|
body.each_with_index do |statement, index|
|
|
10389
10838
|
next if statement.is_a?(VoidStmt)
|
|
10390
10839
|
|
|
@@ -10394,7 +10843,7 @@ class SyntaxTree < Ripper
|
|
|
10394
10843
|
q.breakable(force: true)
|
|
10395
10844
|
q.breakable(force: true)
|
|
10396
10845
|
q.format(statement)
|
|
10397
|
-
elsif statement
|
|
10846
|
+
elsif access_controls[statement] || access_controls[body[index - 1]]
|
|
10398
10847
|
q.breakable(force: true)
|
|
10399
10848
|
q.breakable(force: true)
|
|
10400
10849
|
q.format(statement)
|
|
@@ -10446,7 +10895,7 @@ class SyntaxTree < Ripper
|
|
|
10446
10895
|
location = comment.location
|
|
10447
10896
|
|
|
10448
10897
|
if !comment.inline? && (start_char <= location.start_char) &&
|
|
10449
|
-
(end_char >= location.end_char)
|
|
10898
|
+
(end_char >= location.end_char) && !comment.ignore?
|
|
10450
10899
|
parser_comments.delete_at(comment_index)
|
|
10451
10900
|
|
|
10452
10901
|
while (node = body[body_index]) &&
|
|
@@ -10465,6 +10914,21 @@ class SyntaxTree < Ripper
|
|
|
10465
10914
|
end
|
|
10466
10915
|
end
|
|
10467
10916
|
|
|
10917
|
+
# stmts_add is a parser event that represents a single statement inside a
|
|
10918
|
+
# list of statements within any lexical block. It accepts as arguments the
|
|
10919
|
+
# parent stmts node as well as an stmt which can be any expression in
|
|
10920
|
+
# Ruby.
|
|
10921
|
+
def on_stmts_add(statements, statement)
|
|
10922
|
+
location =
|
|
10923
|
+
if statements.body.empty?
|
|
10924
|
+
statement.location
|
|
10925
|
+
else
|
|
10926
|
+
statements.location.to(statement.location)
|
|
10927
|
+
end
|
|
10928
|
+
|
|
10929
|
+
Statements.new(self, body: statements.body << statement, location: location)
|
|
10930
|
+
end
|
|
10931
|
+
|
|
10468
10932
|
# :call-seq:
|
|
10469
10933
|
# on_stmts_new: () -> Statements
|
|
10470
10934
|
def on_stmts_new
|
|
@@ -10736,10 +11200,16 @@ class SyntaxTree < Ripper
|
|
|
10736
11200
|
embexpr_end.location.start_char
|
|
10737
11201
|
)
|
|
10738
11202
|
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
11203
|
+
location =
|
|
11204
|
+
Location.new(
|
|
11205
|
+
start_line: embexpr_beg.location.start_line,
|
|
11206
|
+
start_char: embexpr_beg.location.start_char,
|
|
11207
|
+
end_line:
|
|
11208
|
+
[embexpr_end.location.end_line, statements.location.end_line].max,
|
|
11209
|
+
end_char: embexpr_end.location.end_char
|
|
11210
|
+
)
|
|
11211
|
+
|
|
11212
|
+
StringEmbExpr.new(statements: statements, location: location)
|
|
10743
11213
|
end
|
|
10744
11214
|
|
|
10745
11215
|
# StringLiteral represents a string literal.
|
|
@@ -10839,12 +11309,21 @@ class SyntaxTree < Ripper
|
|
|
10839
11309
|
)
|
|
10840
11310
|
else
|
|
10841
11311
|
tstring_beg = find_token(TStringBeg)
|
|
10842
|
-
tstring_end = find_token(TStringEnd)
|
|
11312
|
+
tstring_end = find_token(TStringEnd, location: tstring_beg.location)
|
|
11313
|
+
|
|
11314
|
+
location =
|
|
11315
|
+
Location.new(
|
|
11316
|
+
start_line: tstring_beg.location.start_line,
|
|
11317
|
+
start_char: tstring_beg.location.start_char,
|
|
11318
|
+
end_line:
|
|
11319
|
+
[tstring_end.location.end_line, string.location.end_line].max,
|
|
11320
|
+
end_char: tstring_end.location.end_char
|
|
11321
|
+
)
|
|
10843
11322
|
|
|
10844
11323
|
StringLiteral.new(
|
|
10845
11324
|
parts: string.parts,
|
|
10846
11325
|
quote: tstring_beg.value,
|
|
10847
|
-
location:
|
|
11326
|
+
location: location
|
|
10848
11327
|
)
|
|
10849
11328
|
end
|
|
10850
11329
|
end
|
|
@@ -11066,6 +11545,9 @@ class SyntaxTree < Ripper
|
|
|
11066
11545
|
# %I[one two three]
|
|
11067
11546
|
#
|
|
11068
11547
|
class Symbols
|
|
11548
|
+
# [SymbolsBeg] the token that opens this array literal
|
|
11549
|
+
attr_reader :beginning
|
|
11550
|
+
|
|
11069
11551
|
# [Array[ Word ]] the words in the symbol array literal
|
|
11070
11552
|
attr_reader :elements
|
|
11071
11553
|
|
|
@@ -11075,7 +11557,8 @@ class SyntaxTree < Ripper
|
|
|
11075
11557
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
11076
11558
|
attr_reader :comments
|
|
11077
11559
|
|
|
11078
|
-
def initialize(elements:, location:, comments: [])
|
|
11560
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
|
11561
|
+
@beginning = beginning
|
|
11079
11562
|
@elements = elements
|
|
11080
11563
|
@location = location
|
|
11081
11564
|
@comments = comments
|
|
@@ -11086,7 +11569,14 @@ class SyntaxTree < Ripper
|
|
|
11086
11569
|
end
|
|
11087
11570
|
|
|
11088
11571
|
def format(q)
|
|
11089
|
-
|
|
11572
|
+
opening, closing = "%I[", "]"
|
|
11573
|
+
|
|
11574
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
|
11575
|
+
opening = beginning.value
|
|
11576
|
+
closing = Quotes.matching(opening[2])
|
|
11577
|
+
end
|
|
11578
|
+
|
|
11579
|
+
q.group(0, opening, closing) do
|
|
11090
11580
|
q.indent do
|
|
11091
11581
|
q.breakable("")
|
|
11092
11582
|
q.seplist(elements, -> { q.breakable }) do |element|
|
|
@@ -11122,6 +11612,7 @@ class SyntaxTree < Ripper
|
|
|
11122
11612
|
# on_symbols_add: (Symbols symbols, Word word) -> Symbols
|
|
11123
11613
|
def on_symbols_add(symbols, word)
|
|
11124
11614
|
Symbols.new(
|
|
11615
|
+
beginning: symbols.beginning,
|
|
11125
11616
|
elements: symbols.elements << word,
|
|
11126
11617
|
location: symbols.location.to(word.location)
|
|
11127
11618
|
)
|
|
@@ -11164,9 +11655,13 @@ class SyntaxTree < Ripper
|
|
|
11164
11655
|
# :call-seq:
|
|
11165
11656
|
# on_symbols_new: () -> Symbols
|
|
11166
11657
|
def on_symbols_new
|
|
11167
|
-
|
|
11658
|
+
beginning = find_token(SymbolsBeg)
|
|
11168
11659
|
|
|
11169
|
-
Symbols.new(
|
|
11660
|
+
Symbols.new(
|
|
11661
|
+
beginning: beginning,
|
|
11662
|
+
elements: [],
|
|
11663
|
+
location: beginning.location
|
|
11664
|
+
)
|
|
11170
11665
|
end
|
|
11171
11666
|
|
|
11172
11667
|
# TLambda represents the beginning of a lambda literal.
|
|
@@ -11258,6 +11753,11 @@ class SyntaxTree < Ripper
|
|
|
11258
11753
|
[constant]
|
|
11259
11754
|
end
|
|
11260
11755
|
|
|
11756
|
+
def format(q)
|
|
11757
|
+
q.text("::")
|
|
11758
|
+
q.format(constant)
|
|
11759
|
+
end
|
|
11760
|
+
|
|
11261
11761
|
def pretty_print(q)
|
|
11262
11762
|
q.group(2, "(", ")") do
|
|
11263
11763
|
q.text("top_const_field")
|
|
@@ -11412,6 +11912,10 @@ class SyntaxTree < Ripper
|
|
|
11412
11912
|
@comments = comments
|
|
11413
11913
|
end
|
|
11414
11914
|
|
|
11915
|
+
def match?(pattern)
|
|
11916
|
+
value.match?(pattern)
|
|
11917
|
+
end
|
|
11918
|
+
|
|
11415
11919
|
def child_nodes
|
|
11416
11920
|
[]
|
|
11417
11921
|
end
|
|
@@ -11914,23 +12418,39 @@ class SyntaxTree < Ripper
|
|
|
11914
12418
|
end
|
|
11915
12419
|
|
|
11916
12420
|
def format(q)
|
|
12421
|
+
# If the predicate of the loop contains an assignment (in which case we
|
|
12422
|
+
# can't know for certain that that assignment doesn't impact the
|
|
12423
|
+
# statements inside the loop) then we can't use the modifier form and we
|
|
12424
|
+
# must use the block form.
|
|
12425
|
+
if [Assign, MAssign, OpAssign].include?(node.predicate.class)
|
|
12426
|
+
format_break(q)
|
|
12427
|
+
q.break_parent
|
|
12428
|
+
return
|
|
12429
|
+
end
|
|
12430
|
+
|
|
11917
12431
|
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("")
|
|
12432
|
+
q.if_break { format_break(q) }.if_flat do
|
|
12433
|
+
Parentheses.flat(q) do
|
|
11923
12434
|
q.format(statements)
|
|
12435
|
+
q.text(" #{keyword} ")
|
|
12436
|
+
q.format(node.predicate)
|
|
11924
12437
|
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
12438
|
end
|
|
11932
12439
|
end
|
|
11933
12440
|
end
|
|
12441
|
+
|
|
12442
|
+
private
|
|
12443
|
+
|
|
12444
|
+
def format_break(q)
|
|
12445
|
+
q.text("#{keyword} ")
|
|
12446
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
|
12447
|
+
q.indent do
|
|
12448
|
+
q.breakable("")
|
|
12449
|
+
q.format(statements)
|
|
12450
|
+
end
|
|
12451
|
+
q.breakable("")
|
|
12452
|
+
q.text("end")
|
|
12453
|
+
end
|
|
11934
12454
|
end
|
|
11935
12455
|
|
|
11936
12456
|
# Until represents an +until+ loop.
|
|
@@ -12055,7 +12575,22 @@ class SyntaxTree < Ripper
|
|
|
12055
12575
|
end
|
|
12056
12576
|
|
|
12057
12577
|
def format(q)
|
|
12058
|
-
|
|
12578
|
+
# If we're in the modifier form and we're modifying a `begin`, then this
|
|
12579
|
+
# is a special case where we need to explicitly use the modifier form
|
|
12580
|
+
# because otherwise the semantic meaning changes. This looks like:
|
|
12581
|
+
#
|
|
12582
|
+
# begin
|
|
12583
|
+
# foo
|
|
12584
|
+
# end until bar
|
|
12585
|
+
#
|
|
12586
|
+
# The above is effectively a `do...until` loop.
|
|
12587
|
+
if statement.is_a?(Begin)
|
|
12588
|
+
q.format(statement)
|
|
12589
|
+
q.text(" until ")
|
|
12590
|
+
q.format(predicate)
|
|
12591
|
+
else
|
|
12592
|
+
LoopFormatter.new("until", self, statement).format(q)
|
|
12593
|
+
end
|
|
12059
12594
|
end
|
|
12060
12595
|
|
|
12061
12596
|
def pretty_print(q)
|
|
@@ -12291,56 +12826,6 @@ class SyntaxTree < Ripper
|
|
|
12291
12826
|
VarRef.new(value: value, location: value.location)
|
|
12292
12827
|
end
|
|
12293
12828
|
|
|
12294
|
-
# AccessCtrl represents a call to a method visibility control, i.e., +public+,
|
|
12295
|
-
# +protected+, or +private+.
|
|
12296
|
-
#
|
|
12297
|
-
# private
|
|
12298
|
-
#
|
|
12299
|
-
class AccessCtrl
|
|
12300
|
-
# [Ident] the value of this expression
|
|
12301
|
-
attr_reader :value
|
|
12302
|
-
|
|
12303
|
-
# [Location] the location of this node
|
|
12304
|
-
attr_reader :location
|
|
12305
|
-
|
|
12306
|
-
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
12307
|
-
attr_reader :comments
|
|
12308
|
-
|
|
12309
|
-
def initialize(value:, location:, comments: [])
|
|
12310
|
-
@value = value
|
|
12311
|
-
@location = location
|
|
12312
|
-
@comments = comments
|
|
12313
|
-
end
|
|
12314
|
-
|
|
12315
|
-
def child_nodes
|
|
12316
|
-
[value]
|
|
12317
|
-
end
|
|
12318
|
-
|
|
12319
|
-
def format(q)
|
|
12320
|
-
q.format(value)
|
|
12321
|
-
end
|
|
12322
|
-
|
|
12323
|
-
def pretty_print(q)
|
|
12324
|
-
q.group(2, "(", ")") do
|
|
12325
|
-
q.text("access_ctrl")
|
|
12326
|
-
|
|
12327
|
-
q.breakable
|
|
12328
|
-
q.pp(value)
|
|
12329
|
-
|
|
12330
|
-
q.pp(Comment::List.new(comments))
|
|
12331
|
-
end
|
|
12332
|
-
end
|
|
12333
|
-
|
|
12334
|
-
def to_json(*opts)
|
|
12335
|
-
{
|
|
12336
|
-
type: :access_ctrl,
|
|
12337
|
-
value: value,
|
|
12338
|
-
loc: location,
|
|
12339
|
-
cmts: comments
|
|
12340
|
-
}.to_json(*opts)
|
|
12341
|
-
end
|
|
12342
|
-
end
|
|
12343
|
-
|
|
12344
12829
|
# VCall represent any plain named object with Ruby that could be either a
|
|
12345
12830
|
# local variable or a method call.
|
|
12346
12831
|
#
|
|
@@ -12389,19 +12874,9 @@ class SyntaxTree < Ripper
|
|
|
12389
12874
|
end
|
|
12390
12875
|
|
|
12391
12876
|
# :call-seq:
|
|
12392
|
-
# on_vcall: (Ident ident) ->
|
|
12877
|
+
# on_vcall: (Ident ident) -> VCall
|
|
12393
12878
|
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
|
|
12879
|
+
VCall.new(value: ident, location: ident.location)
|
|
12405
12880
|
end
|
|
12406
12881
|
|
|
12407
12882
|
# VoidStmt represents an empty lexical block of code.
|
|
@@ -12552,7 +13027,11 @@ class SyntaxTree < Ripper
|
|
|
12552
13027
|
beginning = find_token(Kw, "when")
|
|
12553
13028
|
ending = consequent || find_token(Kw, "end")
|
|
12554
13029
|
|
|
12555
|
-
|
|
13030
|
+
statements_start = find_token(Kw, "then", consume: false) || arguments
|
|
13031
|
+
statements.bind(
|
|
13032
|
+
find_next_statement_start(statements_start.location.end_char),
|
|
13033
|
+
ending.location.start_char
|
|
13034
|
+
)
|
|
12556
13035
|
|
|
12557
13036
|
When.new(
|
|
12558
13037
|
arguments: arguments,
|
|
@@ -12684,7 +13163,22 @@ class SyntaxTree < Ripper
|
|
|
12684
13163
|
end
|
|
12685
13164
|
|
|
12686
13165
|
def format(q)
|
|
12687
|
-
|
|
13166
|
+
# If we're in the modifier form and we're modifying a `begin`, then this
|
|
13167
|
+
# is a special case where we need to explicitly use the modifier form
|
|
13168
|
+
# because otherwise the semantic meaning changes. This looks like:
|
|
13169
|
+
#
|
|
13170
|
+
# begin
|
|
13171
|
+
# foo
|
|
13172
|
+
# end while bar
|
|
13173
|
+
#
|
|
13174
|
+
# The above is effectively a `do...while` loop.
|
|
13175
|
+
if statement.is_a?(Begin)
|
|
13176
|
+
q.format(statement)
|
|
13177
|
+
q.text(" while ")
|
|
13178
|
+
q.format(predicate)
|
|
13179
|
+
else
|
|
13180
|
+
LoopFormatter.new("while", self, statement).format(q)
|
|
13181
|
+
end
|
|
12688
13182
|
end
|
|
12689
13183
|
|
|
12690
13184
|
def pretty_print(q)
|
|
@@ -12748,6 +13242,10 @@ class SyntaxTree < Ripper
|
|
|
12748
13242
|
@comments = comments
|
|
12749
13243
|
end
|
|
12750
13244
|
|
|
13245
|
+
def match?(pattern)
|
|
13246
|
+
parts.any? { |part| part.is_a?(TStringContent) && part.match?(pattern) }
|
|
13247
|
+
end
|
|
13248
|
+
|
|
12751
13249
|
def child_nodes
|
|
12752
13250
|
parts
|
|
12753
13251
|
end
|
|
@@ -12797,6 +13295,9 @@ class SyntaxTree < Ripper
|
|
|
12797
13295
|
# %W[one two three]
|
|
12798
13296
|
#
|
|
12799
13297
|
class Words
|
|
13298
|
+
# [WordsBeg] the token that opens this array literal
|
|
13299
|
+
attr_reader :beginning
|
|
13300
|
+
|
|
12800
13301
|
# [Array[ Word ]] the elements of this array
|
|
12801
13302
|
attr_reader :elements
|
|
12802
13303
|
|
|
@@ -12806,7 +13307,8 @@ class SyntaxTree < Ripper
|
|
|
12806
13307
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
|
12807
13308
|
attr_reader :comments
|
|
12808
13309
|
|
|
12809
|
-
def initialize(elements:, location:, comments: [])
|
|
13310
|
+
def initialize(beginning:, elements:, location:, comments: [])
|
|
13311
|
+
@beginning = beginning
|
|
12810
13312
|
@elements = elements
|
|
12811
13313
|
@location = location
|
|
12812
13314
|
@comments = comments
|
|
@@ -12817,7 +13319,14 @@ class SyntaxTree < Ripper
|
|
|
12817
13319
|
end
|
|
12818
13320
|
|
|
12819
13321
|
def format(q)
|
|
12820
|
-
|
|
13322
|
+
opening, closing = "%W[", "]"
|
|
13323
|
+
|
|
13324
|
+
if elements.any? { |element| element.match?(/[\[\]]/) }
|
|
13325
|
+
opening = beginning.value
|
|
13326
|
+
closing = Quotes.matching(opening[2])
|
|
13327
|
+
end
|
|
13328
|
+
|
|
13329
|
+
q.group(0, opening, closing) do
|
|
12821
13330
|
q.indent do
|
|
12822
13331
|
q.breakable("")
|
|
12823
13332
|
q.seplist(elements, -> { q.breakable }) do |element|
|
|
@@ -12850,6 +13359,7 @@ class SyntaxTree < Ripper
|
|
|
12850
13359
|
# on_words_add: (Words words, Word word) -> Words
|
|
12851
13360
|
def on_words_add(words, word)
|
|
12852
13361
|
Words.new(
|
|
13362
|
+
beginning: words.beginning,
|
|
12853
13363
|
elements: words.elements << word,
|
|
12854
13364
|
location: words.location.to(word.location)
|
|
12855
13365
|
)
|
|
@@ -12892,9 +13402,9 @@ class SyntaxTree < Ripper
|
|
|
12892
13402
|
# :call-seq:
|
|
12893
13403
|
# on_words_new: () -> Words
|
|
12894
13404
|
def on_words_new
|
|
12895
|
-
|
|
13405
|
+
beginning = find_token(WordsBeg)
|
|
12896
13406
|
|
|
12897
|
-
Words.new(elements: [], location:
|
|
13407
|
+
Words.new(beginning: beginning, elements: [], location: beginning.location)
|
|
12898
13408
|
end
|
|
12899
13409
|
|
|
12900
13410
|
# def on_words_sep(value)
|
|
@@ -13011,7 +13521,7 @@ class SyntaxTree < Ripper
|
|
|
13011
13521
|
location: heredoc.location
|
|
13012
13522
|
)
|
|
13013
13523
|
else
|
|
13014
|
-
ending = find_token(TStringEnd)
|
|
13524
|
+
ending = find_token(TStringEnd, location: xstring.location)
|
|
13015
13525
|
|
|
13016
13526
|
XStringLiteral.new(
|
|
13017
13527
|
parts: xstring.parts,
|
|
@@ -13047,8 +13557,18 @@ class SyntaxTree < Ripper
|
|
|
13047
13557
|
def format(q)
|
|
13048
13558
|
q.group do
|
|
13049
13559
|
q.text("yield")
|
|
13050
|
-
|
|
13051
|
-
|
|
13560
|
+
|
|
13561
|
+
if arguments.is_a?(Paren)
|
|
13562
|
+
q.format(arguments)
|
|
13563
|
+
else
|
|
13564
|
+
q.if_break { q.text("(") }.if_flat { q.text(" ") }
|
|
13565
|
+
q.indent do
|
|
13566
|
+
q.breakable("")
|
|
13567
|
+
q.format(arguments)
|
|
13568
|
+
end
|
|
13569
|
+
q.breakable("")
|
|
13570
|
+
q.if_break { q.text(")") }
|
|
13571
|
+
end
|
|
13052
13572
|
end
|
|
13053
13573
|
end
|
|
13054
13574
|
|