syntax_tree 2.1.1 → 2.3.1
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 +43 -1
- data/Gemfile +0 -5
- data/Gemfile.lock +3 -15
- data/bin/bench +11 -6
- data/bin/profile +16 -7
- data/lib/syntax_tree/formatter.rb +8 -1
- data/lib/syntax_tree/node.rb +651 -92
- data/lib/syntax_tree/parser.rb +81 -8
- data/lib/syntax_tree/prettyprint.rb +25 -13
- data/lib/syntax_tree/version.rb +1 -1
- metadata +3 -3
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,25 @@ 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
|
+
q.if_flat { q.text(" ") }
|
2903
|
+
yield
|
2562
2904
|
in Args[parts: [Command => command]]
|
2563
|
-
align
|
2905
|
+
align(q, command, &block)
|
2564
2906
|
else
|
2565
|
-
|
2907
|
+
q.text(" ")
|
2908
|
+
q.nest(message.value.length + 1) { yield }
|
2566
2909
|
end
|
2567
2910
|
end
|
2568
2911
|
end
|
@@ -2634,9 +2977,15 @@ module SyntaxTree
|
|
2634
2977
|
q.format(message)
|
2635
2978
|
end
|
2636
2979
|
|
2637
|
-
|
2980
|
+
case arguments
|
2981
|
+
in Args[parts: [IfOp]]
|
2982
|
+
q.if_flat { q.text(" ") }
|
2983
|
+
q.format(arguments)
|
2984
|
+
in Args
|
2638
2985
|
q.text(" ")
|
2639
2986
|
q.nest(argument_alignment(q, doc)) { q.format(arguments) }
|
2987
|
+
else
|
2988
|
+
# If there are no arguments, print nothing.
|
2640
2989
|
end
|
2641
2990
|
end
|
2642
2991
|
end
|
@@ -3022,7 +3371,10 @@ module SyntaxTree
|
|
3022
3371
|
q.group do
|
3023
3372
|
q.text("def ")
|
3024
3373
|
q.format(name)
|
3025
|
-
|
3374
|
+
|
3375
|
+
if !params.is_a?(Params) || !params.empty? || params.comments.any?
|
3376
|
+
q.format(params)
|
3377
|
+
end
|
3026
3378
|
end
|
3027
3379
|
|
3028
3380
|
unless bodystmt.empty?
|
@@ -3242,7 +3594,10 @@ module SyntaxTree
|
|
3242
3594
|
q.format(target)
|
3243
3595
|
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
3244
3596
|
q.format(name)
|
3245
|
-
|
3597
|
+
|
3598
|
+
if !params.is_a?(Params) || !params.empty? || params.comments.any?
|
3599
|
+
q.format(params)
|
3600
|
+
end
|
3246
3601
|
end
|
3247
3602
|
|
3248
3603
|
unless bodystmt.empty?
|
@@ -3777,7 +4132,7 @@ module SyntaxTree
|
|
3777
4132
|
end
|
3778
4133
|
|
3779
4134
|
alias deconstruct child_nodes
|
3780
|
-
|
4135
|
+
|
3781
4136
|
def deconstruct_keys(keys)
|
3782
4137
|
{ value: value, location: location }
|
3783
4138
|
end
|
@@ -3807,7 +4162,7 @@ module SyntaxTree
|
|
3807
4162
|
end
|
3808
4163
|
|
3809
4164
|
alias deconstruct child_nodes
|
3810
|
-
|
4165
|
+
|
3811
4166
|
def deconstruct_keys(keys)
|
3812
4167
|
{ value: value, location: location }
|
3813
4168
|
end
|
@@ -3839,7 +4194,7 @@ module SyntaxTree
|
|
3839
4194
|
end
|
3840
4195
|
|
3841
4196
|
alias deconstruct child_nodes
|
3842
|
-
|
4197
|
+
|
3843
4198
|
def deconstruct_keys(keys)
|
3844
4199
|
{ value: value, location: location }
|
3845
4200
|
end
|
@@ -3987,7 +4342,15 @@ module SyntaxTree
|
|
3987
4342
|
|
3988
4343
|
def format(q)
|
3989
4344
|
q.format(value)
|
3990
|
-
|
4345
|
+
|
4346
|
+
if arguments.is_a?(ArgParen) && arguments.arguments.nil? && !value.is_a?(Const)
|
4347
|
+
# If you're using an explicit set of parentheses on something that looks
|
4348
|
+
# like a constant, then we need to match that in order to maintain valid
|
4349
|
+
# Ruby. For example, you could do something like Foo(), on which we
|
4350
|
+
# would need to keep the parentheses to make it look like a method call.
|
4351
|
+
else
|
4352
|
+
q.format(arguments)
|
4353
|
+
end
|
3991
4354
|
end
|
3992
4355
|
end
|
3993
4356
|
|
@@ -4260,6 +4623,29 @@ module SyntaxTree
|
|
4260
4623
|
# { key => value }
|
4261
4624
|
#
|
4262
4625
|
class HashLiteral < Node
|
4626
|
+
class EmptyWithCommentsFormatter
|
4627
|
+
# [LBrace] the opening brace
|
4628
|
+
attr_reader :lbrace
|
4629
|
+
|
4630
|
+
def initialize(lbrace)
|
4631
|
+
@lbrace = lbrace
|
4632
|
+
end
|
4633
|
+
|
4634
|
+
def format(q)
|
4635
|
+
q.group do
|
4636
|
+
q.text("{")
|
4637
|
+
q.indent do
|
4638
|
+
lbrace.comments.each do |comment|
|
4639
|
+
q.breakable(force: true)
|
4640
|
+
comment.format(q)
|
4641
|
+
end
|
4642
|
+
end
|
4643
|
+
q.breakable(force: true)
|
4644
|
+
q.text("}")
|
4645
|
+
end
|
4646
|
+
end
|
4647
|
+
end
|
4648
|
+
|
4263
4649
|
# [LBrace] the left brace that opens this hash
|
4264
4650
|
attr_reader :lbrace
|
4265
4651
|
|
@@ -4304,7 +4690,18 @@ module SyntaxTree
|
|
4304
4690
|
|
4305
4691
|
private
|
4306
4692
|
|
4693
|
+
# If we have an empty hash that contains only comments, then we're going
|
4694
|
+
# to do some special printing to ensure they get indented correctly.
|
4695
|
+
def empty_with_comments?
|
4696
|
+
assocs.empty? && lbrace.comments.any? && lbrace.comments.none?(&:inline?)
|
4697
|
+
end
|
4698
|
+
|
4307
4699
|
def format_contents(q)
|
4700
|
+
if empty_with_comments?
|
4701
|
+
EmptyWithCommentsFormatter.new(lbrace).format(q)
|
4702
|
+
return
|
4703
|
+
end
|
4704
|
+
|
4308
4705
|
q.format(lbrace)
|
4309
4706
|
|
4310
4707
|
if assocs.empty?
|
@@ -4334,6 +4731,9 @@ module SyntaxTree
|
|
4334
4731
|
# [String] the ending of the heredoc
|
4335
4732
|
attr_reader :ending
|
4336
4733
|
|
4734
|
+
# [Integer] how far to dedent the heredoc
|
4735
|
+
attr_reader :dedent
|
4736
|
+
|
4337
4737
|
# [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the
|
4338
4738
|
# heredoc string literal
|
4339
4739
|
attr_reader :parts
|
@@ -4341,9 +4741,10 @@ module SyntaxTree
|
|
4341
4741
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
4342
4742
|
attr_reader :comments
|
4343
4743
|
|
4344
|
-
def initialize(beginning:, ending: nil, parts: [], location:, comments: [])
|
4744
|
+
def initialize(beginning:, ending: nil, dedent: 0, parts: [], location:, comments: [])
|
4345
4745
|
@beginning = beginning
|
4346
4746
|
@ending = ending
|
4747
|
+
@dedent = dedent
|
4347
4748
|
@parts = parts
|
4348
4749
|
@location = location
|
4349
4750
|
@comments = comments
|
@@ -4538,7 +4939,12 @@ module SyntaxTree
|
|
4538
4939
|
parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
|
4539
4940
|
|
4540
4941
|
contents = -> do
|
4541
|
-
q.seplist(parts) { |part| q.format(part, stackable: false) }
|
4942
|
+
q.group { q.seplist(parts) { |part| q.format(part, stackable: false) } }
|
4943
|
+
|
4944
|
+
# If there isn't a constant, and there's a blank keyword_rest, then we
|
4945
|
+
# have an plain ** that needs to have a `then` after it in order to
|
4946
|
+
# parse correctly on the next parse.
|
4947
|
+
q.text(" then") if !constant && keyword_rest && keyword_rest.value.nil?
|
4542
4948
|
end
|
4543
4949
|
|
4544
4950
|
if constant
|
@@ -4617,6 +5023,62 @@ module SyntaxTree
|
|
4617
5023
|
end
|
4618
5024
|
end
|
4619
5025
|
|
5026
|
+
# In order for an `if` or `unless` expression to be shortened to a ternary,
|
5027
|
+
# there has to be one and only one consequent clause which is an Else. Both
|
5028
|
+
# the body of the main node and the body of the Else node must have only one
|
5029
|
+
# statement, and that statement must not be on the denied list of potential
|
5030
|
+
# statements.
|
5031
|
+
module Ternaryable
|
5032
|
+
class << self
|
5033
|
+
def call(q, node)
|
5034
|
+
case q.parents.take(2)[1]
|
5035
|
+
in Paren[contents: Statements[body: [node]]]
|
5036
|
+
# If this is a conditional inside of a parentheses as the only
|
5037
|
+
# content, then we don't want to transform it into a ternary.
|
5038
|
+
# Presumably the user wanted it to be an explicit conditional because
|
5039
|
+
# there are parentheses around it. So we'll just leave it in place.
|
5040
|
+
false
|
5041
|
+
else
|
5042
|
+
# Otherwise, we're going to check the conditional for certain cases.
|
5043
|
+
case node
|
5044
|
+
in { predicate: Assign | Command | CommandCall | MAssign | OpAssign }
|
5045
|
+
false
|
5046
|
+
in { statements: { body: [truthy] }, consequent: Else[statements: { body: [falsy] }] }
|
5047
|
+
ternaryable?(truthy) && ternaryable?(falsy)
|
5048
|
+
else
|
5049
|
+
false
|
5050
|
+
end
|
5051
|
+
end
|
5052
|
+
end
|
5053
|
+
|
5054
|
+
private
|
5055
|
+
|
5056
|
+
# Certain expressions cannot be reduced to a ternary without adding
|
5057
|
+
# parentheses around them. In this case we say they cannot be ternaried and
|
5058
|
+
# default instead to breaking them into multiple lines.
|
5059
|
+
def ternaryable?(statement)
|
5060
|
+
# This is a list of nodes that should not be allowed to be a part of a
|
5061
|
+
# ternary clause.
|
5062
|
+
no_ternary = [
|
5063
|
+
Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, IfOp,
|
5064
|
+
Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, Super,
|
5065
|
+
Undef, Unless, UnlessMod, Until, UntilMod, VarAlias, VoidStmt, While,
|
5066
|
+
WhileMod, Yield, Yield0, ZSuper
|
5067
|
+
]
|
5068
|
+
|
5069
|
+
# Here we're going to check that the only statement inside the
|
5070
|
+
# statements node is no a part of our denied list of nodes that can be
|
5071
|
+
# ternaries.
|
5072
|
+
#
|
5073
|
+
# If the user is using one of the lower precedence "and" or "or"
|
5074
|
+
# operators, then we can't use a ternary expression as it would break
|
5075
|
+
# the flow control.
|
5076
|
+
!no_ternary.include?(statement.class) &&
|
5077
|
+
!(statement.is_a?(Binary) && %i[and or].include?(statement.operator))
|
5078
|
+
end
|
5079
|
+
end
|
5080
|
+
end
|
5081
|
+
|
4620
5082
|
# Formats an If or Unless node.
|
4621
5083
|
class ConditionalFormatter
|
4622
5084
|
# [String] the keyword associated with this conditional
|
@@ -4631,6 +5093,13 @@ module SyntaxTree
|
|
4631
5093
|
end
|
4632
5094
|
|
4633
5095
|
def format(q)
|
5096
|
+
# If we can transform this node into a ternary, then we're going to print
|
5097
|
+
# a special version that uses the ternary operator if it fits on one line.
|
5098
|
+
if Ternaryable.call(q, node)
|
5099
|
+
format_ternary(q)
|
5100
|
+
return
|
5101
|
+
end
|
5102
|
+
|
4634
5103
|
# If the predicate of the conditional contains an assignment (in which
|
4635
5104
|
# case we can't know for certain that that assignment doesn't impact the
|
4636
5105
|
# statements inside the conditional) then we can't use the modifier form
|
@@ -4640,7 +5109,7 @@ module SyntaxTree
|
|
4640
5109
|
return
|
4641
5110
|
end
|
4642
5111
|
|
4643
|
-
if node.consequent || node.statements.empty?
|
5112
|
+
if node.consequent || node.statements.empty? || contains_conditional?
|
4644
5113
|
q.group { format_break(q, force: true) }
|
4645
5114
|
else
|
4646
5115
|
q.group do
|
@@ -4676,6 +5145,59 @@ module SyntaxTree
|
|
4676
5145
|
q.breakable(force: force)
|
4677
5146
|
q.text("end")
|
4678
5147
|
end
|
5148
|
+
|
5149
|
+
def format_ternary(q)
|
5150
|
+
q.group do
|
5151
|
+
q.if_break do
|
5152
|
+
q.text("#{keyword} ")
|
5153
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
5154
|
+
|
5155
|
+
q.indent do
|
5156
|
+
q.breakable
|
5157
|
+
q.format(node.statements)
|
5158
|
+
end
|
5159
|
+
|
5160
|
+
q.breakable
|
5161
|
+
q.group do
|
5162
|
+
q.format(node.consequent.keyword)
|
5163
|
+
q.indent do
|
5164
|
+
# This is a very special case of breakable where we want to force
|
5165
|
+
# it into the output but we _don't_ want to explicitly break the
|
5166
|
+
# parent. If a break-parent shows up in the tree, then it's going
|
5167
|
+
# to force it all the way up to the tree, which is going to negate
|
5168
|
+
# the ternary. Maybe this should be an option in prettyprint? As
|
5169
|
+
# in force: :no_break_parent or something.
|
5170
|
+
q.target << PrettyPrint::Breakable.new(" ", 1, force: true)
|
5171
|
+
q.format(node.consequent.statements)
|
5172
|
+
end
|
5173
|
+
end
|
5174
|
+
|
5175
|
+
q.breakable
|
5176
|
+
q.text("end")
|
5177
|
+
end.if_flat do
|
5178
|
+
Parentheses.flat(q) do
|
5179
|
+
q.format(node.predicate)
|
5180
|
+
q.text(" ? ")
|
5181
|
+
|
5182
|
+
statements = [node.statements, node.consequent.statements]
|
5183
|
+
statements.reverse! if keyword == "unless"
|
5184
|
+
|
5185
|
+
q.format(statements[0])
|
5186
|
+
q.text(" : ")
|
5187
|
+
q.format(statements[1])
|
5188
|
+
end
|
5189
|
+
end
|
5190
|
+
end
|
5191
|
+
end
|
5192
|
+
|
5193
|
+
def contains_conditional?
|
5194
|
+
case node
|
5195
|
+
in { statements: { body: [If | IfMod | IfOp | Unless | UnlessMod] } }
|
5196
|
+
true
|
5197
|
+
else
|
5198
|
+
false
|
5199
|
+
end
|
5200
|
+
end
|
4679
5201
|
end
|
4680
5202
|
|
4681
5203
|
# If represents the first clause in an +if+ chain.
|
@@ -4788,7 +5310,7 @@ module SyntaxTree
|
|
4788
5310
|
Yield0, ZSuper
|
4789
5311
|
]
|
4790
5312
|
|
4791
|
-
if force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
5313
|
+
if q.parent.is_a?(Paren) || force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
4792
5314
|
q.group { format_flat(q) }
|
4793
5315
|
return
|
4794
5316
|
end
|
@@ -5261,7 +5783,7 @@ module SyntaxTree
|
|
5261
5783
|
end
|
5262
5784
|
|
5263
5785
|
alias deconstruct child_nodes
|
5264
|
-
|
5786
|
+
|
5265
5787
|
def deconstruct_keys(keys)
|
5266
5788
|
{ value: value, location: location }
|
5267
5789
|
end
|
@@ -5535,6 +6057,17 @@ module SyntaxTree
|
|
5535
6057
|
end
|
5536
6058
|
|
5537
6059
|
def format(q)
|
6060
|
+
# If we're at the top of a call chain, then we're going to do some
|
6061
|
+
# specialized printing in case we can print it nicely. We _only_ do this
|
6062
|
+
# at the top of the chain to avoid weird recursion issues.
|
6063
|
+
if !CallChainFormatter.chained?(q.parent) && CallChainFormatter.chained?(call)
|
6064
|
+
q.group { q.if_break { CallChainFormatter.new(self).format(q) }.if_flat { format_contents(q) } }
|
6065
|
+
else
|
6066
|
+
format_contents(q)
|
6067
|
+
end
|
6068
|
+
end
|
6069
|
+
|
6070
|
+
def format_contents(q)
|
5538
6071
|
q.format(call)
|
5539
6072
|
q.format(block)
|
5540
6073
|
end
|
@@ -5594,11 +6127,17 @@ module SyntaxTree
|
|
5594
6127
|
# [MLHS | MLHSParen] the contents inside of the parentheses
|
5595
6128
|
attr_reader :contents
|
5596
6129
|
|
6130
|
+
# [boolean] whether or not there is a trailing comma at the end of this
|
6131
|
+
# list, which impacts destructuring. It's an attr_accessor so that while
|
6132
|
+
# the syntax tree is being built it can be set by its parent node
|
6133
|
+
attr_accessor :comma
|
6134
|
+
|
5597
6135
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
5598
6136
|
attr_reader :comments
|
5599
6137
|
|
5600
|
-
def initialize(contents:, location:, comments: [])
|
6138
|
+
def initialize(contents:, comma: false, location:, comments: [])
|
5601
6139
|
@contents = contents
|
6140
|
+
@comma = comma
|
5602
6141
|
@location = location
|
5603
6142
|
@comments = comments
|
5604
6143
|
end
|
@@ -5622,6 +6161,7 @@ module SyntaxTree
|
|
5622
6161
|
|
5623
6162
|
if parent.is_a?(MAssign) || parent.is_a?(MLHSParen)
|
5624
6163
|
q.format(contents)
|
6164
|
+
q.text(",") if comma
|
5625
6165
|
else
|
5626
6166
|
q.group(0, "(", ")") do
|
5627
6167
|
q.indent do
|
@@ -5629,6 +6169,7 @@ module SyntaxTree
|
|
5629
6169
|
q.format(contents)
|
5630
6170
|
end
|
5631
6171
|
|
6172
|
+
q.text(",") if comma
|
5632
6173
|
q.breakable("")
|
5633
6174
|
end
|
5634
6175
|
end
|
@@ -6148,7 +6689,9 @@ module SyntaxTree
|
|
6148
6689
|
q.format(rest) if rest && rest.is_a?(ExcessedComma)
|
6149
6690
|
end
|
6150
6691
|
|
6151
|
-
if [Def, Defs, DefEndless].include?(q.parent.class)
|
6692
|
+
if ![Def, Defs, DefEndless].include?(q.parent.class) || parts.empty?
|
6693
|
+
q.nest(0, &contents)
|
6694
|
+
else
|
6152
6695
|
q.group(0, "(", ")") do
|
6153
6696
|
q.indent do
|
6154
6697
|
q.breakable("")
|
@@ -6156,8 +6699,6 @@ module SyntaxTree
|
|
6156
6699
|
end
|
6157
6700
|
q.breakable("")
|
6158
6701
|
end
|
6159
|
-
else
|
6160
|
-
q.nest(0, &contents)
|
6161
6702
|
end
|
6162
6703
|
end
|
6163
6704
|
end
|
@@ -6378,7 +6919,7 @@ module SyntaxTree
|
|
6378
6919
|
end
|
6379
6920
|
|
6380
6921
|
alias deconstruct child_nodes
|
6381
|
-
|
6922
|
+
|
6382
6923
|
def deconstruct_keys(keys)
|
6383
6924
|
{ value: value, location: location }
|
6384
6925
|
end
|
@@ -6478,7 +7019,7 @@ module SyntaxTree
|
|
6478
7019
|
end
|
6479
7020
|
|
6480
7021
|
alias deconstruct child_nodes
|
6481
|
-
|
7022
|
+
|
6482
7023
|
def deconstruct_keys(keys)
|
6483
7024
|
{ value: value, location: location }
|
6484
7025
|
end
|
@@ -6539,7 +7080,7 @@ module SyntaxTree
|
|
6539
7080
|
end
|
6540
7081
|
|
6541
7082
|
alias deconstruct child_nodes
|
6542
|
-
|
7083
|
+
|
6543
7084
|
def deconstruct_keys(keys)
|
6544
7085
|
{ value: value, location: location }
|
6545
7086
|
end
|
@@ -6564,7 +7105,7 @@ module SyntaxTree
|
|
6564
7105
|
end
|
6565
7106
|
|
6566
7107
|
alias deconstruct child_nodes
|
6567
|
-
|
7108
|
+
|
6568
7109
|
def deconstruct_keys(keys)
|
6569
7110
|
{ value: value, location: location }
|
6570
7111
|
end
|
@@ -6635,7 +7176,7 @@ module SyntaxTree
|
|
6635
7176
|
end
|
6636
7177
|
|
6637
7178
|
alias deconstruct child_nodes
|
6638
|
-
|
7179
|
+
|
6639
7180
|
def deconstruct_keys(keys)
|
6640
7181
|
{ beginning: beginning, parts: parts, location: location }
|
6641
7182
|
end
|
@@ -6668,7 +7209,7 @@ module SyntaxTree
|
|
6668
7209
|
end
|
6669
7210
|
|
6670
7211
|
alias deconstruct child_nodes
|
6671
|
-
|
7212
|
+
|
6672
7213
|
def deconstruct_keys(keys)
|
6673
7214
|
{ value: value, location: location }
|
6674
7215
|
end
|
@@ -6702,7 +7243,7 @@ module SyntaxTree
|
|
6702
7243
|
end
|
6703
7244
|
|
6704
7245
|
alias deconstruct child_nodes
|
6705
|
-
|
7246
|
+
|
6706
7247
|
def deconstruct_keys(keys)
|
6707
7248
|
{ value: value, location: location }
|
6708
7249
|
end
|
@@ -7188,7 +7729,7 @@ module SyntaxTree
|
|
7188
7729
|
@value = value
|
7189
7730
|
@location = location
|
7190
7731
|
end
|
7191
|
-
|
7732
|
+
|
7192
7733
|
def accept(visitor)
|
7193
7734
|
visitor.visit_rparen(self)
|
7194
7735
|
end
|
@@ -7198,7 +7739,7 @@ module SyntaxTree
|
|
7198
7739
|
end
|
7199
7740
|
|
7200
7741
|
alias deconstruct child_nodes
|
7201
|
-
|
7742
|
+
|
7202
7743
|
def deconstruct_keys(keys)
|
7203
7744
|
{ value: value, location: location }
|
7204
7745
|
end
|
@@ -7410,19 +7951,20 @@ module SyntaxTree
|
|
7410
7951
|
comment = parser_comments[comment_index]
|
7411
7952
|
location = comment.location
|
7412
7953
|
|
7413
|
-
if !comment.inline? && (start_char <= location.start_char) &&
|
7414
|
-
|
7415
|
-
parser_comments.delete_at(comment_index)
|
7416
|
-
|
7417
|
-
while (node = body[body_index]) &&
|
7418
|
-
(
|
7419
|
-
node.is_a?(VoidStmt) ||
|
7420
|
-
node.location.start_char < location.start_char
|
7421
|
-
)
|
7954
|
+
if !comment.inline? && (start_char <= location.start_char) && (end_char >= location.end_char) && !comment.ignore?
|
7955
|
+
while (node = body[body_index]) && (node.is_a?(VoidStmt) || node.location.start_char < location.start_char)
|
7422
7956
|
body_index += 1
|
7423
7957
|
end
|
7424
7958
|
|
7425
|
-
body.
|
7959
|
+
if body_index != 0 && body[body_index - 1].location.start_char < location.start_char && body[body_index - 1].location.end_char > location.start_char
|
7960
|
+
# The previous node entirely encapsules the comment, so we don't
|
7961
|
+
# want to attach it here since it will get attached normally. This
|
7962
|
+
# is mostly in the case of hash and array literals.
|
7963
|
+
comment_index += 1
|
7964
|
+
else
|
7965
|
+
parser_comments.delete_at(comment_index)
|
7966
|
+
body.insert(body_index, comment)
|
7967
|
+
end
|
7426
7968
|
else
|
7427
7969
|
comment_index += 1
|
7428
7970
|
end
|
@@ -7453,7 +7995,7 @@ module SyntaxTree
|
|
7453
7995
|
end
|
7454
7996
|
|
7455
7997
|
alias deconstruct child_nodes
|
7456
|
-
|
7998
|
+
|
7457
7999
|
def deconstruct_keys(keys)
|
7458
8000
|
{ parts: parts, location: location }
|
7459
8001
|
end
|
@@ -7750,7 +8292,7 @@ module SyntaxTree
|
|
7750
8292
|
end
|
7751
8293
|
|
7752
8294
|
alias deconstruct child_nodes
|
7753
|
-
|
8295
|
+
|
7754
8296
|
def deconstruct_keys(keys)
|
7755
8297
|
{ value: value, location: location }
|
7756
8298
|
end
|
@@ -7780,7 +8322,7 @@ module SyntaxTree
|
|
7780
8322
|
end
|
7781
8323
|
|
7782
8324
|
alias deconstruct child_nodes
|
7783
|
-
|
8325
|
+
|
7784
8326
|
def deconstruct_keys(keys)
|
7785
8327
|
{ value: value, location: location }
|
7786
8328
|
end
|
@@ -7911,7 +8453,7 @@ module SyntaxTree
|
|
7911
8453
|
end
|
7912
8454
|
|
7913
8455
|
alias deconstruct child_nodes
|
7914
|
-
|
8456
|
+
|
7915
8457
|
def deconstruct_keys(keys)
|
7916
8458
|
{ value: value, location: location }
|
7917
8459
|
end
|
@@ -7940,7 +8482,7 @@ module SyntaxTree
|
|
7940
8482
|
end
|
7941
8483
|
|
7942
8484
|
alias deconstruct child_nodes
|
7943
|
-
|
8485
|
+
|
7944
8486
|
def deconstruct_keys(keys)
|
7945
8487
|
{ value: value, location: location }
|
7946
8488
|
end
|
@@ -7970,7 +8512,7 @@ module SyntaxTree
|
|
7970
8512
|
end
|
7971
8513
|
|
7972
8514
|
alias deconstruct child_nodes
|
7973
|
-
|
8515
|
+
|
7974
8516
|
def deconstruct_keys(keys)
|
7975
8517
|
{ value: value, location: location }
|
7976
8518
|
end
|
@@ -8081,7 +8623,7 @@ module SyntaxTree
|
|
8081
8623
|
end
|
8082
8624
|
|
8083
8625
|
alias deconstruct child_nodes
|
8084
|
-
|
8626
|
+
|
8085
8627
|
def deconstruct_keys(keys)
|
8086
8628
|
{ value: value, location: location }
|
8087
8629
|
end
|
@@ -8159,7 +8701,7 @@ module SyntaxTree
|
|
8159
8701
|
end
|
8160
8702
|
|
8161
8703
|
alias deconstruct child_nodes
|
8162
|
-
|
8704
|
+
|
8163
8705
|
def deconstruct_keys(keys)
|
8164
8706
|
{ value: value, location: location }
|
8165
8707
|
end
|
@@ -8206,9 +8748,26 @@ module SyntaxTree
|
|
8206
8748
|
end
|
8207
8749
|
|
8208
8750
|
def format(q)
|
8209
|
-
q.
|
8751
|
+
parent = q.parents.take(2)[1]
|
8752
|
+
ternary = (parent.is_a?(If) || parent.is_a?(Unless)) && Ternaryable.call(q, parent)
|
8753
|
+
|
8754
|
+
q.text("not")
|
8755
|
+
|
8756
|
+
if parentheses
|
8757
|
+
q.text("(")
|
8758
|
+
elsif ternary
|
8759
|
+
q.if_break { q.text(" ") }.if_flat { q.text("(") }
|
8760
|
+
else
|
8761
|
+
q.text(" ")
|
8762
|
+
end
|
8763
|
+
|
8210
8764
|
q.format(statement) if statement
|
8211
|
-
|
8765
|
+
|
8766
|
+
if parentheses
|
8767
|
+
q.text(")")
|
8768
|
+
elsif ternary
|
8769
|
+
q.if_flat { q.text(")") }
|
8770
|
+
end
|
8212
8771
|
end
|
8213
8772
|
end
|
8214
8773
|
|
@@ -9184,7 +9743,7 @@ module SyntaxTree
|
|
9184
9743
|
end
|
9185
9744
|
|
9186
9745
|
alias deconstruct child_nodes
|
9187
|
-
|
9746
|
+
|
9188
9747
|
def deconstruct_keys(keys)
|
9189
9748
|
{ value: value, location: location }
|
9190
9749
|
end
|
@@ -9213,7 +9772,7 @@ module SyntaxTree
|
|
9213
9772
|
end
|
9214
9773
|
|
9215
9774
|
alias deconstruct child_nodes
|
9216
|
-
|
9775
|
+
|
9217
9776
|
def deconstruct_keys(keys)
|
9218
9777
|
{ parts: parts, location: location }
|
9219
9778
|
end
|