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.
@@ -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
- (value.is_a?(Call) && skip_indent?(value.receiver)) ||
1031
- [
1032
- ArrayLiteral,
1033
- HashLiteral,
1034
- Heredoc,
1035
- Lambda,
1036
- QSymbols,
1037
- QWords,
1038
- Symbols,
1039
- Words
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 Next node.
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
- if arguments.parts.any?
2050
- if arguments.parts.length == 1
2051
- part = arguments.parts.first
2052
-
2053
- if part.is_a?(Paren)
2054
- q.format(arguments)
2055
- elsif part.is_a?(ArrayLiteral)
2056
- q.text(" ")
2057
- q.format(arguments)
2058
- else
2059
- format_arguments(q, "(", ")")
2060
- end
2061
- else
2062
- format_arguments(q, " [", "]")
2063
- end
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.format(arguments) if arguments
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.text(" ")
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?(node)
2896
+ def align(q, node, &block)
2559
2897
  case node.arguments
2560
2898
  in Args[parts: [Def | Defs | DefEndless]]
2561
- false
2899
+ q.text(" ")
2900
+ yield
2901
+ in Args[parts: [IfOp]]
2902
+ yield
2562
2903
  in Args[parts: [Command => command]]
2563
- align?(command)
2904
+ align(q, command, &block)
2564
2905
  else
2565
- true
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
- if arguments
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
- q.format(params) if !params.is_a?(Params) || !params.empty?
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
- q.format(params) if !params.is_a?(Params) || !params.empty?
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
- q.format(arguments)
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
- parent = q.parent
4550
- if PATTERNS.include?(parent.class)
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
- (end_char >= location.end_char) && !comment.ignore?
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.insert(body_index, comment)
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.text(parentheses ? "not(" : "not ")
8208
- q.format(statement)
8209
- q.text(")") if parentheses
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
- q.format(value) if value
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