syntax_tree 2.1.0 → 2.3.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 +48 -1
- data/Gemfile +0 -5
- data/Gemfile.lock +3 -15
- data/README.md +21 -0
- data/bin/bench +11 -6
- data/bin/profile +16 -7
- data/lib/syntax_tree/formatter.rb +8 -1
- data/lib/syntax_tree/node.rb +661 -97
- data/lib/syntax_tree/parser.rb +110 -27
- data/lib/syntax_tree/prettyprint.rb +25 -13
- data/lib/syntax_tree/version.rb +1 -1
- metadata +2 -2
data/lib/syntax_tree/node.rb
CHANGED
@@ -35,6 +35,21 @@ module SyntaxTree
|
|
35
35
|
)
|
36
36
|
end
|
37
37
|
|
38
|
+
def deconstruct
|
39
|
+
[start_line, start_char, start_column, end_line, end_char, end_column]
|
40
|
+
end
|
41
|
+
|
42
|
+
def deconstruct_keys(keys)
|
43
|
+
{
|
44
|
+
start_line: start_line,
|
45
|
+
start_char: start_char,
|
46
|
+
start_column: start_column,
|
47
|
+
end_line: end_line,
|
48
|
+
end_char: end_char,
|
49
|
+
end_column: end_column
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
38
53
|
def self.token(line:, char:, column:, size:)
|
39
54
|
new(
|
40
55
|
start_line: line,
|
@@ -801,6 +816,29 @@ module SyntaxTree
|
|
801
816
|
end
|
802
817
|
end
|
803
818
|
|
819
|
+
class EmptyWithCommentsFormatter
|
820
|
+
# [LBracket] the opening bracket
|
821
|
+
attr_reader :lbracket
|
822
|
+
|
823
|
+
def initialize(lbracket)
|
824
|
+
@lbracket = lbracket
|
825
|
+
end
|
826
|
+
|
827
|
+
def format(q)
|
828
|
+
q.group do
|
829
|
+
q.text("[")
|
830
|
+
q.indent do
|
831
|
+
lbracket.comments.each do |comment|
|
832
|
+
q.breakable(force: true)
|
833
|
+
comment.format(q)
|
834
|
+
end
|
835
|
+
end
|
836
|
+
q.breakable(force: true)
|
837
|
+
q.text("]")
|
838
|
+
end
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
804
842
|
# [LBracket] the bracket that opens this array
|
805
843
|
attr_reader :lbracket
|
806
844
|
|
@@ -852,6 +890,11 @@ module SyntaxTree
|
|
852
890
|
return
|
853
891
|
end
|
854
892
|
|
893
|
+
if empty_with_comments?
|
894
|
+
EmptyWithCommentsFormatter.new(lbracket).format(q)
|
895
|
+
return
|
896
|
+
end
|
897
|
+
|
855
898
|
q.group do
|
856
899
|
q.format(lbracket)
|
857
900
|
|
@@ -904,6 +947,12 @@ module SyntaxTree
|
|
904
947
|
q.maxwidth * 2
|
905
948
|
)
|
906
949
|
end
|
950
|
+
|
951
|
+
# If we have an empty array that contains only comments, then we're going
|
952
|
+
# to do some special printing to ensure they get indented correctly.
|
953
|
+
def empty_with_comments?
|
954
|
+
contents.nil? && lbracket.comments.any? && lbracket.comments.none?(&:inline?)
|
955
|
+
end
|
907
956
|
end
|
908
957
|
|
909
958
|
# AryPtn represents matching against an array pattern using the Ruby 2.7+
|
@@ -1027,17 +1076,16 @@ module SyntaxTree
|
|
1027
1076
|
# Determins if the following value should be indented or not.
|
1028
1077
|
module AssignFormatting
|
1029
1078
|
def self.skip_indent?(value)
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
].include?(value.class)
|
1079
|
+
case value
|
1080
|
+
in ArrayLiteral | HashLiteral | Heredoc | Lambda | QSymbols | QWords | Symbols | Words
|
1081
|
+
true
|
1082
|
+
in Call[receiver:]
|
1083
|
+
skip_indent?(receiver)
|
1084
|
+
in DynaSymbol[quote:]
|
1085
|
+
quote.start_with?("%s")
|
1086
|
+
else
|
1087
|
+
false
|
1088
|
+
end
|
1041
1089
|
end
|
1042
1090
|
end
|
1043
1091
|
|
@@ -1537,12 +1585,12 @@ module SyntaxTree
|
|
1537
1585
|
q.text(" ") unless power
|
1538
1586
|
|
1539
1587
|
if operator == :<<
|
1540
|
-
q.text(operator)
|
1588
|
+
q.text(operator.to_s)
|
1541
1589
|
q.text(" ")
|
1542
1590
|
q.format(right)
|
1543
1591
|
else
|
1544
1592
|
q.group do
|
1545
|
-
q.text(operator)
|
1593
|
+
q.text(operator.to_s)
|
1546
1594
|
|
1547
1595
|
q.indent do
|
1548
1596
|
q.breakable(power ? "" : " ")
|
@@ -2027,12 +2075,12 @@ module SyntaxTree
|
|
2027
2075
|
end
|
2028
2076
|
end
|
2029
2077
|
|
2030
|
-
# Formats either a Break or
|
2078
|
+
# Formats either a Break, Next, or Return node.
|
2031
2079
|
class FlowControlFormatter
|
2032
2080
|
# [String] the keyword to print
|
2033
2081
|
attr_reader :keyword
|
2034
2082
|
|
2035
|
-
# [Break | Next] the node being formatted
|
2083
|
+
# [Break | Next | Return] the node being formatted
|
2036
2084
|
attr_reader :node
|
2037
2085
|
|
2038
2086
|
def initialize(keyword, node)
|
@@ -2041,32 +2089,119 @@ module SyntaxTree
|
|
2041
2089
|
end
|
2042
2090
|
|
2043
2091
|
def format(q)
|
2044
|
-
arguments = node.arguments
|
2045
|
-
|
2046
2092
|
q.group do
|
2047
2093
|
q.text(keyword)
|
2048
2094
|
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2095
|
+
case node.arguments.parts
|
2096
|
+
in []
|
2097
|
+
# Here there are no arguments at all, so we're not going to print
|
2098
|
+
# anything. This would be like if we had:
|
2099
|
+
#
|
2100
|
+
# break
|
2101
|
+
#
|
2102
|
+
in [Paren[contents: { body: [ArrayLiteral[contents: { parts: [_, _, *] }] => array] }]]
|
2103
|
+
# Here we have a single argument that is a set of parentheses wrapping
|
2104
|
+
# an array literal that has at least 2 elements. We're going to print
|
2105
|
+
# the contents of the array directly. This would be like if we had:
|
2106
|
+
#
|
2107
|
+
# break([1, 2, 3])
|
2108
|
+
#
|
2109
|
+
# which we will print as:
|
2110
|
+
#
|
2111
|
+
# break 1, 2, 3
|
2112
|
+
#
|
2113
|
+
q.text(" ")
|
2114
|
+
format_array_contents(q, array)
|
2115
|
+
in [Paren[contents: { body: [ArrayLiteral => statement] }]]
|
2116
|
+
# Here we have a single argument that is a set of parentheses wrapping
|
2117
|
+
# an array literal that has 0 or 1 elements. We're going to skip the
|
2118
|
+
# parentheses but print the array itself. This would be like if we
|
2119
|
+
# had:
|
2120
|
+
#
|
2121
|
+
# break([1])
|
2122
|
+
#
|
2123
|
+
# which we will print as:
|
2124
|
+
#
|
2125
|
+
# break [1]
|
2126
|
+
#
|
2127
|
+
q.text(" ")
|
2128
|
+
q.format(statement)
|
2129
|
+
in [Paren[contents: { body: [statement] }]] if skip_parens?(statement)
|
2130
|
+
# Here we have a single argument that is a set of parentheses that
|
2131
|
+
# themselves contain a single statement. That statement is a simple
|
2132
|
+
# value that we can skip the parentheses for. This would be like if we
|
2133
|
+
# had:
|
2134
|
+
#
|
2135
|
+
# break(1)
|
2136
|
+
#
|
2137
|
+
# which we will print as:
|
2138
|
+
#
|
2139
|
+
# break 1
|
2140
|
+
#
|
2141
|
+
q.text(" ")
|
2142
|
+
q.format(statement)
|
2143
|
+
in [Paren => part]
|
2144
|
+
# Here we have a single argument that is a set of parentheses. We're
|
2145
|
+
# going to print the parentheses themselves as if they were the set of
|
2146
|
+
# arguments. This would be like if we had:
|
2147
|
+
#
|
2148
|
+
# break(foo.bar)
|
2149
|
+
#
|
2150
|
+
q.format(part)
|
2151
|
+
in [ArrayLiteral[contents: { parts: [_, _, *] }] => array]
|
2152
|
+
# Here there is a single argument that is an array literal with at
|
2153
|
+
# least two elements. We skip directly into the array literal's
|
2154
|
+
# elements in order to print the contents. This would be like if we
|
2155
|
+
# had:
|
2156
|
+
#
|
2157
|
+
# break [1, 2, 3]
|
2158
|
+
#
|
2159
|
+
# which we will print as:
|
2160
|
+
#
|
2161
|
+
# break 1, 2, 3
|
2162
|
+
#
|
2163
|
+
q.text(" ")
|
2164
|
+
format_array_contents(q, array)
|
2165
|
+
in [ArrayLiteral => part]
|
2166
|
+
# Here there is a single argument that is an array literal with 0 or 1
|
2167
|
+
# elements. In this case we're going to print the array as it is
|
2168
|
+
# because skipping the brackets would change the remaining. This would
|
2169
|
+
# be like if we had:
|
2170
|
+
#
|
2171
|
+
# break []
|
2172
|
+
# break [1]
|
2173
|
+
#
|
2174
|
+
q.text(" ")
|
2175
|
+
q.format(part)
|
2176
|
+
in [_]
|
2177
|
+
# Here there is a single argument that hasn't matched one of our
|
2178
|
+
# previous cases. We're going to print the argument as it is. This
|
2179
|
+
# would be like if we had:
|
2180
|
+
#
|
2181
|
+
# break foo
|
2182
|
+
#
|
2183
|
+
format_arguments(q, "(", ")")
|
2184
|
+
else
|
2185
|
+
# If there are multiple arguments, format them all. If the line is
|
2186
|
+
# going to break into multiple, then use brackets to start and end the
|
2187
|
+
# expression.
|
2188
|
+
format_arguments(q, " [", "]")
|
2064
2189
|
end
|
2065
2190
|
end
|
2066
2191
|
end
|
2067
2192
|
|
2068
2193
|
private
|
2069
2194
|
|
2195
|
+
def format_array_contents(q, array)
|
2196
|
+
q.if_break { q.text("[") }
|
2197
|
+
q.indent do
|
2198
|
+
q.breakable("")
|
2199
|
+
q.format(array.contents)
|
2200
|
+
end
|
2201
|
+
q.breakable("")
|
2202
|
+
q.if_break { q.text("]") }
|
2203
|
+
end
|
2204
|
+
|
2070
2205
|
def format_arguments(q, opening, closing)
|
2071
2206
|
q.if_break { q.text(opening) }
|
2072
2207
|
q.indent do
|
@@ -2076,6 +2211,17 @@ module SyntaxTree
|
|
2076
2211
|
q.breakable("")
|
2077
2212
|
q.if_break { q.text(closing) }
|
2078
2213
|
end
|
2214
|
+
|
2215
|
+
def skip_parens?(node)
|
2216
|
+
case node
|
2217
|
+
in FloatLiteral | Imaginary | Int | RationalLiteral
|
2218
|
+
true
|
2219
|
+
in VarRef[value: Const | CVar | GVar | IVar | Kw]
|
2220
|
+
true
|
2221
|
+
else
|
2222
|
+
false
|
2223
|
+
end
|
2224
|
+
end
|
2079
2225
|
end
|
2080
2226
|
|
2081
2227
|
# Break represents using the +break+ keyword.
|
@@ -2141,6 +2287,181 @@ module SyntaxTree
|
|
2141
2287
|
end
|
2142
2288
|
end
|
2143
2289
|
|
2290
|
+
class CallChainFormatter
|
2291
|
+
# [Call | MethodAddBlock] the top of the call chain
|
2292
|
+
attr_reader :node
|
2293
|
+
|
2294
|
+
def initialize(node)
|
2295
|
+
@node = node
|
2296
|
+
end
|
2297
|
+
|
2298
|
+
def format(q)
|
2299
|
+
children = [node]
|
2300
|
+
threshold = 3
|
2301
|
+
|
2302
|
+
# First, walk down the chain until we get to the point where we're not
|
2303
|
+
# longer at a chainable node.
|
2304
|
+
while true
|
2305
|
+
case children.last
|
2306
|
+
in Call[receiver: Call]
|
2307
|
+
children << children.last.receiver
|
2308
|
+
in Call[receiver: MethodAddBlock[call: Call]]
|
2309
|
+
children << children.last.receiver
|
2310
|
+
in MethodAddBlock[call: Call]
|
2311
|
+
children << children.last.call
|
2312
|
+
else
|
2313
|
+
break
|
2314
|
+
end
|
2315
|
+
end
|
2316
|
+
|
2317
|
+
# Here, we have very specialized behavior where if we're within a sig
|
2318
|
+
# block, then we're going to assume we're creating a Sorbet type
|
2319
|
+
# signature. In that case, we really want the threshold to be lowered so
|
2320
|
+
# that we create method chains off of any two method calls within the
|
2321
|
+
# block. For more details, see
|
2322
|
+
# https://github.com/prettier/plugin-ruby/issues/863.
|
2323
|
+
parents = q.parents.take(4)
|
2324
|
+
if parent = parents[2]
|
2325
|
+
# If we're at a do_block, then we want to go one more level up. This is
|
2326
|
+
# because do blocks have BodyStmt nodes instead of just Statements
|
2327
|
+
# nodes.
|
2328
|
+
parent = parents[3] if parent.is_a?(DoBlock)
|
2329
|
+
|
2330
|
+
case parent
|
2331
|
+
in MethodAddBlock[call: FCall[value: { value: "sig" }]]
|
2332
|
+
threshold = 2
|
2333
|
+
else
|
2334
|
+
end
|
2335
|
+
end
|
2336
|
+
|
2337
|
+
if children.length >= threshold
|
2338
|
+
q.group { q.if_break { format_chain(q, children) }.if_flat { node.format_contents(q) } }
|
2339
|
+
else
|
2340
|
+
node.format_contents(q)
|
2341
|
+
end
|
2342
|
+
end
|
2343
|
+
|
2344
|
+
def format_chain(q, children)
|
2345
|
+
# We're going to have some specialized behavior for if it's an entire
|
2346
|
+
# chain of calls without arguments except for the last one. This is common
|
2347
|
+
# enough in Ruby source code that it's worth the extra complexity here.
|
2348
|
+
empty_except_last =
|
2349
|
+
children.drop(1).all? do |child|
|
2350
|
+
child.is_a?(Call) && child.arguments.nil?
|
2351
|
+
end
|
2352
|
+
|
2353
|
+
# Here, we're going to add all of the children onto the stack of the
|
2354
|
+
# formatter so it's as if we had descending normally into them. This is
|
2355
|
+
# necessary so they can check their parents as normal.
|
2356
|
+
q.stack.concat(children)
|
2357
|
+
q.format(children.last.receiver)
|
2358
|
+
|
2359
|
+
q.group do
|
2360
|
+
if attach_directly?(children.last)
|
2361
|
+
format_child(q, children.pop)
|
2362
|
+
q.stack.pop
|
2363
|
+
end
|
2364
|
+
|
2365
|
+
q.indent do
|
2366
|
+
# We track another variable that checks if you need to move the
|
2367
|
+
# operator to the previous line in case there are trailing comments
|
2368
|
+
# and a trailing operator.
|
2369
|
+
skip_operator = false
|
2370
|
+
|
2371
|
+
while child = children.pop
|
2372
|
+
case child
|
2373
|
+
in Call[receiver: Call[message: { value: "where" }], message: { value: "not" }]
|
2374
|
+
# This is very specialized behavior wherein we group
|
2375
|
+
# .where.not calls together because it looks better. For more
|
2376
|
+
# information, see
|
2377
|
+
# https://github.com/prettier/plugin-ruby/issues/862.
|
2378
|
+
in Call
|
2379
|
+
# If we're at a Call node and not a MethodAddBlock node in the
|
2380
|
+
# chain then we're going to add a newline so it indents properly.
|
2381
|
+
q.breakable("")
|
2382
|
+
else
|
2383
|
+
end
|
2384
|
+
|
2385
|
+
format_child(
|
2386
|
+
q,
|
2387
|
+
child,
|
2388
|
+
skip_comments: children.empty?,
|
2389
|
+
skip_operator: skip_operator,
|
2390
|
+
skip_attached: empty_except_last && children.empty?
|
2391
|
+
)
|
2392
|
+
|
2393
|
+
# If the parent call node has a comment on the message then we need
|
2394
|
+
# to print the operator trailing in order to keep it working.
|
2395
|
+
case children.last
|
2396
|
+
in Call[message: { comments: [_, *] }, operator:]
|
2397
|
+
q.format(CallOperatorFormatter.new(operator))
|
2398
|
+
skip_operator = true
|
2399
|
+
else
|
2400
|
+
skip_operator = false
|
2401
|
+
end
|
2402
|
+
|
2403
|
+
# Pop off the formatter's stack so that it aligns with what would
|
2404
|
+
# have happened if we had been formatting normally.
|
2405
|
+
q.stack.pop
|
2406
|
+
end
|
2407
|
+
end
|
2408
|
+
end
|
2409
|
+
|
2410
|
+
if empty_except_last
|
2411
|
+
case node
|
2412
|
+
in Call
|
2413
|
+
node.format_arguments(q)
|
2414
|
+
in MethodAddBlock[block:]
|
2415
|
+
q.format(block)
|
2416
|
+
end
|
2417
|
+
end
|
2418
|
+
end
|
2419
|
+
|
2420
|
+
def self.chained?(node)
|
2421
|
+
case node
|
2422
|
+
in Call | MethodAddBlock[call: Call]
|
2423
|
+
true
|
2424
|
+
else
|
2425
|
+
false
|
2426
|
+
end
|
2427
|
+
end
|
2428
|
+
|
2429
|
+
private
|
2430
|
+
|
2431
|
+
# For certain nodes, we want to attach directly to the end and don't
|
2432
|
+
# want to indent the first call. So we'll pop off the first children and
|
2433
|
+
# format it separately here.
|
2434
|
+
def attach_directly?(node)
|
2435
|
+
[ArrayLiteral, HashLiteral, Heredoc, If, Unless, XStringLiteral]
|
2436
|
+
.include?(node.receiver.class)
|
2437
|
+
end
|
2438
|
+
|
2439
|
+
def format_child(q, child, skip_comments: false, skip_operator: false, skip_attached: false)
|
2440
|
+
# First, format the actual contents of the child.
|
2441
|
+
case child
|
2442
|
+
in Call
|
2443
|
+
q.group do
|
2444
|
+
q.format(CallOperatorFormatter.new(child.operator)) unless skip_operator
|
2445
|
+
q.format(child.message) if child.message != :call
|
2446
|
+
child.format_arguments(q) unless skip_attached
|
2447
|
+
end
|
2448
|
+
in MethodAddBlock
|
2449
|
+
q.format(child.block) unless skip_attached
|
2450
|
+
end
|
2451
|
+
|
2452
|
+
# If there are any comments on this node then we need to explicitly print
|
2453
|
+
# them out here since we're bypassing the normal comment printing.
|
2454
|
+
if child.comments.any? && !skip_comments
|
2455
|
+
child.comments.each do |comment|
|
2456
|
+
comment.inline? ? q.text(" ") : q.breakable
|
2457
|
+
comment.format(q)
|
2458
|
+
end
|
2459
|
+
|
2460
|
+
q.break_parent
|
2461
|
+
end
|
2462
|
+
end
|
2463
|
+
end
|
2464
|
+
|
2144
2465
|
# Call represents a method call.
|
2145
2466
|
#
|
2146
2467
|
# receiver.message
|
@@ -2204,6 +2525,29 @@ module SyntaxTree
|
|
2204
2525
|
end
|
2205
2526
|
|
2206
2527
|
def format(q)
|
2528
|
+
# If we're at the top of a call chain, then we're going to do some
|
2529
|
+
# specialized printing in case we can print it nicely. We _only_ do this
|
2530
|
+
# at the top of the chain to avoid weird recursion issues.
|
2531
|
+
if !CallChainFormatter.chained?(q.parent) && CallChainFormatter.chained?(receiver)
|
2532
|
+
q.group { q.if_break { CallChainFormatter.new(self).format(q) }.if_flat { format_contents(q) } }
|
2533
|
+
else
|
2534
|
+
format_contents(q)
|
2535
|
+
end
|
2536
|
+
end
|
2537
|
+
|
2538
|
+
def format_arguments(q)
|
2539
|
+
case arguments
|
2540
|
+
in ArgParen
|
2541
|
+
q.format(arguments)
|
2542
|
+
in Args
|
2543
|
+
q.text(" ")
|
2544
|
+
q.format(arguments)
|
2545
|
+
else
|
2546
|
+
# Do nothing if there are no arguments.
|
2547
|
+
end
|
2548
|
+
end
|
2549
|
+
|
2550
|
+
def format_contents(q)
|
2207
2551
|
call_operator = CallOperatorFormatter.new(operator)
|
2208
2552
|
|
2209
2553
|
q.group do
|
@@ -2226,7 +2570,7 @@ module SyntaxTree
|
|
2226
2570
|
q.format(message) if message != :call
|
2227
2571
|
end
|
2228
2572
|
|
2229
|
-
q
|
2573
|
+
format_arguments(q)
|
2230
2574
|
end
|
2231
2575
|
end
|
2232
2576
|
end
|
@@ -2492,7 +2836,7 @@ module SyntaxTree
|
|
2492
2836
|
end
|
2493
2837
|
|
2494
2838
|
alias deconstruct child_nodes
|
2495
|
-
|
2839
|
+
|
2496
2840
|
def deconstruct_keys(keys)
|
2497
2841
|
{ value: value, location: location }
|
2498
2842
|
end
|
@@ -2543,26 +2887,24 @@ module SyntaxTree
|
|
2543
2887
|
def format(q)
|
2544
2888
|
q.group do
|
2545
2889
|
q.format(message)
|
2546
|
-
q.
|
2547
|
-
|
2548
|
-
if align?(self)
|
2549
|
-
q.nest(message.value.length + 1) { q.format(arguments) }
|
2550
|
-
else
|
2551
|
-
q.format(arguments)
|
2552
|
-
end
|
2890
|
+
align(q, self) { q.format(arguments) }
|
2553
2891
|
end
|
2554
2892
|
end
|
2555
2893
|
|
2556
2894
|
private
|
2557
2895
|
|
2558
|
-
def align
|
2896
|
+
def align(q, node, &block)
|
2559
2897
|
case node.arguments
|
2560
2898
|
in Args[parts: [Def | Defs | DefEndless]]
|
2561
|
-
|
2899
|
+
q.text(" ")
|
2900
|
+
yield
|
2901
|
+
in Args[parts: [IfOp]]
|
2902
|
+
yield
|
2562
2903
|
in Args[parts: [Command => command]]
|
2563
|
-
align
|
2904
|
+
align(q, command, &block)
|
2564
2905
|
else
|
2565
|
-
|
2906
|
+
q.text(" ")
|
2907
|
+
q.nest(message.value.length + 1) { yield }
|
2566
2908
|
end
|
2567
2909
|
end
|
2568
2910
|
end
|
@@ -2634,9 +2976,15 @@ module SyntaxTree
|
|
2634
2976
|
q.format(message)
|
2635
2977
|
end
|
2636
2978
|
|
2637
|
-
|
2979
|
+
case arguments
|
2980
|
+
in Args[parts: [IfOp]]
|
2981
|
+
q.if_flat { q.text(" ") }
|
2982
|
+
q.format(arguments)
|
2983
|
+
in Args
|
2638
2984
|
q.text(" ")
|
2639
2985
|
q.nest(argument_alignment(q, doc)) { q.format(arguments) }
|
2986
|
+
else
|
2987
|
+
# If there are no arguments, print nothing.
|
2640
2988
|
end
|
2641
2989
|
end
|
2642
2990
|
end
|
@@ -3022,7 +3370,10 @@ module SyntaxTree
|
|
3022
3370
|
q.group do
|
3023
3371
|
q.text("def ")
|
3024
3372
|
q.format(name)
|
3025
|
-
|
3373
|
+
|
3374
|
+
if !params.is_a?(Params) || !params.empty? || params.comments.any?
|
3375
|
+
q.format(params)
|
3376
|
+
end
|
3026
3377
|
end
|
3027
3378
|
|
3028
3379
|
unless bodystmt.empty?
|
@@ -3242,7 +3593,10 @@ module SyntaxTree
|
|
3242
3593
|
q.format(target)
|
3243
3594
|
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
3244
3595
|
q.format(name)
|
3245
|
-
|
3596
|
+
|
3597
|
+
if !params.is_a?(Params) || !params.empty? || params.comments.any?
|
3598
|
+
q.format(params)
|
3599
|
+
end
|
3246
3600
|
end
|
3247
3601
|
|
3248
3602
|
unless bodystmt.empty?
|
@@ -3777,7 +4131,7 @@ module SyntaxTree
|
|
3777
4131
|
end
|
3778
4132
|
|
3779
4133
|
alias deconstruct child_nodes
|
3780
|
-
|
4134
|
+
|
3781
4135
|
def deconstruct_keys(keys)
|
3782
4136
|
{ value: value, location: location }
|
3783
4137
|
end
|
@@ -3807,7 +4161,7 @@ module SyntaxTree
|
|
3807
4161
|
end
|
3808
4162
|
|
3809
4163
|
alias deconstruct child_nodes
|
3810
|
-
|
4164
|
+
|
3811
4165
|
def deconstruct_keys(keys)
|
3812
4166
|
{ value: value, location: location }
|
3813
4167
|
end
|
@@ -3839,7 +4193,7 @@ module SyntaxTree
|
|
3839
4193
|
end
|
3840
4194
|
|
3841
4195
|
alias deconstruct child_nodes
|
3842
|
-
|
4196
|
+
|
3843
4197
|
def deconstruct_keys(keys)
|
3844
4198
|
{ value: value, location: location }
|
3845
4199
|
end
|
@@ -3987,7 +4341,15 @@ module SyntaxTree
|
|
3987
4341
|
|
3988
4342
|
def format(q)
|
3989
4343
|
q.format(value)
|
3990
|
-
|
4344
|
+
|
4345
|
+
if arguments.is_a?(ArgParen) && arguments.arguments.nil? && !value.is_a?(Const)
|
4346
|
+
# If you're using an explicit set of parentheses on something that looks
|
4347
|
+
# like a constant, then we need to match that in order to maintain valid
|
4348
|
+
# Ruby. For example, you could do something like Foo(), on which we
|
4349
|
+
# would need to keep the parentheses to make it look like a method call.
|
4350
|
+
else
|
4351
|
+
q.format(arguments)
|
4352
|
+
end
|
3991
4353
|
end
|
3992
4354
|
end
|
3993
4355
|
|
@@ -4260,6 +4622,29 @@ module SyntaxTree
|
|
4260
4622
|
# { key => value }
|
4261
4623
|
#
|
4262
4624
|
class HashLiteral < Node
|
4625
|
+
class EmptyWithCommentsFormatter
|
4626
|
+
# [LBrace] the opening brace
|
4627
|
+
attr_reader :lbrace
|
4628
|
+
|
4629
|
+
def initialize(lbrace)
|
4630
|
+
@lbrace = lbrace
|
4631
|
+
end
|
4632
|
+
|
4633
|
+
def format(q)
|
4634
|
+
q.group do
|
4635
|
+
q.text("{")
|
4636
|
+
q.indent do
|
4637
|
+
lbrace.comments.each do |comment|
|
4638
|
+
q.breakable(force: true)
|
4639
|
+
comment.format(q)
|
4640
|
+
end
|
4641
|
+
end
|
4642
|
+
q.breakable(force: true)
|
4643
|
+
q.text("}")
|
4644
|
+
end
|
4645
|
+
end
|
4646
|
+
end
|
4647
|
+
|
4263
4648
|
# [LBrace] the left brace that opens this hash
|
4264
4649
|
attr_reader :lbrace
|
4265
4650
|
|
@@ -4304,7 +4689,18 @@ module SyntaxTree
|
|
4304
4689
|
|
4305
4690
|
private
|
4306
4691
|
|
4692
|
+
# If we have an empty hash that contains only comments, then we're going
|
4693
|
+
# to do some special printing to ensure they get indented correctly.
|
4694
|
+
def empty_with_comments?
|
4695
|
+
assocs.empty? && lbrace.comments.any? && lbrace.comments.none?(&:inline?)
|
4696
|
+
end
|
4697
|
+
|
4307
4698
|
def format_contents(q)
|
4699
|
+
if empty_with_comments?
|
4700
|
+
EmptyWithCommentsFormatter.new(lbrace).format(q)
|
4701
|
+
return
|
4702
|
+
end
|
4703
|
+
|
4308
4704
|
q.format(lbrace)
|
4309
4705
|
|
4310
4706
|
if assocs.empty?
|
@@ -4334,6 +4730,9 @@ module SyntaxTree
|
|
4334
4730
|
# [String] the ending of the heredoc
|
4335
4731
|
attr_reader :ending
|
4336
4732
|
|
4733
|
+
# [Integer] how far to dedent the heredoc
|
4734
|
+
attr_reader :dedent
|
4735
|
+
|
4337
4736
|
# [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
|
4338
4737
|
# heredoc string literal
|
4339
4738
|
attr_reader :parts
|
@@ -4341,9 +4740,10 @@ module SyntaxTree
|
|
4341
4740
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
4342
4741
|
attr_reader :comments
|
4343
4742
|
|
4344
|
-
def initialize(beginning:, ending: nil, parts: [], location:, comments: [])
|
4743
|
+
def initialize(beginning:, ending: nil, dedent: 0, parts: [], location:, comments: [])
|
4345
4744
|
@beginning = beginning
|
4346
4745
|
@ending = ending
|
4746
|
+
@dedent = dedent
|
4347
4747
|
@parts = parts
|
4348
4748
|
@location = location
|
4349
4749
|
@comments = comments
|
@@ -4536,8 +4936,14 @@ module SyntaxTree
|
|
4536
4936
|
def format(q)
|
4537
4937
|
parts = keywords.map { |(key, value)| KeywordFormatter.new(key, value) }
|
4538
4938
|
parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
|
4939
|
+
|
4539
4940
|
contents = -> do
|
4540
|
-
q.seplist(parts) { |part| q.format(part, stackable: false) }
|
4941
|
+
q.group { q.seplist(parts) { |part| q.format(part, stackable: false) } }
|
4942
|
+
|
4943
|
+
# If there isn't a constant, and there's a blank keyword_rest, then we
|
4944
|
+
# have an plain ** that needs to have a `then` after it in order to
|
4945
|
+
# parse correctly on the next parse.
|
4946
|
+
q.text(" then") if !constant && keyword_rest && keyword_rest.value.nil?
|
4541
4947
|
end
|
4542
4948
|
|
4543
4949
|
if constant
|
@@ -4546,8 +4952,9 @@ module SyntaxTree
|
|
4546
4952
|
return
|
4547
4953
|
end
|
4548
4954
|
|
4549
|
-
|
4550
|
-
|
4955
|
+
if parts.empty?
|
4956
|
+
q.text("{}")
|
4957
|
+
elsif PATTERNS.include?(q.parent.class)
|
4551
4958
|
q.text("{ ")
|
4552
4959
|
contents.call
|
4553
4960
|
q.text(" }")
|
@@ -4615,6 +5022,62 @@ module SyntaxTree
|
|
4615
5022
|
end
|
4616
5023
|
end
|
4617
5024
|
|
5025
|
+
# In order for an `if` or `unless` expression to be shortened to a ternary,
|
5026
|
+
# there has to be one and only one consequent clause which is an Else. Both
|
5027
|
+
# the body of the main node and the body of the Else node must have only one
|
5028
|
+
# statement, and that statement must not be on the denied list of potential
|
5029
|
+
# statements.
|
5030
|
+
module Ternaryable
|
5031
|
+
class << self
|
5032
|
+
def call(q, node)
|
5033
|
+
case q.parents.take(2)[1]
|
5034
|
+
in Paren[contents: Statements[body: [node]]]
|
5035
|
+
# If this is a conditional inside of a parentheses as the only
|
5036
|
+
# content, then we don't want to transform it into a ternary.
|
5037
|
+
# Presumably the user wanted it to be an explicit conditional because
|
5038
|
+
# there are parentheses around it. So we'll just leave it in place.
|
5039
|
+
false
|
5040
|
+
else
|
5041
|
+
# Otherwise, we're going to check the conditional for certain cases.
|
5042
|
+
case node
|
5043
|
+
in { predicate: Assign | Command | CommandCall | MAssign | OpAssign }
|
5044
|
+
false
|
5045
|
+
in { statements: { body: [truthy] }, consequent: Else[statements: { body: [falsy] }] }
|
5046
|
+
ternaryable?(truthy) && ternaryable?(falsy)
|
5047
|
+
else
|
5048
|
+
false
|
5049
|
+
end
|
5050
|
+
end
|
5051
|
+
end
|
5052
|
+
|
5053
|
+
private
|
5054
|
+
|
5055
|
+
# Certain expressions cannot be reduced to a ternary without adding
|
5056
|
+
# parentheses around them. In this case we say they cannot be ternaried and
|
5057
|
+
# default instead to breaking them into multiple lines.
|
5058
|
+
def ternaryable?(statement)
|
5059
|
+
# This is a list of nodes that should not be allowed to be a part of a
|
5060
|
+
# ternary clause.
|
5061
|
+
no_ternary = [
|
5062
|
+
Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, IfOp,
|
5063
|
+
Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, Super,
|
5064
|
+
Undef, Unless, UnlessMod, Until, UntilMod, VarAlias, VoidStmt, While,
|
5065
|
+
WhileMod, Yield, Yield0, ZSuper
|
5066
|
+
]
|
5067
|
+
|
5068
|
+
# Here we're going to check that the only statement inside the
|
5069
|
+
# statements node is no a part of our denied list of nodes that can be
|
5070
|
+
# ternaries.
|
5071
|
+
#
|
5072
|
+
# If the user is using one of the lower precedence "and" or "or"
|
5073
|
+
# operators, then we can't use a ternary expression as it would break
|
5074
|
+
# the flow control.
|
5075
|
+
!no_ternary.include?(statement.class) &&
|
5076
|
+
!(statement.is_a?(Binary) && %i[and or].include?(statement.operator))
|
5077
|
+
end
|
5078
|
+
end
|
5079
|
+
end
|
5080
|
+
|
4618
5081
|
# Formats an If or Unless node.
|
4619
5082
|
class ConditionalFormatter
|
4620
5083
|
# [String] the keyword associated with this conditional
|
@@ -4629,6 +5092,13 @@ module SyntaxTree
|
|
4629
5092
|
end
|
4630
5093
|
|
4631
5094
|
def format(q)
|
5095
|
+
# If we can transform this node into a ternary, then we're going to print
|
5096
|
+
# a special version that uses the ternary operator if it fits on one line.
|
5097
|
+
if Ternaryable.call(q, node)
|
5098
|
+
format_ternary(q)
|
5099
|
+
return
|
5100
|
+
end
|
5101
|
+
|
4632
5102
|
# If the predicate of the conditional contains an assignment (in which
|
4633
5103
|
# case we can't know for certain that that assignment doesn't impact the
|
4634
5104
|
# statements inside the conditional) then we can't use the modifier form
|
@@ -4638,7 +5108,7 @@ module SyntaxTree
|
|
4638
5108
|
return
|
4639
5109
|
end
|
4640
5110
|
|
4641
|
-
if node.consequent || node.statements.empty?
|
5111
|
+
if node.consequent || node.statements.empty? || contains_conditional?
|
4642
5112
|
q.group { format_break(q, force: true) }
|
4643
5113
|
else
|
4644
5114
|
q.group do
|
@@ -4674,6 +5144,59 @@ module SyntaxTree
|
|
4674
5144
|
q.breakable(force: force)
|
4675
5145
|
q.text("end")
|
4676
5146
|
end
|
5147
|
+
|
5148
|
+
def format_ternary(q)
|
5149
|
+
q.group do
|
5150
|
+
q.if_break do
|
5151
|
+
q.text("#{keyword} ")
|
5152
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
5153
|
+
|
5154
|
+
q.indent do
|
5155
|
+
q.breakable
|
5156
|
+
q.format(node.statements)
|
5157
|
+
end
|
5158
|
+
|
5159
|
+
q.breakable
|
5160
|
+
q.group do
|
5161
|
+
q.format(node.consequent.keyword)
|
5162
|
+
q.indent do
|
5163
|
+
# This is a very special case of breakable where we want to force
|
5164
|
+
# it into the output but we _don't_ want to explicitly break the
|
5165
|
+
# parent. If a break-parent shows up in the tree, then it's going
|
5166
|
+
# to force it all the way up to the tree, which is going to negate
|
5167
|
+
# the ternary. Maybe this should be an option in prettyprint? As
|
5168
|
+
# in force: :no_break_parent or something.
|
5169
|
+
q.target << PrettyPrint::Breakable.new(" ", 1, force: true)
|
5170
|
+
q.format(node.consequent.statements)
|
5171
|
+
end
|
5172
|
+
end
|
5173
|
+
|
5174
|
+
q.breakable
|
5175
|
+
q.text("end")
|
5176
|
+
end.if_flat do
|
5177
|
+
Parentheses.flat(q) do
|
5178
|
+
q.format(node.predicate)
|
5179
|
+
q.text(" ? ")
|
5180
|
+
|
5181
|
+
statements = [node.statements, node.consequent.statements]
|
5182
|
+
statements.reverse! if keyword == "unless"
|
5183
|
+
|
5184
|
+
q.format(statements[0])
|
5185
|
+
q.text(" : ")
|
5186
|
+
q.format(statements[1])
|
5187
|
+
end
|
5188
|
+
end
|
5189
|
+
end
|
5190
|
+
end
|
5191
|
+
|
5192
|
+
def contains_conditional?
|
5193
|
+
case node
|
5194
|
+
in { statements: { body: [If | IfMod | IfOp | Unless | UnlessMod] } }
|
5195
|
+
true
|
5196
|
+
else
|
5197
|
+
false
|
5198
|
+
end
|
5199
|
+
end
|
4677
5200
|
end
|
4678
5201
|
|
4679
5202
|
# If represents the first clause in an +if+ chain.
|
@@ -4786,7 +5309,7 @@ module SyntaxTree
|
|
4786
5309
|
Yield0, ZSuper
|
4787
5310
|
]
|
4788
5311
|
|
4789
|
-
if force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
5312
|
+
if q.parent.is_a?(Paren) || force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
4790
5313
|
q.group { format_flat(q) }
|
4791
5314
|
return
|
4792
5315
|
end
|
@@ -5259,7 +5782,7 @@ module SyntaxTree
|
|
5259
5782
|
end
|
5260
5783
|
|
5261
5784
|
alias deconstruct child_nodes
|
5262
|
-
|
5785
|
+
|
5263
5786
|
def deconstruct_keys(keys)
|
5264
5787
|
{ value: value, location: location }
|
5265
5788
|
end
|
@@ -5533,6 +6056,17 @@ module SyntaxTree
|
|
5533
6056
|
end
|
5534
6057
|
|
5535
6058
|
def format(q)
|
6059
|
+
# If we're at the top of a call chain, then we're going to do some
|
6060
|
+
# specialized printing in case we can print it nicely. We _only_ do this
|
6061
|
+
# at the top of the chain to avoid weird recursion issues.
|
6062
|
+
if !CallChainFormatter.chained?(q.parent) && CallChainFormatter.chained?(call)
|
6063
|
+
q.group { q.if_break { CallChainFormatter.new(self).format(q) }.if_flat { format_contents(q) } }
|
6064
|
+
else
|
6065
|
+
format_contents(q)
|
6066
|
+
end
|
6067
|
+
end
|
6068
|
+
|
6069
|
+
def format_contents(q)
|
5536
6070
|
q.format(call)
|
5537
6071
|
q.format(block)
|
5538
6072
|
end
|
@@ -5592,11 +6126,17 @@ module SyntaxTree
|
|
5592
6126
|
# [MLHS | MLHSParen] the contents inside of the parentheses
|
5593
6127
|
attr_reader :contents
|
5594
6128
|
|
6129
|
+
# [boolean] whether or not there is a trailing comma at the end of this
|
6130
|
+
# list, which impacts destructuring. It's an attr_accessor so that while
|
6131
|
+
# the syntax tree is being built it can be set by its parent node
|
6132
|
+
attr_accessor :comma
|
6133
|
+
|
5595
6134
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
5596
6135
|
attr_reader :comments
|
5597
6136
|
|
5598
|
-
def initialize(contents:, location:, comments: [])
|
6137
|
+
def initialize(contents:, comma: false, location:, comments: [])
|
5599
6138
|
@contents = contents
|
6139
|
+
@comma = comma
|
5600
6140
|
@location = location
|
5601
6141
|
@comments = comments
|
5602
6142
|
end
|
@@ -5620,6 +6160,7 @@ module SyntaxTree
|
|
5620
6160
|
|
5621
6161
|
if parent.is_a?(MAssign) || parent.is_a?(MLHSParen)
|
5622
6162
|
q.format(contents)
|
6163
|
+
q.text(",") if comma
|
5623
6164
|
else
|
5624
6165
|
q.group(0, "(", ")") do
|
5625
6166
|
q.indent do
|
@@ -5627,6 +6168,7 @@ module SyntaxTree
|
|
5627
6168
|
q.format(contents)
|
5628
6169
|
end
|
5629
6170
|
|
6171
|
+
q.text(",") if comma
|
5630
6172
|
q.breakable("")
|
5631
6173
|
end
|
5632
6174
|
end
|
@@ -6146,7 +6688,9 @@ module SyntaxTree
|
|
6146
6688
|
q.format(rest) if rest && rest.is_a?(ExcessedComma)
|
6147
6689
|
end
|
6148
6690
|
|
6149
|
-
if [Def, Defs, DefEndless].include?(q.parent.class)
|
6691
|
+
if ![Def, Defs, DefEndless].include?(q.parent.class) || parts.empty?
|
6692
|
+
q.nest(0, &contents)
|
6693
|
+
else
|
6150
6694
|
q.group(0, "(", ")") do
|
6151
6695
|
q.indent do
|
6152
6696
|
q.breakable("")
|
@@ -6154,8 +6698,6 @@ module SyntaxTree
|
|
6154
6698
|
end
|
6155
6699
|
q.breakable("")
|
6156
6700
|
end
|
6157
|
-
else
|
6158
|
-
q.nest(0, &contents)
|
6159
6701
|
end
|
6160
6702
|
end
|
6161
6703
|
end
|
@@ -6376,7 +6918,7 @@ module SyntaxTree
|
|
6376
6918
|
end
|
6377
6919
|
|
6378
6920
|
alias deconstruct child_nodes
|
6379
|
-
|
6921
|
+
|
6380
6922
|
def deconstruct_keys(keys)
|
6381
6923
|
{ value: value, location: location }
|
6382
6924
|
end
|
@@ -6476,7 +7018,7 @@ module SyntaxTree
|
|
6476
7018
|
end
|
6477
7019
|
|
6478
7020
|
alias deconstruct child_nodes
|
6479
|
-
|
7021
|
+
|
6480
7022
|
def deconstruct_keys(keys)
|
6481
7023
|
{ value: value, location: location }
|
6482
7024
|
end
|
@@ -6537,7 +7079,7 @@ module SyntaxTree
|
|
6537
7079
|
end
|
6538
7080
|
|
6539
7081
|
alias deconstruct child_nodes
|
6540
|
-
|
7082
|
+
|
6541
7083
|
def deconstruct_keys(keys)
|
6542
7084
|
{ value: value, location: location }
|
6543
7085
|
end
|
@@ -6562,7 +7104,7 @@ module SyntaxTree
|
|
6562
7104
|
end
|
6563
7105
|
|
6564
7106
|
alias deconstruct child_nodes
|
6565
|
-
|
7107
|
+
|
6566
7108
|
def deconstruct_keys(keys)
|
6567
7109
|
{ value: value, location: location }
|
6568
7110
|
end
|
@@ -6633,7 +7175,7 @@ module SyntaxTree
|
|
6633
7175
|
end
|
6634
7176
|
|
6635
7177
|
alias deconstruct child_nodes
|
6636
|
-
|
7178
|
+
|
6637
7179
|
def deconstruct_keys(keys)
|
6638
7180
|
{ beginning: beginning, parts: parts, location: location }
|
6639
7181
|
end
|
@@ -6666,7 +7208,7 @@ module SyntaxTree
|
|
6666
7208
|
end
|
6667
7209
|
|
6668
7210
|
alias deconstruct child_nodes
|
6669
|
-
|
7211
|
+
|
6670
7212
|
def deconstruct_keys(keys)
|
6671
7213
|
{ value: value, location: location }
|
6672
7214
|
end
|
@@ -6700,7 +7242,7 @@ module SyntaxTree
|
|
6700
7242
|
end
|
6701
7243
|
|
6702
7244
|
alias deconstruct child_nodes
|
6703
|
-
|
7245
|
+
|
6704
7246
|
def deconstruct_keys(keys)
|
6705
7247
|
{ value: value, location: location }
|
6706
7248
|
end
|
@@ -7186,7 +7728,7 @@ module SyntaxTree
|
|
7186
7728
|
@value = value
|
7187
7729
|
@location = location
|
7188
7730
|
end
|
7189
|
-
|
7731
|
+
|
7190
7732
|
def accept(visitor)
|
7191
7733
|
visitor.visit_rparen(self)
|
7192
7734
|
end
|
@@ -7196,7 +7738,7 @@ module SyntaxTree
|
|
7196
7738
|
end
|
7197
7739
|
|
7198
7740
|
alias deconstruct child_nodes
|
7199
|
-
|
7741
|
+
|
7200
7742
|
def deconstruct_keys(keys)
|
7201
7743
|
{ value: value, location: location }
|
7202
7744
|
end
|
@@ -7408,19 +7950,20 @@ module SyntaxTree
|
|
7408
7950
|
comment = parser_comments[comment_index]
|
7409
7951
|
location = comment.location
|
7410
7952
|
|
7411
|
-
if !comment.inline? && (start_char <= location.start_char) &&
|
7412
|
-
|
7413
|
-
parser_comments.delete_at(comment_index)
|
7414
|
-
|
7415
|
-
while (node = body[body_index]) &&
|
7416
|
-
(
|
7417
|
-
node.is_a?(VoidStmt) ||
|
7418
|
-
node.location.start_char < location.start_char
|
7419
|
-
)
|
7953
|
+
if !comment.inline? && (start_char <= location.start_char) && (end_char >= location.end_char) && !comment.ignore?
|
7954
|
+
while (node = body[body_index]) && (node.is_a?(VoidStmt) || node.location.start_char < location.start_char)
|
7420
7955
|
body_index += 1
|
7421
7956
|
end
|
7422
7957
|
|
7423
|
-
body.
|
7958
|
+
if body_index != 0 && body[body_index - 1].location.start_char < location.start_char && body[body_index - 1].location.end_char > location.start_char
|
7959
|
+
# The previous node entirely encapsules the comment, so we don't
|
7960
|
+
# want to attach it here since it will get attached normally. This
|
7961
|
+
# is mostly in the case of hash and array literals.
|
7962
|
+
comment_index += 1
|
7963
|
+
else
|
7964
|
+
parser_comments.delete_at(comment_index)
|
7965
|
+
body.insert(body_index, comment)
|
7966
|
+
end
|
7424
7967
|
else
|
7425
7968
|
comment_index += 1
|
7426
7969
|
end
|
@@ -7451,7 +7994,7 @@ module SyntaxTree
|
|
7451
7994
|
end
|
7452
7995
|
|
7453
7996
|
alias deconstruct child_nodes
|
7454
|
-
|
7997
|
+
|
7455
7998
|
def deconstruct_keys(keys)
|
7456
7999
|
{ parts: parts, location: location }
|
7457
8000
|
end
|
@@ -7748,7 +8291,7 @@ module SyntaxTree
|
|
7748
8291
|
end
|
7749
8292
|
|
7750
8293
|
alias deconstruct child_nodes
|
7751
|
-
|
8294
|
+
|
7752
8295
|
def deconstruct_keys(keys)
|
7753
8296
|
{ value: value, location: location }
|
7754
8297
|
end
|
@@ -7778,7 +8321,7 @@ module SyntaxTree
|
|
7778
8321
|
end
|
7779
8322
|
|
7780
8323
|
alias deconstruct child_nodes
|
7781
|
-
|
8324
|
+
|
7782
8325
|
def deconstruct_keys(keys)
|
7783
8326
|
{ value: value, location: location }
|
7784
8327
|
end
|
@@ -7909,7 +8452,7 @@ module SyntaxTree
|
|
7909
8452
|
end
|
7910
8453
|
|
7911
8454
|
alias deconstruct child_nodes
|
7912
|
-
|
8455
|
+
|
7913
8456
|
def deconstruct_keys(keys)
|
7914
8457
|
{ value: value, location: location }
|
7915
8458
|
end
|
@@ -7938,7 +8481,7 @@ module SyntaxTree
|
|
7938
8481
|
end
|
7939
8482
|
|
7940
8483
|
alias deconstruct child_nodes
|
7941
|
-
|
8484
|
+
|
7942
8485
|
def deconstruct_keys(keys)
|
7943
8486
|
{ value: value, location: location }
|
7944
8487
|
end
|
@@ -7968,7 +8511,7 @@ module SyntaxTree
|
|
7968
8511
|
end
|
7969
8512
|
|
7970
8513
|
alias deconstruct child_nodes
|
7971
|
-
|
8514
|
+
|
7972
8515
|
def deconstruct_keys(keys)
|
7973
8516
|
{ value: value, location: location }
|
7974
8517
|
end
|
@@ -8079,7 +8622,7 @@ module SyntaxTree
|
|
8079
8622
|
end
|
8080
8623
|
|
8081
8624
|
alias deconstruct child_nodes
|
8082
|
-
|
8625
|
+
|
8083
8626
|
def deconstruct_keys(keys)
|
8084
8627
|
{ value: value, location: location }
|
8085
8628
|
end
|
@@ -8157,7 +8700,7 @@ module SyntaxTree
|
|
8157
8700
|
end
|
8158
8701
|
|
8159
8702
|
alias deconstruct child_nodes
|
8160
|
-
|
8703
|
+
|
8161
8704
|
def deconstruct_keys(keys)
|
8162
8705
|
{ value: value, location: location }
|
8163
8706
|
end
|
@@ -8168,7 +8711,7 @@ module SyntaxTree
|
|
8168
8711
|
# not value
|
8169
8712
|
#
|
8170
8713
|
class Not < Node
|
8171
|
-
# [untyped] the statement on which to operate
|
8714
|
+
# [nil | untyped] the statement on which to operate
|
8172
8715
|
attr_reader :statement
|
8173
8716
|
|
8174
8717
|
# [boolean] whether or not parentheses were used
|
@@ -8204,9 +8747,26 @@ module SyntaxTree
|
|
8204
8747
|
end
|
8205
8748
|
|
8206
8749
|
def format(q)
|
8207
|
-
q.
|
8208
|
-
|
8209
|
-
|
8750
|
+
parent = q.parents.take(2)[1]
|
8751
|
+
ternary = (parent.is_a?(If) || parent.is_a?(Unless)) && Ternaryable.call(q, parent)
|
8752
|
+
|
8753
|
+
q.text("not")
|
8754
|
+
|
8755
|
+
if parentheses
|
8756
|
+
q.text("(")
|
8757
|
+
elsif ternary
|
8758
|
+
q.if_break { q.text(" ") }.if_flat { q.text("(") }
|
8759
|
+
else
|
8760
|
+
q.text(" ")
|
8761
|
+
end
|
8762
|
+
|
8763
|
+
q.format(statement) if statement
|
8764
|
+
|
8765
|
+
if parentheses
|
8766
|
+
q.text(")")
|
8767
|
+
elsif ternary
|
8768
|
+
q.if_flat { q.text(")") }
|
8769
|
+
end
|
8210
8770
|
end
|
8211
8771
|
end
|
8212
8772
|
|
@@ -8675,7 +9235,11 @@ module SyntaxTree
|
|
8675
9235
|
end
|
8676
9236
|
|
8677
9237
|
def format(q)
|
8678
|
-
|
9238
|
+
if value == :nil
|
9239
|
+
q.text("nil")
|
9240
|
+
elsif value
|
9241
|
+
q.format(value)
|
9242
|
+
end
|
8679
9243
|
end
|
8680
9244
|
end
|
8681
9245
|
|
@@ -9178,7 +9742,7 @@ module SyntaxTree
|
|
9178
9742
|
end
|
9179
9743
|
|
9180
9744
|
alias deconstruct child_nodes
|
9181
|
-
|
9745
|
+
|
9182
9746
|
def deconstruct_keys(keys)
|
9183
9747
|
{ value: value, location: location }
|
9184
9748
|
end
|
@@ -9207,7 +9771,7 @@ module SyntaxTree
|
|
9207
9771
|
end
|
9208
9772
|
|
9209
9773
|
alias deconstruct child_nodes
|
9210
|
-
|
9774
|
+
|
9211
9775
|
def deconstruct_keys(keys)
|
9212
9776
|
{ parts: parts, location: location }
|
9213
9777
|
end
|