syntax_tree 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|