xref: /aosp_15_r20/external/ktfmt/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt (revision 5be3f65c8cf0e6db0a7e312df5006e8e93cdf9ec)
1 /*
<lambda>null2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.facebook.ktfmt.format
18 
19 import com.google.common.base.Throwables
20 import com.google.common.collect.ImmutableList
21 import com.google.googlejavaformat.Doc
22 import com.google.googlejavaformat.FormattingError
23 import com.google.googlejavaformat.Indent
24 import com.google.googlejavaformat.Indent.Const.ZERO
25 import com.google.googlejavaformat.OpsBuilder
26 import com.google.googlejavaformat.Output.BreakTag
27 import java.util.ArrayDeque
28 import java.util.Optional
29 import org.jetbrains.kotlin.com.intellij.psi.PsiComment
30 import org.jetbrains.kotlin.com.intellij.psi.PsiElement
31 import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
32 import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
33 import org.jetbrains.kotlin.lexer.KtTokens
34 import org.jetbrains.kotlin.psi.KtAnnotatedExpression
35 import org.jetbrains.kotlin.psi.KtAnnotation
36 import org.jetbrains.kotlin.psi.KtAnnotationEntry
37 import org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget
38 import org.jetbrains.kotlin.psi.KtArrayAccessExpression
39 import org.jetbrains.kotlin.psi.KtBinaryExpression
40 import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
41 import org.jetbrains.kotlin.psi.KtBlockExpression
42 import org.jetbrains.kotlin.psi.KtBreakExpression
43 import org.jetbrains.kotlin.psi.KtCallExpression
44 import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
45 import org.jetbrains.kotlin.psi.KtCatchClause
46 import org.jetbrains.kotlin.psi.KtClass
47 import org.jetbrains.kotlin.psi.KtClassBody
48 import org.jetbrains.kotlin.psi.KtClassInitializer
49 import org.jetbrains.kotlin.psi.KtClassLiteralExpression
50 import org.jetbrains.kotlin.psi.KtClassOrObject
51 import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
52 import org.jetbrains.kotlin.psi.KtConstantExpression
53 import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
54 import org.jetbrains.kotlin.psi.KtContainerNode
55 import org.jetbrains.kotlin.psi.KtContextReceiverList
56 import org.jetbrains.kotlin.psi.KtContinueExpression
57 import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
58 import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
59 import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
60 import org.jetbrains.kotlin.psi.KtDoWhileExpression
61 import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
62 import org.jetbrains.kotlin.psi.KtDynamicType
63 import org.jetbrains.kotlin.psi.KtElement
64 import org.jetbrains.kotlin.psi.KtEnumEntry
65 import org.jetbrains.kotlin.psi.KtExpression
66 import org.jetbrains.kotlin.psi.KtFile
67 import org.jetbrains.kotlin.psi.KtFileAnnotationList
68 import org.jetbrains.kotlin.psi.KtFinallySection
69 import org.jetbrains.kotlin.psi.KtForExpression
70 import org.jetbrains.kotlin.psi.KtFunctionType
71 import org.jetbrains.kotlin.psi.KtIfExpression
72 import org.jetbrains.kotlin.psi.KtImportDirective
73 import org.jetbrains.kotlin.psi.KtImportList
74 import org.jetbrains.kotlin.psi.KtIntersectionType
75 import org.jetbrains.kotlin.psi.KtIsExpression
76 import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
77 import org.jetbrains.kotlin.psi.KtLabeledExpression
78 import org.jetbrains.kotlin.psi.KtLambdaArgument
79 import org.jetbrains.kotlin.psi.KtLambdaExpression
80 import org.jetbrains.kotlin.psi.KtModifierList
81 import org.jetbrains.kotlin.psi.KtNamedFunction
82 import org.jetbrains.kotlin.psi.KtNullableType
83 import org.jetbrains.kotlin.psi.KtPackageDirective
84 import org.jetbrains.kotlin.psi.KtParameter
85 import org.jetbrains.kotlin.psi.KtParameterList
86 import org.jetbrains.kotlin.psi.KtParenthesizedExpression
87 import org.jetbrains.kotlin.psi.KtPostfixExpression
88 import org.jetbrains.kotlin.psi.KtPrefixExpression
89 import org.jetbrains.kotlin.psi.KtPrimaryConstructor
90 import org.jetbrains.kotlin.psi.KtProjectionKind
91 import org.jetbrains.kotlin.psi.KtProperty
92 import org.jetbrains.kotlin.psi.KtPropertyAccessor
93 import org.jetbrains.kotlin.psi.KtPropertyDelegate
94 import org.jetbrains.kotlin.psi.KtQualifiedExpression
95 import org.jetbrains.kotlin.psi.KtReferenceExpression
96 import org.jetbrains.kotlin.psi.KtReturnExpression
97 import org.jetbrains.kotlin.psi.KtScript
98 import org.jetbrains.kotlin.psi.KtScriptInitializer
99 import org.jetbrains.kotlin.psi.KtSecondaryConstructor
100 import org.jetbrains.kotlin.psi.KtSimpleNameExpression
101 import org.jetbrains.kotlin.psi.KtStringTemplateExpression
102 import org.jetbrains.kotlin.psi.KtSuperExpression
103 import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
104 import org.jetbrains.kotlin.psi.KtSuperTypeList
105 import org.jetbrains.kotlin.psi.KtThisExpression
106 import org.jetbrains.kotlin.psi.KtThrowExpression
107 import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
108 import org.jetbrains.kotlin.psi.KtTryExpression
109 import org.jetbrains.kotlin.psi.KtTypeAlias
110 import org.jetbrains.kotlin.psi.KtTypeArgumentList
111 import org.jetbrains.kotlin.psi.KtTypeConstraint
112 import org.jetbrains.kotlin.psi.KtTypeConstraintList
113 import org.jetbrains.kotlin.psi.KtTypeParameter
114 import org.jetbrains.kotlin.psi.KtTypeParameterList
115 import org.jetbrains.kotlin.psi.KtTypeProjection
116 import org.jetbrains.kotlin.psi.KtTypeReference
117 import org.jetbrains.kotlin.psi.KtUserType
118 import org.jetbrains.kotlin.psi.KtValueArgument
119 import org.jetbrains.kotlin.psi.KtValueArgumentList
120 import org.jetbrains.kotlin.psi.KtWhenConditionInRange
121 import org.jetbrains.kotlin.psi.KtWhenConditionIsPattern
122 import org.jetbrains.kotlin.psi.KtWhenConditionWithExpression
123 import org.jetbrains.kotlin.psi.KtWhenExpression
124 import org.jetbrains.kotlin.psi.KtWhileExpression
125 import org.jetbrains.kotlin.psi.psiUtil.children
126 import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespace
127 import org.jetbrains.kotlin.psi.psiUtil.startOffset
128 import org.jetbrains.kotlin.psi.psiUtil.startsWithComment
129 import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
130 import org.jetbrains.kotlin.psi.stubs.impl.KotlinPlaceHolderStubImpl
131 
132 /** An AST visitor that builds a stream of {@link Op}s to format. */
133 class KotlinInputAstVisitor(
134     private val options: FormattingOptions,
135     private val builder: OpsBuilder
136 ) : KtTreeVisitorVoid() {
137 
138   /** Standard indentation for a block */
139   private val blockIndent: Indent.Const = Indent.Const.make(options.blockIndent, 1)
140 
141   /**
142    * Standard indentation for a long expression or function call, it is different than block
143    * indentation on purpose
144    */
145   private val expressionBreakIndent: Indent.Const = Indent.Const.make(options.continuationIndent, 1)
146 
147   private val blockPlusExpressionBreakIndent: Indent.Const =
148       Indent.Const.make(options.blockIndent + options.continuationIndent, 1)
149 
150   private val doubleExpressionBreakIndent: Indent.Const =
151       Indent.Const.make(options.continuationIndent, 2)
152 
153   private val expressionBreakNegativeIndent: Indent.Const =
154       Indent.Const.make(-options.continuationIndent, 1)
155 
156   /** A record of whether we have visited into an expression. */
157   private val inExpression = ArrayDeque(ImmutableList.of(false))
158 
159   /** Tracks whether we are handling an import directive */
160   private var inImport = false
161 
162   /** Example: `fun foo(n: Int) { println(n) }` */
163   override fun visitNamedFunction(function: KtNamedFunction) {
164     builder.sync(function)
165     builder.block(ZERO) {
166       visitFunctionLikeExpression(
167           contextReceiverList =
168               function.getStubOrPsiChild(KtStubElementTypes.CONTEXT_RECEIVER_LIST),
169           modifierList = function.modifierList,
170           keyword = "fun",
171           typeParameters = function.typeParameterList,
172           receiverTypeReference = function.receiverTypeReference,
173           name = function.nameIdentifier?.text,
174           parameterList = function.valueParameterList,
175           typeConstraintList = function.typeConstraintList,
176           bodyExpression = function.bodyBlockExpression ?: function.bodyExpression,
177           typeOrDelegationCall = function.typeReference,
178       )
179     }
180   }
181 
182   /** Example `Int`, `(String)` or `() -> Int` */
183   override fun visitTypeReference(typeReference: KtTypeReference) {
184     builder.sync(typeReference)
185     // Normally we'd visit the children nodes through accessors on 'typeReference', and  we wouldn't
186     // loop over children.
187     // But, in this case the modifier list can either be inside the parenthesis:
188     // ... (@Composable (x) -> Unit)
189     // or outside of them:
190     // ... @Composable ((x) -> Unit)
191     val modifierList = typeReference.modifierList
192     val typeElement = typeReference.typeElement
193     for (child in typeReference.node.children()) {
194       when {
195         child.psi == modifierList -> visit(modifierList)
196         child.psi == typeElement -> visit(typeElement)
197         child.elementType == KtTokens.LPAR -> builder.token("(")
198         child.elementType == KtTokens.RPAR -> builder.token(")")
199       }
200     }
201   }
202 
203   override fun visitDynamicType(type: KtDynamicType) {
204     builder.token("dynamic")
205   }
206 
207   /** Example: `String?` or `((Int) -> Unit)?` */
208   override fun visitNullableType(nullableType: KtNullableType) {
209     builder.sync(nullableType)
210 
211     // Normally we wouldn't loop over children, but there can be multiple layers of parens.
212     val modifierList = nullableType.modifierList
213     val innerType = nullableType.innerType
214     for (child in nullableType.node.children()) {
215       when {
216         child.psi == modifierList -> visit(modifierList)
217         child.psi == innerType -> visit(innerType)
218         child.elementType == KtTokens.LPAR -> builder.token("(")
219         child.elementType == KtTokens.RPAR -> builder.token(")")
220       }
221     }
222     builder.token("?")
223   }
224 
225   /** Example: `String` or `List<Int>`, */
226   override fun visitUserType(type: KtUserType) {
227     builder.sync(type)
228 
229     if (type.qualifier != null) {
230       visit(type.qualifier)
231       builder.token(".")
232     }
233     visit(type.referenceExpression)
234     val typeArgumentList = type.typeArgumentList
235     if (typeArgumentList != null) {
236       builder.block(expressionBreakIndent) { visit(typeArgumentList) }
237     }
238   }
239 
240   /** Example: `A & B`, */
241   override fun visitIntersectionType(type: KtIntersectionType) {
242     builder.sync(type)
243 
244     // TODO(strulovich): Should this have the same indentation behaviour as `x && y`?
245     visit(type.getLeftTypeRef())
246     builder.space()
247     builder.token("&")
248     builder.space()
249     visit(type.getRightTypeRef())
250   }
251 
252   /** Example `<Int, String>` in `List<Int, String>` */
253   override fun visitTypeArgumentList(typeArgumentList: KtTypeArgumentList) {
254     builder.sync(typeArgumentList)
255     visitEachCommaSeparated(
256         typeArgumentList.arguments,
257         typeArgumentList.trailingComma != null,
258         wrapInBlock = !options.manageTrailingCommas,
259         prefix = "<",
260         postfix = ">",
261     )
262   }
263 
264   override fun visitTypeProjection(typeProjection: KtTypeProjection) {
265     builder.sync(typeProjection)
266     val typeReference = typeProjection.typeReference
267     when (typeProjection.projectionKind) {
268       KtProjectionKind.IN -> {
269         builder.token("in")
270         builder.space()
271         visit(typeReference)
272       }
273       KtProjectionKind.OUT -> {
274         builder.token("out")
275         builder.space()
276         visit(typeReference)
277       }
278       KtProjectionKind.STAR -> builder.token("*")
279       KtProjectionKind.NONE -> visit(typeReference)
280     }
281   }
282 
283   /**
284    * @param keyword e.g., "fun" or "class".
285    * @param typeOrDelegationCall for functions, the return typeOrDelegationCall; for classes, the
286    *   list of supertypes.
287    */
288   private fun visitFunctionLikeExpression(
289       contextReceiverList: KtContextReceiverList?,
290       modifierList: KtModifierList?,
291       keyword: String?,
292       typeParameters: KtTypeParameterList?,
293       receiverTypeReference: KtTypeReference?,
294       name: String?,
295       parameterList: KtParameterList?,
296       typeConstraintList: KtTypeConstraintList?,
297       bodyExpression: KtExpression?,
298       typeOrDelegationCall: KtElement?,
299   ) {
300     fun emitTypeOrDelegationCall(block: () -> Unit) {
301       if (typeOrDelegationCall != null) {
302         builder.block(ZERO) {
303           if (typeOrDelegationCall is KtConstructorDelegationCall) {
304             builder.space()
305           }
306           builder.token(":")
307           block()
308         }
309       }
310     }
311 
312     val forceTrailingBreak = name != null
313     builder.block(ZERO, isEnabled = forceTrailingBreak) {
314       if (contextReceiverList != null) {
315         visitContextReceiverList(contextReceiverList)
316       }
317       if (modifierList != null) {
318         visitModifierList(modifierList)
319       }
320       if (keyword != null) {
321         builder.token(keyword)
322       }
323       if (typeParameters != null) {
324         builder.space()
325         builder.block(ZERO) { visit(typeParameters) }
326       }
327 
328       if (name != null || receiverTypeReference != null) {
329         builder.space()
330       }
331       builder.block(ZERO) {
332         if (receiverTypeReference != null) {
333           visit(receiverTypeReference)
334           builder.breakOp(Doc.FillMode.INDEPENDENT, "", expressionBreakIndent)
335           builder.token(".")
336         }
337         if (name != null) {
338           builder.token(name)
339         }
340       }
341 
342       if (parameterList != null && parameterList.hasEmptyParens()) {
343         builder.block(ZERO) {
344           builder.token("(")
345           builder.token(")")
346           emitTypeOrDelegationCall {
347             builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
348             builder.block(expressionBreakIndent) { visit(typeOrDelegationCall) }
349           }
350         }
351       } else {
352         builder.block(expressionBreakIndent) {
353           if (parameterList != null) {
354             visitEachCommaSeparated(
355                 list = parameterList.parameters,
356                 hasTrailingComma = parameterList.trailingComma != null,
357                 prefix = "(",
358                 postfix = ")",
359                 wrapInBlock = false,
360                 breakBeforePostfix = true,
361             )
362           }
363           emitTypeOrDelegationCall {
364             builder.space()
365             builder.block(expressionBreakNegativeIndent) { visit(typeOrDelegationCall) }
366           }
367         }
368       }
369 
370       if (typeConstraintList != null) {
371         builder.space()
372         visit(typeConstraintList)
373       }
374       if (bodyExpression is KtBlockExpression) {
375         builder.space()
376         visit(bodyExpression)
377       } else if (bodyExpression != null) {
378         builder.space()
379         builder.block(ZERO) {
380           builder.token("=")
381           if (isLambdaOrScopingFunction(bodyExpression)) {
382             visitLambdaOrScopingFunction(bodyExpression)
383           } else {
384             builder.block(expressionBreakIndent) {
385               builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
386               builder.block(ZERO) { visit(bodyExpression) }
387             }
388           }
389         }
390       }
391       builder.guessToken(";")
392     }
393     if (forceTrailingBreak) {
394       builder.forcedBreak()
395     }
396   }
397 
398   private fun genSym(): BreakTag {
399     return BreakTag()
400   }
401 
402   private fun emitBracedBlock(
403       bodyBlockExpression: PsiElement,
404       emitChildren: (Array<PsiElement>) -> Unit,
405   ) {
406     builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
407     val statements = bodyBlockExpression.children
408     if (statements.isNotEmpty()) {
409       builder.block(blockIndent) {
410         builder.forcedBreak()
411         builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
412         emitChildren(statements)
413       }
414       builder.forcedBreak()
415       builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
416     }
417     builder.token("}", blockIndent)
418   }
419 
420   private fun visitStatement(statement: PsiElement) {
421     builder.block(ZERO) { visit(statement) }
422     builder.guessToken(";")
423   }
424 
425   private fun visitStatements(statements: Array<PsiElement>) {
426     var first = true
427     builder.guessToken(";")
428     for (statement in statements) {
429       builder.forcedBreak()
430       if (!first) {
431         builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
432       }
433       first = false
434       visitStatement(statement)
435     }
436   }
437 
438   override fun visitProperty(property: KtProperty) {
439     builder.sync(property)
440     builder.block(ZERO) {
441       declareOne(
442           kind = DeclarationKind.FIELD,
443           modifiers = property.modifierList,
444           valOrVarKeyword = property.valOrVarKeyword.text,
445           typeParameters = property.typeParameterList,
446           receiver = property.receiverTypeReference,
447           name = property.nameIdentifier?.text,
448           type = property.typeReference,
449           typeConstraintList = property.typeConstraintList,
450           delegate = property.delegate,
451           initializer = property.initializer,
452           accessors = property.accessors)
453     }
454     builder.guessToken(";")
455     if (property.parent !is KtWhenExpression) {
456       builder.forcedBreak()
457     }
458   }
459 
460   /**
461    * Example: "com.facebook.bla.bla" in imports or "a.b.c.d" in expressions.
462    *
463    * There's a few cases that are different. We deal with imports by keeping them on the same line.
464    * For regular chained expressions we go the left most descendant so we can start indentation only
465    * before the first break (a `.` or `?.`), and keep the seem indentation for this chain of calls.
466    */
467   override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
468     builder.sync(expression)
469     val receiver = expression.receiverExpression
470     when {
471       inImport -> {
472         visit(receiver)
473         val selectorExpression = expression.selectorExpression
474         if (selectorExpression != null) {
475           builder.token(".")
476           visit(selectorExpression)
477         }
478       }
479       receiver is KtStringTemplateExpression -> {
480         builder.block(expressionBreakIndent) {
481           visit(receiver)
482           builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
483           builder.token(expression.operationSign.value)
484           visit(expression.selectorExpression)
485         }
486       }
487       receiver is KtWhenExpression -> {
488         builder.block(ZERO) {
489           visit(receiver)
490           builder.token(expression.operationSign.value)
491           visit(expression.selectorExpression)
492         }
493       }
494       else -> {
495         emitQualifiedExpression(expression)
496       }
497     }
498   }
499 
500   /** Extra data to help [emitQualifiedExpression] know when to open and close a group */
501   private class GroupingInfo {
502     var groupOpenCount = 0
503     var shouldCloseGroup = false
504   }
505 
506   /**
507    * Handles a chain of qualified expressions, i.e. `a[5].b!!.c()[4].f()`
508    *
509    * This is by far the most complicated part of this formatter. We start by breaking the expression
510    * to the steps it is executed in (which are in the opposite order of how the syntax tree is
511    * built).
512    *
513    * We then calculate information to know which parts need to be groups, and finally go part by
514    * part, emitting it to the [builder] while closing and opening groups.
515    */
516   private fun emitQualifiedExpression(expression: KtExpression) {
517     val parts = breakIntoParts(expression)
518     // whether we want to make a lambda look like a block, this make Kotlin DSLs look as expected
519     val useBlockLikeLambdaStyle = parts.last().isLambda() && parts.count { it.isLambda() } == 1
520     val groupingInfos = computeGroupingInfo(parts, useBlockLikeLambdaStyle)
521     builder.block(expressionBreakIndent) {
522       val nameTag = genSym() // allows adjusting arguments indentation if a break will be made
523       for ((index, ktExpression) in parts.withIndex()) {
524         if (ktExpression is KtQualifiedExpression) {
525           builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO, Optional.of(nameTag))
526         }
527         repeat(groupingInfos[index].groupOpenCount) { builder.open(ZERO) }
528         when (ktExpression) {
529           is KtQualifiedExpression -> {
530             builder.token(ktExpression.operationSign.value)
531             val selectorExpression = ktExpression.selectorExpression
532             if (selectorExpression !is KtCallExpression) {
533               // selector is a simple field access
534               visit(selectorExpression)
535               if (groupingInfos[index].shouldCloseGroup) {
536                 builder.close()
537               }
538             } else {
539               // selector is a function call, we may close a group after its name
540               // emit `doIt` from `doIt(1, 2) { it }`
541               visit(selectorExpression.calleeExpression)
542               // close groups according to instructions
543               if (groupingInfos[index].shouldCloseGroup) {
544                 builder.close()
545               }
546               // close group due to last lambda to allow block-like style in `as.forEach { ... }`
547               val isTrailingLambda = useBlockLikeLambdaStyle && index == parts.size - 1
548               if (isTrailingLambda) {
549                 builder.close()
550               }
551               val argsIndentElse = if (index == parts.size - 1) ZERO else expressionBreakIndent
552               val lambdaIndentElse = if (isTrailingLambda) expressionBreakNegativeIndent else ZERO
553               val negativeLambdaIndentElse = if (isTrailingLambda) expressionBreakIndent else ZERO
554 
555               // emit `(1, 2) { it }` from `doIt(1, 2) { it }`
556               visitCallElement(
557                   null,
558                   selectorExpression.typeArgumentList,
559                   selectorExpression.valueArgumentList,
560                   selectorExpression.lambdaArguments,
561                   argumentsIndent = Indent.If.make(nameTag, expressionBreakIndent, argsIndentElse),
562                   lambdaIndent = Indent.If.make(nameTag, ZERO, lambdaIndentElse),
563                   negativeLambdaIndent = Indent.If.make(nameTag, ZERO, negativeLambdaIndentElse),
564               )
565             }
566           }
567           is KtArrayAccessExpression -> {
568             visitArrayAccessBrackets(ktExpression)
569             builder.close()
570           }
571           is KtPostfixExpression -> {
572             builder.token(ktExpression.operationReference.text)
573             builder.close()
574           }
575           else -> {
576             check(index == 0)
577             visit(ktExpression)
578           }
579         }
580       }
581     }
582   }
583 
584   /**
585    * Decomposes a qualified expression into parts, so `rainbow.red.orange.yellow` becomes `[rainbow,
586    * rainbow.red, rainbow.red.orange, rainbow.orange.yellow]`
587    */
588   private fun breakIntoParts(expression: KtExpression): List<KtExpression> {
589     val parts = ArrayDeque<KtExpression>()
590 
591     // use an ArrayDeque and add elements to the beginning so the innermost expression comes first
592     // foo.bar.yay -> [yay, bar.yay, foo.bar.yay]
593 
594     var node: KtExpression? = expression
595     while (node != null) {
596       parts.addFirst(node)
597       node =
598           when (node) {
599             is KtQualifiedExpression -> node.receiverExpression
600             is KtArrayAccessExpression -> node.arrayExpression
601             is KtPostfixExpression -> node.baseExpression
602             else -> null
603           }
604     }
605 
606     return parts.toList()
607   }
608 
609   /**
610    * Generates the [GroupingInfo] array to go with an array of [KtQualifiedExpression] parts
611    *
612    * For example, the expression `a.b[2].c.d()` is made of four expressions:
613    * 1. [KtQualifiedExpression] `a.b[2].c . d()` (this will be `parts[4]`)
614    * 1. [KtQualifiedExpression] `a.b[2] . c` (this will be `parts[3]`)
615    * 2. [KtArrayAccessExpression] `a.b [2]` (this will be `parts[2]`)
616    * 3. [KtQualifiedExpression] `a . b` (this will be `parts[1]`)
617    * 4. [KtSimpleNameExpression] `a` (this will be `parts[0]`)
618    *
619    * Once in parts, these are in the reverse order. To render the array correct we need to make sure
620    * `b` and [2] are in a group so we avoid splitting them. To do so we need to open a group for `b`
621    * (that will be done in part 2), and always close a group for an array.
622    *
623    * Here is the same expression, with justified braces marking the groupings it will get:
624    * ```
625    *  a . b [2] . c . d ()
626    * {a . b} --> Grouping `a.b` because it can be a package name or simple field access so we add 1
627    *             to the number of groups to open at groupingInfos[0], and mark to close a group at
628    *             groupingInfos[1]
629    * {a . b [2]} --> Grouping `a.b` with `[2]`, since otherwise we may break inside the brackets
630    *                 instead of preferring breaks before dots. So we open a group at [0], but since
631    *                 we always close a group after brackets, we don't store that information.
632    *             {c . d} --> another group to attach the first function name to the fields before it
633    *                         this time we don't start the group in the beginning, and use
634    *                         lastIndexToOpen to track the spot after the last time we stopped
635    *                         grouping.
636    * ```
637    *
638    * The final expression with groupings:
639    * ```
640    * {{a.b}[2]}.{c.d}()
641    * ```
642    */
643   private fun computeGroupingInfo(
644       parts: List<KtExpression>,
645       useBlockLikeLambdaStyle: Boolean
646   ): List<GroupingInfo> {
647     val groupingInfos = List(parts.size) { GroupingInfo() }
648     var lastIndexToOpen = 0
649     for ((index, part) in parts.withIndex()) {
650       when (part) {
651         is KtQualifiedExpression -> {
652           val receiverExpression = part.receiverExpression
653           val previous =
654               (receiverExpression as? KtQualifiedExpression)?.selectorExpression
655                   ?: receiverExpression
656           val current = checkNotNull(part.selectorExpression)
657           if (lastIndexToOpen == 0 &&
658               shouldGroupPartWithPrevious(parts, part, index, previous, current)) {
659             // this and the previous items should be grouped for better style
660             // we add another group to open in index 0
661             groupingInfos[0].groupOpenCount++
662             // we don't always close a group when emitting this node, so we need this flag to
663             // mark if we need to close a group
664             groupingInfos[index].shouldCloseGroup = true
665           } else {
666             // use this index in to open future groups for arrays and postfixes
667             // we will also stop grouping field access to the beginning of the expression
668             lastIndexToOpen = index
669           }
670         }
671         is KtArrayAccessExpression,
672         is KtPostfixExpression -> {
673           // we group these with the last item with a name, and we always close them
674           groupingInfos[lastIndexToOpen].groupOpenCount++
675         }
676       }
677     }
678     if (useBlockLikeLambdaStyle) {
679       // a trailing lambda adds a group that we stop before emitting the lambda
680       groupingInfos[0].groupOpenCount++
681     }
682     return groupingInfos
683   }
684 
685   /** Decide whether a [KtQualifiedExpression] part should be grouped with the previous part */
686   private fun shouldGroupPartWithPrevious(
687       parts: List<KtExpression>,
688       part: KtExpression,
689       index: Int,
690       previous: KtExpression,
691       current: KtExpression
692   ): Boolean {
693     // this is the second, and the first is short, avoid `.` "hanging in air"
694     if (index == 1 && previous.text.length < options.continuationIndent) {
695       return true
696     }
697     // the previous part is `this` or `super`
698     if (previous is KtSuperExpression || previous is KtThisExpression) {
699       return true
700     }
701     // this and the previous part are a package name, type name, or property
702     if (previous is KtSimpleNameExpression &&
703         current is KtSimpleNameExpression &&
704         part is KtDotQualifiedExpression) {
705       return true
706     }
707     // this is `Foo` in `com.facebook.Foo`, so everything before it is a package name
708     if (current.text.first().isUpperCase() &&
709         current is KtSimpleNameExpression &&
710         part is KtDotQualifiedExpression) {
711       return true
712     }
713     // this is the `foo()` in `com.facebook.Foo.foo()` or in `Foo.foo()`
714     if (current is KtCallExpression &&
715         (previous !is KtCallExpression) &&
716         previous.text?.firstOrNull()?.isUpperCase() == true) {
717       return true
718     }
719     // this is an invocation and the last item, and the previous it not, i.e. `a.b.c()`
720     // keeping it grouped and splitting the arguments makes `a.b(...)` feel like `aab()`
721     return current is KtCallExpression &&
722         previous !is KtCallExpression &&
723         index == parts.indices.last
724   }
725 
726   override fun visitCallExpression(callExpression: KtCallExpression) {
727     builder.sync(callExpression)
728     with(callExpression) {
729       visitCallElement(
730           calleeExpression,
731           typeArgumentList,
732           valueArgumentList,
733           lambdaArguments,
734       )
735     }
736   }
737 
738   /**
739    * Examples `foo<T>(a, b)`, `foo(a)`, `boo()`, `super(a)`
740    *
741    * @param lambdaIndent how to indent [lambdaArguments], if present
742    * @param negativeLambdaIndent the negative indentation of [lambdaIndent]
743    */
744   private fun visitCallElement(
745       callee: KtExpression?,
746       typeArgumentList: KtTypeArgumentList?,
747       argumentList: KtValueArgumentList?,
748       lambdaArguments: List<KtLambdaArgument>,
749       argumentsIndent: Indent = expressionBreakIndent,
750       lambdaIndent: Indent = ZERO,
751       negativeLambdaIndent: Indent = ZERO,
752   ) {
753     // Apply the lambda indent to the callee, type args, value args, and the lambda.
754     // This is undone for the first three by the negative lambda indent.
755     // This way they're in one block, and breaks in the argument list cause a break in the lambda.
756     builder.block(lambdaIndent) {
757 
758       // Used to keep track of whether or not we need to indent the lambda
759       // This is based on if there is a break in the argument list
760       var brokeBeforeBrace: BreakTag? = null
761 
762       builder.block(negativeLambdaIndent) {
763         visit(callee)
764         builder.block(argumentsIndent) {
765           builder.block(ZERO) { visit(typeArgumentList) }
766           if (argumentList != null) {
767             brokeBeforeBrace = visitValueArgumentListInternal(argumentList)
768           }
769         }
770       }
771       when (lambdaArguments.size) {
772         0 -> {}
773         1 -> {
774           builder.space()
775           visitArgumentInternal(
776               lambdaArguments.single(),
777               wrapInBlock = false,
778               brokeBeforeBrace = brokeBeforeBrace,
779           )
780         }
781         else -> throw ParseError("Maximum one trailing lambda is allowed", lambdaArguments[1])
782       }
783     }
784   }
785 
786   /** Example (`1, "hi"`) in a function call */
787   override fun visitValueArgumentList(list: KtValueArgumentList) {
788     visitValueArgumentListInternal(list)
789   }
790 
791   /**
792    * Example (`1, "hi"`) in a function call
793    *
794    * @return a [BreakTag] which can tell you if a break was taken, but only when the list doesn't
795    *   terminate in a negative closing indent. See [visitEachCommaSeparated] for examples.
796    */
797   private fun visitValueArgumentListInternal(list: KtValueArgumentList): BreakTag? {
798     builder.sync(list)
799 
800     val arguments = list.arguments
801     val isSingleUnnamedLambda =
802         arguments.size == 1 &&
803             arguments.first().getArgumentExpression() is KtLambdaExpression &&
804             arguments.first().getArgumentName() == null
805     val hasTrailingComma = list.trailingComma != null
806     val hasEmptyParens = list.hasEmptyParens()
807 
808     val wrapInBlock: Boolean
809     val breakBeforePostfix: Boolean
810     val leadingBreak: Boolean
811     val breakAfterPrefix: Boolean
812     if (isSingleUnnamedLambda) {
813       wrapInBlock = true
814       breakBeforePostfix = false
815       leadingBreak = !hasEmptyParens && hasTrailingComma
816       breakAfterPrefix = false
817     } else {
818       wrapInBlock = !options.manageTrailingCommas
819       breakBeforePostfix = options.manageTrailingCommas && !hasEmptyParens
820       leadingBreak = !hasEmptyParens
821       breakAfterPrefix = !hasEmptyParens
822     }
823 
824     return visitEachCommaSeparated(
825         arguments,
826         hasTrailingComma,
827         wrapInBlock = wrapInBlock,
828         breakBeforePostfix = breakBeforePostfix,
829         leadingBreak = leadingBreak,
830         prefix = "(",
831         postfix = ")",
832         breakAfterPrefix = breakAfterPrefix,
833     )
834   }
835 
836   /** Example `{ 1 + 1 }` (as lambda) or `{ (x, y) -> x + y }` */
837   override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
838     visitLambdaExpressionInternal(lambdaExpression, brokeBeforeBrace = null)
839   }
840 
841   /**
842    * The internal version of [visitLambdaExpression].
843    *
844    * @param brokeBeforeBrace used for tracking if a break was taken right before the lambda
845    *   expression. Useful for scoping functions where we want good looking indentation. For example,
846    *   here we have correct indentation before `bar()` and `car()` because we can detect the break
847    *   after the equals:
848    * ```
849    * fun foo() =
850    *     coroutineScope { x ->
851    *       bar()
852    *       car()
853    *     }
854    * ```
855    */
856   private fun visitLambdaExpressionInternal(
857       lambdaExpression: KtLambdaExpression,
858       brokeBeforeBrace: BreakTag?,
859   ) {
860     builder.sync(lambdaExpression)
861 
862     val valueParams = lambdaExpression.valueParameters
863     val hasParams = valueParams.isNotEmpty()
864     val bodyExpression = lambdaExpression.bodyExpression ?: fail()
865     val expressionStatements = bodyExpression.children
866     val hasStatements = expressionStatements.isNotEmpty()
867     val hasComments = bodyExpression.children().any { it is PsiComment }
868     val hasArrow = lambdaExpression.functionLiteral.arrow != null
869 
870     fun ifBrokeBeforeBrace(onTrue: Indent, onFalse: Indent): Indent {
871       if (brokeBeforeBrace == null) return onFalse
872       return Indent.If.make(brokeBeforeBrace, onTrue, onFalse)
873     }
874 
875     /**
876      * Enable correct formatting of the `fun foo() = scope {` syntax.
877      *
878      * We can't denote the lambda (+ scope function) as a block, since (for multiline lambdas) the
879      * rectangle rule would force the entire lambda onto a lower line. Instead, we conditionally
880      * indent all the interior levels of the lambda based on whether we had to break before the
881      * opening brace (or scope function). This mimics the look of a block when the break is taken.
882      *
883      * These conditional indents should not be used inside interior blocks, since that would apply
884      * the condition twice.
885      */
886     val bracePlusBlockIndent = ifBrokeBeforeBrace(blockPlusExpressionBreakIndent, blockIndent)
887     val bracePlusExpressionIndent =
888         ifBrokeBeforeBrace(doubleExpressionBreakIndent, expressionBreakIndent)
889     val bracePlusZeroIndent = ifBrokeBeforeBrace(expressionBreakIndent, ZERO)
890 
891     builder.token("{")
892 
893     if (hasParams || hasArrow) {
894       builder.space()
895       builder.block(bracePlusExpressionIndent) { visitEachCommaSeparated(valueParams) }
896       builder.block(bracePlusBlockIndent) {
897         if (lambdaExpression.functionLiteral.valueParameterList?.trailingComma != null) {
898           builder.token(",")
899           builder.forcedBreak()
900         } else if (hasParams) {
901           builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
902         }
903         builder.token("->")
904       }
905     }
906 
907     if (hasParams || hasArrow || hasStatements || hasComments) {
908       builder.breakOp(Doc.FillMode.UNIFIED, " ", bracePlusZeroIndent)
909     }
910 
911     if (hasStatements) {
912       builder.breakOp(Doc.FillMode.UNIFIED, "", bracePlusBlockIndent)
913       builder.block(bracePlusBlockIndent) {
914         builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
915         if (expressionStatements.size == 1 &&
916             expressionStatements.first() !is KtReturnExpression &&
917             !bodyExpression.startsWithComment()) {
918           visitStatement(expressionStatements[0])
919         } else {
920           visitStatements(expressionStatements)
921         }
922         builder.breakOp(Doc.FillMode.UNIFIED, " ", bracePlusZeroIndent)
923       }
924     }
925 
926     if (hasParams || hasArrow || hasStatements) {
927       // If we had to break in the body, ensure there is a break before the closing brace
928       builder.breakOp(Doc.FillMode.UNIFIED, "", bracePlusZeroIndent)
929     }
930     builder.block(bracePlusZeroIndent) {
931       builder.fenceComments()
932       builder.token("}", blockIndent)
933     }
934   }
935 
936   /** Example `this` or `this@Foo` */
937   override fun visitThisExpression(expression: KtThisExpression) {
938     builder.sync(expression)
939     builder.token("this")
940     visit(expression.getTargetLabel())
941   }
942 
943   /** Example `Foo` or `@Foo` */
944   override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
945     builder.sync(expression)
946     when (expression) {
947       is KtLabelReferenceExpression -> {
948         if (expression.text[0] == '@') {
949           builder.token("@")
950           builder.token(expression.getIdentifier()?.text ?: fail())
951         } else {
952           builder.token(expression.getIdentifier()?.text ?: fail())
953           builder.token("@")
954         }
955       }
956       else -> {
957         if (expression.text.isNotEmpty()) {
958           builder.token(expression.text)
959         }
960       }
961     }
962   }
963 
964   /** e.g., `a: Int, b: Int, c: Int` in `fun foo(a: Int, b: Int, c: Int) { ... }`. */
965   override fun visitParameterList(list: KtParameterList) {
966     visitEachCommaSeparated(list.parameters, list.trailingComma != null, wrapInBlock = false)
967   }
968 
969   /**
970    * Visit each element in [list], with comma (,) tokens in-between.
971    *
972    * Example:
973    * ```
974    * a, b, c, 3, 4, 5
975    * ```
976    *
977    * Either the entire list fits in one line, or each element is put on its own line:
978    * ```
979    * a,
980    * b,
981    * c,
982    * 3,
983    * 4,
984    * 5
985    * ```
986    *
987    * Optionally include a prefix and postfix:
988    * ```
989    *   (
990    *     a,
991    *     b,
992    *     c,
993    * )
994    * ```
995    *
996    * @param hasTrailingComma if true, each element is placed on its own line (even if they could've
997    *   fit in a single line), and a trailing comma is emitted.
998    *
999    * Example:
1000    * ```
1001    * a,
1002    * b,
1003    * ```
1004    *
1005    * @param wrapInBlock if true, place all the elements in a block. When there's no [leadingBreak],
1006    *   this will be negatively indented. Note that the [prefix] and [postfix] aren't included in the
1007    *   block.
1008    * @param leadingBreak if true, break before the first element.
1009    * @param prefix if provided, emit this before the first element.
1010    * @param postfix if provided, emit this after the last element (or trailing comma).
1011    * @param breakAfterPrefix if true, emit a break after [prefix], but before the start of the
1012    *   block.
1013    * @param breakBeforePostfix if true, place a break after the last element. Redundant when
1014    *   [hasTrailingComma] is true.
1015    * @return a [BreakTag] which can tell you if a break was taken, but only when the list doesn't
1016    *   terminate in a negative closing indent.
1017    *
1018    * Example 1, this returns a BreakTag which tells you a break wasn't taken:
1019    * ```
1020    * (arg1, arg2)
1021    * ```
1022    *
1023    * Example 2, this returns a BreakTag which tells you a break WAS taken:
1024    * ```
1025    * (
1026    *     arg1,
1027    *     arg2)
1028    * ```
1029    *
1030    * Example 3, this returns null:
1031    * ```
1032    * (
1033    *     arg1,
1034    *     arg2,
1035    * )
1036    * ```
1037    *
1038    * Example 4, this also returns null (similar to example 2, but Google style):
1039    * ```
1040    * (
1041    *     arg1,
1042    *     arg2
1043    * )
1044    * ```
1045    */
1046   private fun visitEachCommaSeparated(
1047       list: Iterable<PsiElement>,
1048       hasTrailingComma: Boolean = false,
1049       wrapInBlock: Boolean = true,
1050       leadingBreak: Boolean = true,
1051       prefix: String? = null,
1052       postfix: String? = null,
1053       breakAfterPrefix: Boolean = true,
1054       breakBeforePostfix: Boolean = options.manageTrailingCommas,
1055   ): BreakTag? {
1056     val breakAfterLastElement = hasTrailingComma || (postfix != null && breakBeforePostfix)
1057     val nameTag = if (breakAfterLastElement) null else genSym()
1058 
1059     if (prefix != null) {
1060       builder.token(prefix)
1061       if (breakAfterPrefix) {
1062         builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO, Optional.ofNullable(nameTag))
1063       }
1064     }
1065 
1066     val breakType = if (hasTrailingComma) Doc.FillMode.FORCED else Doc.FillMode.UNIFIED
1067     fun emitComma() {
1068       builder.token(",")
1069       builder.breakOp(breakType, " ", ZERO)
1070     }
1071 
1072     val indent = if (leadingBreak) ZERO else expressionBreakNegativeIndent
1073     builder.block(indent, isEnabled = wrapInBlock) {
1074       if (leadingBreak) {
1075         builder.breakOp(breakType, "", ZERO)
1076       }
1077 
1078       var first = true
1079       for (value in list) {
1080         if (!first) emitComma()
1081         first = false
1082         visit(value)
1083       }
1084 
1085       if (hasTrailingComma) {
1086         emitComma()
1087       }
1088     }
1089 
1090     if (breakAfterLastElement) {
1091       // a negative closing indent places the postfix to the left of the elements
1092       // see examples 2 and 4 in the docstring
1093       builder.breakOp(breakType, "", expressionBreakNegativeIndent)
1094     }
1095 
1096     if (postfix != null) {
1097       if (breakAfterLastElement) {
1098         builder.block(expressionBreakNegativeIndent) {
1099           builder.fenceComments()
1100           builder.token(postfix, expressionBreakIndent)
1101         }
1102       } else {
1103         builder.token(postfix)
1104       }
1105     }
1106 
1107     return nameTag
1108   }
1109 
1110   /** Example `a` in `foo(a)`, or `*a`, or `limit = 50` */
1111   override fun visitArgument(argument: KtValueArgument) {
1112     visitArgumentInternal(
1113         argument,
1114         wrapInBlock = true,
1115         brokeBeforeBrace = null,
1116     )
1117   }
1118 
1119   /**
1120    * The internal version of [visitArgument].
1121    *
1122    * @param wrapInBlock if true places the argument expression in a block.
1123    */
1124   private fun visitArgumentInternal(
1125       argument: KtValueArgument,
1126       wrapInBlock: Boolean,
1127       brokeBeforeBrace: BreakTag?,
1128   ) {
1129     builder.sync(argument)
1130     val hasArgName = argument.getArgumentName() != null
1131     val isLambda = argument.getArgumentExpression() is KtLambdaExpression
1132     if (hasArgName) {
1133       visit(argument.getArgumentName())
1134       builder.space()
1135       builder.token("=")
1136       if (isLambda) {
1137         builder.space()
1138       }
1139     }
1140     val indent = if (hasArgName && !isLambda) expressionBreakIndent else ZERO
1141     builder.block(indent, isEnabled = wrapInBlock) {
1142       if (hasArgName && !isLambda) {
1143         builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
1144       }
1145       if (argument.isSpread) {
1146         builder.token("*")
1147       }
1148       if (isLambda) {
1149         visitLambdaExpressionInternal(
1150             argument.getArgumentExpression() as KtLambdaExpression,
1151             brokeBeforeBrace = brokeBeforeBrace,
1152         )
1153       } else {
1154         visit(argument.getArgumentExpression())
1155       }
1156     }
1157   }
1158 
1159   override fun visitReferenceExpression(expression: KtReferenceExpression) {
1160     builder.sync(expression)
1161     builder.token(expression.text)
1162   }
1163 
1164   override fun visitReturnExpression(expression: KtReturnExpression) {
1165     builder.sync(expression)
1166     builder.token("return")
1167     visit(expression.getTargetLabel())
1168     val returnedExpression = expression.returnedExpression
1169     if (returnedExpression != null) {
1170       builder.space()
1171       visit(returnedExpression)
1172     }
1173     builder.guessToken(";")
1174   }
1175 
1176   /**
1177    * For example `a + b`, `a + b + c` or `a..b`
1178    *
1179    * The extra handling here drills to the left most expression and handles it for long chains of
1180    * binary expressions that are formatted not accordingly to the associative values That is, we
1181    * want to think of `a + b + c` as `(a + b) + c`, whereas the AST parses it as `a + (b + c)`
1182    */
1183   override fun visitBinaryExpression(expression: KtBinaryExpression) {
1184     builder.sync(expression)
1185     val op = expression.operationToken
1186 
1187     if (KtTokens.ALL_ASSIGNMENTS.contains(op) && isLambdaOrScopingFunction(expression.right)) {
1188       // Assignments are statements in Kotlin; we don't have to worry about compound assignment.
1189       visit(expression.left)
1190       builder.space()
1191       builder.token(expression.operationReference.text)
1192       visitLambdaOrScopingFunction(expression.right)
1193       return
1194     }
1195 
1196     val parts =
1197         ArrayDeque<KtBinaryExpression>().apply {
1198           var current: KtExpression? = expression
1199           while (current is KtBinaryExpression && current.operationToken == op) {
1200             addFirst(current)
1201             current = current.left
1202           }
1203         }
1204 
1205     val leftMostExpression = parts.first()
1206     visit(leftMostExpression.left)
1207     for (leftExpression in parts) {
1208       val isFirst = leftExpression === leftMostExpression
1209 
1210       when (leftExpression.operationToken) {
1211         KtTokens.RANGE,
1212         KtTokens.RANGE_UNTIL -> {
1213           if (isFirst) {
1214             builder.open(expressionBreakIndent)
1215           }
1216           builder.token(leftExpression.operationReference.text)
1217         }
1218         KtTokens.ELVIS -> {
1219           if (isFirst) {
1220             builder.open(expressionBreakIndent)
1221           }
1222           builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1223           builder.token(leftExpression.operationReference.text)
1224           builder.space()
1225         }
1226         else -> {
1227           builder.space()
1228           if (isFirst) {
1229             builder.open(expressionBreakIndent)
1230           }
1231           builder.token(leftExpression.operationReference.text)
1232           builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1233         }
1234       }
1235       visit(leftExpression.right)
1236     }
1237     builder.close()
1238   }
1239 
1240   override fun visitPostfixExpression(expression: KtPostfixExpression) {
1241     builder.sync(expression)
1242     builder.block(ZERO) {
1243       val baseExpression = expression.baseExpression
1244       val operator = expression.operationReference.text
1245 
1246       visit(baseExpression)
1247       if (baseExpression is KtPostfixExpression &&
1248           baseExpression.operationReference.text.last() == operator.first()) {
1249         builder.space()
1250       }
1251       builder.token(operator)
1252     }
1253   }
1254 
1255   override fun visitPrefixExpression(expression: KtPrefixExpression) {
1256     builder.sync(expression)
1257     builder.block(ZERO) {
1258       val baseExpression = expression.baseExpression
1259       val operator = expression.operationReference.text
1260 
1261       builder.token(operator)
1262       if (baseExpression is KtPrefixExpression &&
1263           operator.last() == baseExpression.operationReference.text.first()) {
1264         builder.space()
1265       }
1266       visit(baseExpression)
1267     }
1268   }
1269 
1270   override fun visitLabeledExpression(expression: KtLabeledExpression) {
1271     builder.sync(expression)
1272     visit(expression.labelQualifier)
1273     if (expression.baseExpression !is KtLambdaExpression) {
1274       builder.space()
1275     }
1276     visit(expression.baseExpression)
1277   }
1278 
1279   internal enum class DeclarationKind {
1280     FIELD,
1281     PARAMETER
1282   }
1283 
1284   /**
1285    * Declare one variable or variable-like thing.
1286    *
1287    * Examples:
1288    * - `var a: Int = 5`
1289    * - `a: Int`
1290    * - `private val b:
1291    */
1292   private fun declareOne(
1293       kind: DeclarationKind,
1294       modifiers: KtModifierList?,
1295       valOrVarKeyword: String?,
1296       typeParameters: KtTypeParameterList? = null,
1297       receiver: KtTypeReference? = null,
1298       name: String?,
1299       type: KtTypeReference?,
1300       typeConstraintList: KtTypeConstraintList? = null,
1301       initializer: KtExpression?,
1302       delegate: KtPropertyDelegate? = null,
1303       accessors: List<KtPropertyAccessor>? = null
1304   ): Int {
1305     val verticalAnnotationBreak = genSym()
1306 
1307     val isField = kind == DeclarationKind.FIELD
1308 
1309     if (isField) {
1310       builder.blankLineWanted(OpsBuilder.BlankLineWanted.conditional(verticalAnnotationBreak))
1311     }
1312 
1313     visit(modifiers)
1314     builder.block(ZERO) {
1315       builder.block(ZERO) {
1316         if (valOrVarKeyword != null) {
1317           builder.token(valOrVarKeyword)
1318           builder.space()
1319         }
1320 
1321         if (typeParameters != null) {
1322           visit(typeParameters)
1323           builder.space()
1324         }
1325 
1326         // conditionally indent the name and initializer +4 if the type spans
1327         // multiple lines
1328         if (name != null) {
1329           if (receiver != null) {
1330             visit(receiver)
1331             builder.token(".")
1332           }
1333           builder.token(name)
1334         }
1335       }
1336 
1337       builder.block(expressionBreakIndent, isEnabled = name != null) {
1338         // For example `: String` in `val thisIsALongName: String` or `fun f(): String`
1339         if (type != null) {
1340           if (name != null) {
1341             builder.token(":")
1342             builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1343           }
1344           visit(type)
1345         }
1346       }
1347 
1348       // For example `where T : Int` in a generic method
1349       if (typeConstraintList != null) {
1350         builder.space()
1351         visit(typeConstraintList)
1352         builder.space()
1353       }
1354 
1355       // for example `by lazy { compute() }`
1356       if (delegate != null) {
1357         builder.space()
1358         builder.token("by")
1359         if (isLambdaOrScopingFunction(delegate.expression)) {
1360           builder.space()
1361           visit(delegate)
1362         } else {
1363           builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
1364           builder.block(expressionBreakIndent) {
1365             builder.fenceComments()
1366             visit(delegate)
1367           }
1368         }
1369       } else if (initializer != null) {
1370         builder.space()
1371         builder.token("=")
1372         if (isLambdaOrScopingFunction(initializer)) {
1373           visitLambdaOrScopingFunction(initializer)
1374         } else {
1375           builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
1376           builder.block(expressionBreakIndent) {
1377             builder.fenceComments()
1378             visit(initializer)
1379           }
1380         }
1381       }
1382     }
1383     // for example `private set` or `get = 2 * field`
1384     if (accessors?.isNotEmpty() == true) {
1385       builder.block(blockIndent) {
1386         for (accessor in accessors) {
1387           builder.forcedBreak()
1388           // The semicolon must come after the newline, or the output code will not parse.
1389           builder.guessToken(";")
1390 
1391           builder.block(ZERO) {
1392             visitFunctionLikeExpression(
1393                 contextReceiverList = null,
1394                 modifierList = accessor.modifierList,
1395                 keyword = accessor.namePlaceholder.text,
1396                 typeParameters = null,
1397                 receiverTypeReference = null,
1398                 name = null,
1399                 parameterList = getParameterListWithBugFixes(accessor),
1400                 typeConstraintList = null,
1401                 bodyExpression = accessor.bodyBlockExpression ?: accessor.bodyExpression,
1402                 typeOrDelegationCall = accessor.returnTypeReference,
1403             )
1404           }
1405         }
1406       }
1407     }
1408 
1409     builder.guessToken(";")
1410 
1411     if (isField) {
1412       builder.blankLineWanted(OpsBuilder.BlankLineWanted.conditional(verticalAnnotationBreak))
1413     }
1414 
1415     return 0
1416   }
1417 
1418   // Bug in Kotlin 1.9.10: KtProperyAccessor is the direct parent of the left and right paren
1419   // elements. Also parameterList is always null for getters. As a workaround, we create our own
1420   // fake KtParameterList.
1421   private fun getParameterListWithBugFixes(accessor: KtPropertyAccessor): KtParameterList? {
1422     if (accessor.bodyExpression == null && accessor.bodyBlockExpression == null) return null
1423 
1424     return object :
1425         KtParameterList(
1426             KotlinPlaceHolderStubImpl(accessor.stub, KtStubElementTypes.VALUE_PARAMETER_LIST)) {
1427       override fun getParameters(): List<KtParameter> {
1428         return accessor.valueParameters
1429       }
1430 
1431       override fun getTrailingComma(): PsiElement? {
1432         return accessor.parameterList?.trailingComma
1433       }
1434 
1435       override fun getLeftParenthesis(): PsiElement? {
1436         return accessor.leftParenthesis
1437       }
1438 
1439       override fun getRightParenthesis(): PsiElement? {
1440         return accessor.rightParenthesis
1441       }
1442     }
1443   }
1444 
1445   /**
1446    * Returns whether an expression is a lambda or initializer expression in which case we will want
1447    * to avoid indenting the lambda block
1448    *
1449    * Examples:
1450    * 1. '... = { ... }' is a lambda expression
1451    * 2. '... = Runnable { ... }' is considered a scoping function
1452    * 3. '... = scope { ... }' '... = apply { ... }' is a scoping function
1453    *
1454    * but not:
1455    * 1. '... = foo() { ... }' due to the empty parenthesis
1456    * 2. '... = Runnable @Annotation { ... }' due to the annotation
1457    */
1458   private fun isLambdaOrScopingFunction(expression: KtExpression?): Boolean {
1459     if (expression == null) return false
1460     if (expression.getPrevSiblingIgnoringWhitespace() is PsiComment) {
1461       return false // Leading comments cause weird indentation.
1462     }
1463 
1464     var carry = expression
1465     if (carry is KtCallExpression) {
1466       if (carry.valueArgumentList?.leftParenthesis == null &&
1467           carry.lambdaArguments.isNotEmpty() &&
1468           carry.typeArgumentList?.arguments.isNullOrEmpty()) {
1469         carry = carry.lambdaArguments[0].getArgumentExpression()
1470       } else {
1471         return false
1472       }
1473     }
1474     if (carry is KtLabeledExpression) {
1475       carry = carry.baseExpression
1476     }
1477     if (carry is KtLambdaExpression) {
1478       return true
1479     }
1480 
1481     return false
1482   }
1483 
1484   /** See [isLambdaOrScopingFunction] for examples. */
1485   private fun visitLambdaOrScopingFunction(expr: PsiElement?) {
1486     val breakToExpr = genSym()
1487     builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent, Optional.of(breakToExpr))
1488 
1489     var carry = expr
1490     if (carry is KtCallExpression) {
1491       visit(carry.calleeExpression)
1492       builder.space()
1493       carry = carry.lambdaArguments[0].getArgumentExpression()
1494     }
1495     if (carry is KtLabeledExpression) {
1496       visit(carry.labelQualifier)
1497       carry = carry.baseExpression ?: fail()
1498     }
1499     if (carry is KtLambdaExpression) {
1500       visitLambdaExpressionInternal(carry, brokeBeforeBrace = breakToExpr)
1501       return
1502     }
1503 
1504     throw AssertionError(carry)
1505   }
1506 
1507   override fun visitClassOrObject(classOrObject: KtClassOrObject) {
1508     builder.sync(classOrObject)
1509     val contextReceiverList =
1510         classOrObject.getStubOrPsiChild(KtStubElementTypes.CONTEXT_RECEIVER_LIST)
1511     val modifierList = classOrObject.modifierList
1512     builder.block(ZERO) {
1513       if (contextReceiverList != null) {
1514         visitContextReceiverList(contextReceiverList)
1515       }
1516       if (modifierList != null) {
1517         visitModifierList(modifierList)
1518       }
1519       val declarationKeyword = classOrObject.getDeclarationKeyword()
1520       if (declarationKeyword != null) {
1521         builder.token(declarationKeyword.text ?: fail())
1522       }
1523       val name = classOrObject.nameIdentifier
1524       if (name != null) {
1525         builder.space()
1526         builder.token(name.text)
1527         visit(classOrObject.typeParameterList)
1528       }
1529       visit(classOrObject.primaryConstructor)
1530       val superTypes = classOrObject.getSuperTypeList()
1531       if (superTypes != null) {
1532         builder.space()
1533         builder.block(ZERO) {
1534           builder.token(":")
1535           builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
1536           visit(superTypes)
1537         }
1538       }
1539       builder.space()
1540       val typeConstraintList = classOrObject.typeConstraintList
1541       if (typeConstraintList != null) {
1542         if (superTypes?.entries?.lastOrNull() is KtDelegatedSuperTypeEntry) {
1543           builder.forcedBreak()
1544         }
1545         visit(typeConstraintList)
1546         builder.space()
1547       }
1548       visit(classOrObject.body)
1549     }
1550     if (classOrObject.nameIdentifier != null) {
1551       builder.forcedBreak()
1552     }
1553   }
1554 
1555   override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor) {
1556     builder.sync(constructor)
1557     builder.block(ZERO) {
1558       if (constructor.hasConstructorKeyword()) {
1559         builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1560       }
1561       visitFunctionLikeExpression(
1562           contextReceiverList = null,
1563           modifierList = constructor.modifierList,
1564           keyword = if (constructor.hasConstructorKeyword()) "constructor" else null,
1565           typeParameters = null,
1566           receiverTypeReference = null,
1567           name = null,
1568           parameterList = constructor.valueParameterList,
1569           typeConstraintList = null,
1570           bodyExpression = constructor.bodyExpression,
1571           typeOrDelegationCall = null,
1572       )
1573     }
1574   }
1575 
1576   /** Example `private constructor(n: Int) : this(4, 5) { ... }` inside a class's body */
1577   override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) {
1578     builder.sync(constructor)
1579     builder.block(ZERO) {
1580       val delegationCall = constructor.getDelegationCall()
1581       visitFunctionLikeExpression(
1582           contextReceiverList =
1583               constructor.getStubOrPsiChild(KtStubElementTypes.CONTEXT_RECEIVER_LIST),
1584           modifierList = constructor.modifierList,
1585           keyword = "constructor",
1586           typeParameters = null,
1587           receiverTypeReference = null,
1588           name = null,
1589           parameterList = constructor.valueParameterList,
1590           typeConstraintList = null,
1591           bodyExpression = constructor.bodyExpression,
1592           typeOrDelegationCall = if (!delegationCall.isImplicit) delegationCall else null,
1593       )
1594     }
1595   }
1596 
1597   override fun visitConstructorDelegationCall(call: KtConstructorDelegationCall) {
1598     // Work around a misfeature in kotlin-compiler: call.calleeExpression.accept doesn't call
1599     // visitReferenceExpression, but calls visitElement instead.
1600     builder.block(ZERO) {
1601       builder.token(if (call.isCallToThis) "this" else "super")
1602       visitCallElement(
1603           null,
1604           call.typeArgumentList,
1605           call.valueArgumentList,
1606           call.lambdaArguments,
1607       )
1608     }
1609   }
1610 
1611   override fun visitClassInitializer(initializer: KtClassInitializer) {
1612     builder.sync(initializer)
1613     builder.token("init")
1614     builder.space()
1615     visit(initializer.body)
1616   }
1617 
1618   override fun visitConstantExpression(expression: KtConstantExpression) {
1619     builder.sync(expression)
1620     builder.token(expression.text)
1621   }
1622 
1623   /** Example `(1 + 1)` */
1624   override fun visitParenthesizedExpression(expression: KtParenthesizedExpression) {
1625     builder.sync(expression)
1626     builder.token("(")
1627     visit(expression.expression)
1628     builder.token(")")
1629   }
1630 
1631   override fun visitPackageDirective(directive: KtPackageDirective) {
1632     builder.sync(directive)
1633     if (directive.packageKeyword == null) {
1634       return
1635     }
1636     builder.token("package")
1637     builder.space()
1638     var first = true
1639     for (packageName in directive.packageNames) {
1640       if (first) {
1641         first = false
1642       } else {
1643         builder.token(".")
1644       }
1645       builder.token(packageName.getIdentifier()?.text ?: packageName.getReferencedName())
1646     }
1647 
1648     builder.guessToken(";")
1649     builder.forcedBreak()
1650   }
1651 
1652   /** Example `import com.foo.A; import com.bar.B` */
1653   override fun visitImportList(importList: KtImportList) {
1654     builder.sync(importList)
1655     importList.imports.forEach { visit(it) }
1656   }
1657 
1658   /** Example `import com.foo.A` */
1659   override fun visitImportDirective(directive: KtImportDirective) {
1660     builder.sync(directive)
1661     builder.token("import")
1662     builder.space()
1663 
1664     val importedReference = directive.importedReference
1665     if (importedReference != null) {
1666       inImport = true
1667       visit(importedReference)
1668       inImport = false
1669     }
1670     if (directive.isAllUnder) {
1671       builder.token(".")
1672       builder.token("*")
1673     }
1674 
1675     // Possible alias.
1676     val alias = directive.alias?.nameIdentifier
1677     if (alias != null) {
1678       builder.space()
1679       builder.token("as")
1680       builder.space()
1681       builder.token(alias.text ?: fail())
1682     }
1683 
1684     // Force a newline afterwards.
1685     builder.guessToken(";")
1686     builder.forcedBreak()
1687   }
1688 
1689   /** Example `context(Logger, Raise<Error>)` */
1690   override fun visitContextReceiverList(contextReceiverList: KtContextReceiverList) {
1691     builder.sync(contextReceiverList)
1692     builder.token("context")
1693     visitEachCommaSeparated(
1694         contextReceiverList.contextReceivers(),
1695         prefix = "(",
1696         postfix = ")",
1697         breakAfterPrefix = false,
1698         breakBeforePostfix = false,
1699     )
1700     builder.forcedBreak()
1701   }
1702 
1703   /** For example `@Magic private final` */
1704   override fun visitModifierList(list: KtModifierList) {
1705     builder.sync(list)
1706     var onlyAnnotationsSoFar = true
1707 
1708     for (child in list.node.children()) {
1709       val psi = child.psi
1710       if (psi is PsiWhiteSpace) {
1711         continue
1712       }
1713 
1714       if (child.elementType is KtModifierKeywordToken) {
1715         onlyAnnotationsSoFar = false
1716         builder.token(child.text)
1717       } else {
1718         visit(psi)
1719       }
1720 
1721       if (onlyAnnotationsSoFar) {
1722         builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1723       } else {
1724         builder.space()
1725       }
1726     }
1727   }
1728 
1729   /**
1730    * Example:
1731    * ```
1732    * @SuppressLint("MagicNumber")
1733    * print(10)
1734    * ```
1735    *
1736    * in
1737    *
1738    * ```
1739    * fun f() {
1740    *   @SuppressLint("MagicNumber")
1741    *   print(10)
1742    * }
1743    * ```
1744    */
1745   override fun visitAnnotatedExpression(expression: KtAnnotatedExpression) {
1746     builder.sync(expression)
1747     builder.block(ZERO) {
1748       val baseExpression = expression.baseExpression
1749 
1750       builder.block(ZERO) {
1751         val annotationEntries = expression.annotationEntries
1752         for (annotationEntry in annotationEntries) {
1753           if (annotationEntry !== annotationEntries.first()) {
1754             builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1755           }
1756           visit(annotationEntry)
1757         }
1758       }
1759 
1760       // Binary expressions in a block have a different meaning according to their formatting.
1761       // If there in the line above, they refer to the entire expression, if they're in the same
1762       // line then only to the first operand of the operator.
1763       // We force a break to avoid such semantic changes
1764       when {
1765         (baseExpression is KtBinaryExpression || baseExpression is KtBinaryExpressionWithTypeRHS) &&
1766             expression.parent is KtBlockExpression -> builder.forcedBreak()
1767         baseExpression is KtLambdaExpression -> builder.space()
1768         baseExpression is KtReturnExpression -> builder.forcedBreak()
1769         else -> builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1770       }
1771 
1772       visit(expression.baseExpression)
1773     }
1774   }
1775 
1776   /**
1777    * For example, @field:[Inject Named("WEB_VIEW")]
1778    *
1779    * A KtAnnotation is used only to group multiple annotations with the same use-site-target. It
1780    * only appears in a modifier list since annotated expressions do not have use-site-targets.
1781    */
1782   override fun visitAnnotation(annotation: KtAnnotation) {
1783     builder.sync(annotation)
1784     builder.block(ZERO) {
1785       builder.token("@")
1786       val useSiteTarget = annotation.useSiteTarget
1787       if (useSiteTarget != null) {
1788         visit(useSiteTarget)
1789         builder.token(":")
1790       }
1791       builder.block(expressionBreakIndent) {
1792         builder.token("[")
1793 
1794         builder.block(ZERO) {
1795           var first = true
1796           builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
1797           for (value in annotation.entries) {
1798             if (!first) {
1799               builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
1800             }
1801             first = false
1802 
1803             visit(value)
1804           }
1805         }
1806       }
1807       builder.token("]")
1808     }
1809     builder.forcedBreak()
1810   }
1811 
1812   /** For example, 'field' in @field:[Inject Named("WEB_VIEW")] */
1813   override fun visitAnnotationUseSiteTarget(
1814       annotationTarget: KtAnnotationUseSiteTarget,
1815       data: Void?
1816   ): Void? {
1817     builder.token(annotationTarget.getAnnotationUseSiteTarget().renderName)
1818     return null
1819   }
1820 
1821   /** For example `@Magic` or `@Fred(1, 5)` */
1822   override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) {
1823     builder.sync(annotationEntry)
1824     if (annotationEntry.atSymbol != null) {
1825       builder.token("@")
1826     }
1827     val useSiteTarget = annotationEntry.useSiteTarget
1828     if (useSiteTarget != null && useSiteTarget.parent == annotationEntry) {
1829       visit(useSiteTarget)
1830       builder.token(":")
1831     }
1832     visitCallElement(
1833         annotationEntry.calleeExpression,
1834         null, // Type-arguments are included in the annotation's callee expression.
1835         annotationEntry.valueArgumentList,
1836         listOf())
1837   }
1838 
1839   override fun visitFileAnnotationList(
1840       fileAnnotationList: KtFileAnnotationList,
1841       data: Void?
1842   ): Void? {
1843     for (child in fileAnnotationList.node.children()) {
1844       if (child is PsiElement) {
1845         continue
1846       }
1847       visit(child.psi)
1848       builder.forcedBreak()
1849     }
1850 
1851     return null
1852   }
1853 
1854   override fun visitSuperTypeList(list: KtSuperTypeList) {
1855     builder.sync(list)
1856     builder.block(expressionBreakIndent) { visitEachCommaSeparated(list.entries) }
1857   }
1858 
1859   override fun visitSuperTypeCallEntry(call: KtSuperTypeCallEntry) {
1860     builder.sync(call)
1861     visitCallElement(call.calleeExpression, null, call.valueArgumentList, call.lambdaArguments)
1862   }
1863 
1864   /**
1865    * Example `Collection<Int> by list` in `class MyList(list: List<Int>) : Collection<Int> by list`
1866    */
1867   override fun visitDelegatedSuperTypeEntry(specifier: KtDelegatedSuperTypeEntry) {
1868     builder.sync(specifier)
1869     visit(specifier.typeReference)
1870     builder.space()
1871     builder.token("by")
1872     builder.space()
1873     visit(specifier.delegateExpression)
1874   }
1875 
1876   override fun visitWhenExpression(expression: KtWhenExpression) {
1877     builder.sync(expression)
1878     builder.block(ZERO) {
1879       emitKeywordWithCondition("when", expression.subjectExpression)
1880 
1881       builder.space()
1882       builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
1883 
1884       expression.entries.forEachIndexed { index, whenEntry ->
1885         builder.block(blockIndent) {
1886           if (index != 0) {
1887             // preserve new line if there's one
1888             builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
1889           }
1890           builder.forcedBreak()
1891           if (whenEntry.isElse) {
1892             builder.token("else")
1893           } else {
1894             builder.block(ZERO) {
1895               val conditions = whenEntry.conditions
1896               for ((index, condition) in conditions.withIndex()) {
1897                 visit(condition)
1898                 builder.guessToken(",")
1899                 if (index != conditions.lastIndex) {
1900                   builder.forcedBreak()
1901                 }
1902               }
1903             }
1904           }
1905           val whenExpression = whenEntry.expression
1906           builder.space()
1907           builder.token("->")
1908           if (whenExpression is KtBlockExpression) {
1909             builder.space()
1910             visit(whenExpression)
1911           } else {
1912             builder.block(expressionBreakIndent) {
1913               builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
1914               visit(whenExpression)
1915             }
1916           }
1917           builder.guessToken(";")
1918         }
1919         builder.forcedBreak()
1920       }
1921       builder.token("}")
1922     }
1923   }
1924 
1925   override fun visitClassBody(body: KtClassBody) {
1926     builder.sync(body)
1927     emitBracedBlock(body) { children ->
1928       val enumEntryList = EnumEntryList.extractChildList(body)
1929       val members = children.filter { it !is KtEnumEntry }
1930 
1931       if (enumEntryList != null) {
1932         builder.block(ZERO) {
1933           builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
1934           for (value in enumEntryList.enumEntries) {
1935             visit(value)
1936             if (builder.peekToken() == Optional.of(",")) {
1937               builder.token(",")
1938               builder.forcedBreak()
1939             }
1940           }
1941         }
1942         builder.guessToken(";")
1943 
1944         if (members.isNotEmpty()) {
1945           builder.forcedBreak()
1946           builder.blankLineWanted(OpsBuilder.BlankLineWanted.YES)
1947         }
1948       } else {
1949         val parent = body.parent
1950         if (parent is KtClass && parent.isEnum() && children.isNotEmpty()) {
1951           builder.token(";")
1952           builder.forcedBreak()
1953         }
1954       }
1955 
1956       var prev: PsiElement? = null
1957       for (curr in members) {
1958         val blankLineBetweenMembers =
1959             when {
1960               prev == null -> OpsBuilder.BlankLineWanted.PRESERVE
1961               prev !is KtProperty -> OpsBuilder.BlankLineWanted.YES
1962               prev.getter != null || prev.setter != null -> OpsBuilder.BlankLineWanted.YES
1963               curr is KtProperty -> OpsBuilder.BlankLineWanted.PRESERVE
1964               else -> OpsBuilder.BlankLineWanted.YES
1965             }
1966         builder.blankLineWanted(blankLineBetweenMembers)
1967 
1968         builder.block(ZERO) { visit(curr) }
1969         builder.guessToken(";")
1970         builder.forcedBreak()
1971 
1972         prev = curr
1973       }
1974     }
1975   }
1976 
1977   override fun visitBlockExpression(expression: KtBlockExpression) {
1978     builder.sync(expression)
1979     emitBracedBlock(expression) { children -> visitStatements(children) }
1980   }
1981 
1982   override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
1983     builder.sync(condition)
1984     visit(condition.expression)
1985   }
1986 
1987   override fun visitWhenConditionIsPattern(condition: KtWhenConditionIsPattern) {
1988     builder.sync(condition)
1989     builder.token(if (condition.isNegated) "!is" else "is")
1990     builder.space()
1991     visit(condition.typeReference)
1992   }
1993 
1994   /** Example `in 1..2` as part of a when expression */
1995   override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
1996     builder.sync(condition)
1997     // TODO: replace with 'condition.isNegated' once https://youtrack.jetbrains.com/issue/KT-34395
1998     // is fixed.
1999     val isNegated = condition.firstChild?.node?.findChildByType(KtTokens.NOT_IN) != null
2000     builder.token(if (isNegated) "!in" else "in")
2001     builder.space()
2002     visit(condition.rangeExpression)
2003   }
2004 
2005   override fun visitIfExpression(expression: KtIfExpression) {
2006     builder.sync(expression)
2007     builder.block(ZERO) {
2008       emitKeywordWithCondition("if", expression.condition)
2009 
2010       if (expression.then is KtBlockExpression) {
2011         builder.space()
2012         builder.block(ZERO) { visit(expression.then) }
2013       } else {
2014         builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
2015         builder.block(expressionBreakIndent) { visit(expression.then) }
2016       }
2017 
2018       if (expression.elseKeyword != null) {
2019         if (expression.then is KtBlockExpression) {
2020           builder.space()
2021         } else {
2022           builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
2023         }
2024 
2025         builder.block(ZERO) {
2026           builder.token("else")
2027           if (expression.`else` is KtBlockExpression || expression.`else` is KtIfExpression) {
2028             builder.space()
2029             builder.block(ZERO) { visit(expression.`else`) }
2030           } else {
2031             builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
2032             builder.block(expressionBreakIndent) { visit(expression.`else`) }
2033           }
2034         }
2035       }
2036     }
2037   }
2038 
2039   /** Example `a[3]`, `b["a", 5]` or `a.b.c[4]` */
2040   override fun visitArrayAccessExpression(expression: KtArrayAccessExpression) {
2041     builder.sync(expression)
2042     if (expression.arrayExpression is KtQualifiedExpression) {
2043       emitQualifiedExpression(expression)
2044     } else {
2045       visit(expression.arrayExpression)
2046       visitArrayAccessBrackets(expression)
2047     }
2048   }
2049 
2050   /**
2051    * Example `[3]` in `a[3]` or `a[3].b` Separated since it needs to be used from a top level array
2052    * expression (`a[3]`) and from within a qualified chain (`a[3].b)
2053    */
2054   private fun visitArrayAccessBrackets(expression: KtArrayAccessExpression) {
2055     builder.block(ZERO) {
2056       builder.token("[")
2057       builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
2058       builder.block(expressionBreakIndent) {
2059         visitEachCommaSeparated(
2060             expression.indexExpressions, expression.trailingComma != null, wrapInBlock = true)
2061       }
2062     }
2063     builder.token("]")
2064   }
2065 
2066   /** Example `val (a, b: Int) = Pair(1, 2)` */
2067   override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) {
2068     builder.sync(destructuringDeclaration)
2069     val valOrVarKeyword = destructuringDeclaration.valOrVarKeyword
2070     if (valOrVarKeyword != null) {
2071       builder.token(valOrVarKeyword.text)
2072       builder.space()
2073     }
2074     val hasTrailingComma = destructuringDeclaration.trailingComma != null
2075     builder.block(ZERO) {
2076       builder.token("(")
2077       builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
2078       builder.block(expressionBreakIndent) {
2079         visitEachCommaSeparated(
2080             destructuringDeclaration.entries, hasTrailingComma, wrapInBlock = true)
2081       }
2082     }
2083     builder.token(")")
2084     val initializer = destructuringDeclaration.initializer
2085     if (initializer != null) {
2086       builder.space()
2087       builder.token("=")
2088       if (hasTrailingComma) {
2089         builder.space()
2090       } else {
2091         builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
2092       }
2093       builder.block(expressionBreakIndent, !hasTrailingComma) { visit(initializer) }
2094     }
2095   }
2096 
2097   /** Example `a: String` which is part of `(a: String, b: String)` */
2098   override fun visitDestructuringDeclarationEntry(
2099       multiDeclarationEntry: KtDestructuringDeclarationEntry
2100   ) {
2101     builder.sync(multiDeclarationEntry)
2102     declareOne(
2103         initializer = null,
2104         kind = DeclarationKind.PARAMETER,
2105         modifiers = multiDeclarationEntry.modifierList,
2106         name = multiDeclarationEntry.nameIdentifier?.text ?: fail(),
2107         type = multiDeclarationEntry.typeReference,
2108         valOrVarKeyword = null,
2109     )
2110   }
2111 
2112   /** Example `"Hello $world!"` or `"""Hello world!"""` */
2113   override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) {
2114     builder.sync(expression)
2115     builder.token(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(expression.text))
2116   }
2117 
2118   /** Example `super` in `super.doIt(5)` or `super<Foo>` in `super<Foo>.doIt(5)` */
2119   override fun visitSuperExpression(expression: KtSuperExpression) {
2120     builder.sync(expression)
2121     builder.token("super")
2122     val superTypeQualifier = expression.superTypeQualifier
2123     if (superTypeQualifier != null) {
2124       builder.token("<")
2125       visit(superTypeQualifier)
2126       builder.token(">")
2127     }
2128     visit(expression.labelQualifier)
2129   }
2130 
2131   /** Example `<T, S>` */
2132   override fun visitTypeParameterList(list: KtTypeParameterList) {
2133     builder.sync(list)
2134     builder.block(expressionBreakIndent) {
2135       visitEachCommaSeparated(
2136           list = list.parameters,
2137           hasTrailingComma = list.trailingComma != null,
2138           prefix = "<",
2139           postfix = ">",
2140           wrapInBlock = !options.manageTrailingCommas,
2141       )
2142     }
2143   }
2144 
2145   override fun visitTypeParameter(parameter: KtTypeParameter) {
2146     builder.sync(parameter)
2147     visit(parameter.modifierList)
2148     builder.token(parameter.nameIdentifier?.text ?: "")
2149     val extendsBound = parameter.extendsBound
2150     if (extendsBound != null) {
2151       builder.space()
2152       builder.token(":")
2153       builder.space()
2154       visit(extendsBound)
2155     }
2156   }
2157 
2158   /** Example `where T : View, T : Listener` */
2159   override fun visitTypeConstraintList(list: KtTypeConstraintList) {
2160     builder.token("where")
2161     builder.space()
2162     builder.sync(list)
2163     visitEachCommaSeparated(list.constraints)
2164   }
2165 
2166   /** Example `T : Foo` */
2167   override fun visitTypeConstraint(constraint: KtTypeConstraint) {
2168     builder.sync(constraint)
2169     // TODO(nreid260): What about annotations on the type reference? `where @A T : Int`
2170     visit(constraint.subjectTypeParameterName)
2171     builder.space()
2172     builder.token(":")
2173     builder.space()
2174     visit(constraint.boundTypeReference)
2175   }
2176 
2177   /** Example `for (i in items) { ... }` */
2178   override fun visitForExpression(expression: KtForExpression) {
2179     builder.sync(expression)
2180     builder.block(ZERO) {
2181       builder.token("for")
2182       builder.space()
2183       builder.token("(")
2184       visit(expression.loopParameter)
2185       builder.space()
2186       builder.token("in")
2187       builder.block(ZERO) {
2188         builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
2189         builder.block(expressionBreakIndent) { visit(expression.loopRange) }
2190       }
2191       builder.token(")")
2192       builder.space()
2193       visit(expression.body)
2194     }
2195   }
2196 
2197   /** Example `while (a < b) { ... }` */
2198   override fun visitWhileExpression(expression: KtWhileExpression) {
2199     builder.sync(expression)
2200     emitKeywordWithCondition("while", expression.condition)
2201     builder.space()
2202     visit(expression.body)
2203   }
2204 
2205   /** Example `do { ... } while (a < b)` */
2206   override fun visitDoWhileExpression(expression: KtDoWhileExpression) {
2207     builder.sync(expression)
2208     builder.token("do")
2209     builder.space()
2210     if (expression.body != null) {
2211       visit(expression.body)
2212       builder.space()
2213     }
2214     emitKeywordWithCondition("while", expression.condition)
2215   }
2216 
2217   /** Example `break` or `break@foo` in a loop */
2218   override fun visitBreakExpression(expression: KtBreakExpression) {
2219     builder.sync(expression)
2220     builder.token("break")
2221     visit(expression.labelQualifier)
2222   }
2223 
2224   /** Example `continue` or `continue@foo` in a loop */
2225   override fun visitContinueExpression(expression: KtContinueExpression) {
2226     builder.sync(expression)
2227     builder.token("continue")
2228     visit(expression.labelQualifier)
2229   }
2230 
2231   /** Example `f: String`, or `private val n: Int` or `(a: Int, b: String)` (in for-loops) */
2232   override fun visitParameter(parameter: KtParameter) {
2233     builder.sync(parameter)
2234     builder.block(ZERO) {
2235       val destructuringDeclaration = parameter.destructuringDeclaration
2236       val typeReference = parameter.typeReference
2237       if (destructuringDeclaration != null) {
2238         builder.block(ZERO) {
2239           visit(destructuringDeclaration)
2240           if (typeReference != null) {
2241             builder.token(":")
2242             builder.space()
2243             visit(typeReference)
2244           }
2245         }
2246       } else {
2247         declareOne(
2248             kind = DeclarationKind.PARAMETER,
2249             modifiers = parameter.modifierList,
2250             valOrVarKeyword = parameter.valOrVarKeyword?.text,
2251             name = parameter.nameIdentifier?.text,
2252             type = typeReference,
2253             initializer = parameter.defaultValue)
2254       }
2255     }
2256   }
2257 
2258   /** Example `String::isNullOrEmpty` */
2259   override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression) {
2260     builder.sync(expression)
2261     visit(expression.receiverExpression)
2262 
2263     // For some reason, expression.receiverExpression doesn't contain the question-mark token in
2264     // case of a nullable type, e.g., in String?::isNullOrEmpty.
2265     // Instead, KtCallableReferenceExpression exposes a method that looks for the QUEST token in
2266     // its children.
2267     if (expression.hasQuestionMarks) {
2268       builder.token("?")
2269     }
2270 
2271     builder.block(expressionBreakIndent) {
2272       builder.token("::")
2273       builder.breakOp(Doc.FillMode.INDEPENDENT, "", ZERO)
2274       visit(expression.callableReference)
2275     }
2276   }
2277 
2278   override fun visitClassLiteralExpression(expression: KtClassLiteralExpression) {
2279     builder.sync(expression)
2280     val receiverExpression = expression.receiverExpression
2281     if (receiverExpression is KtCallExpression) {
2282       visitCallElement(
2283           receiverExpression.calleeExpression,
2284           receiverExpression.typeArgumentList,
2285           receiverExpression.valueArgumentList,
2286           receiverExpression.lambdaArguments)
2287     } else {
2288       visit(receiverExpression)
2289     }
2290     builder.token("::")
2291     builder.token("class")
2292   }
2293 
2294   override fun visitFunctionType(type: KtFunctionType) {
2295     builder.sync(type)
2296 
2297     type.contextReceiverList?.let { visitContextReceiverList(it) }
2298 
2299     val receiver = type.receiver
2300     if (receiver != null) {
2301       visit(receiver)
2302       builder.token(".")
2303     }
2304     builder.block(expressionBreakIndent) {
2305       val parameterList = type.parameterList
2306       if (parameterList != null) {
2307         visitEachCommaSeparated(
2308             parameterList.parameters,
2309             prefix = "(",
2310             postfix = ")",
2311             hasTrailingComma = parameterList.trailingComma != null,
2312         )
2313       }
2314     }
2315     builder.space()
2316     builder.token("->")
2317     builder.space()
2318     builder.block(expressionBreakIndent) { visit(type.returnTypeReference) }
2319   }
2320 
2321   /** Example `a is Int` or `b !is Int` */
2322   override fun visitIsExpression(expression: KtIsExpression) {
2323     builder.sync(expression)
2324     val openGroupBeforeLeft = expression.leftHandSide !is KtQualifiedExpression
2325     if (openGroupBeforeLeft) builder.open(ZERO)
2326     visit(expression.leftHandSide)
2327     if (!openGroupBeforeLeft) builder.open(ZERO)
2328     val parent = expression.parent
2329     if (parent is KtValueArgument ||
2330         parent is KtParenthesizedExpression ||
2331         parent is KtContainerNode) {
2332       builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
2333     } else {
2334       builder.space()
2335     }
2336     visit(expression.operationReference)
2337     builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
2338     builder.block(expressionBreakIndent) { visit(expression.typeReference) }
2339     builder.close()
2340   }
2341 
2342   /** Example `a as Int` or `a as? Int` */
2343   override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS) {
2344     builder.sync(expression)
2345     val openGroupBeforeLeft = expression.left !is KtQualifiedExpression
2346     if (openGroupBeforeLeft) builder.open(ZERO)
2347     visit(expression.left)
2348     if (!openGroupBeforeLeft) builder.open(ZERO)
2349     builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
2350     visit(expression.operationReference)
2351     builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
2352     builder.block(expressionBreakIndent) { visit(expression.right) }
2353     builder.close()
2354   }
2355 
2356   /**
2357    * Example:
2358    * ```
2359    * fun f() {
2360    *   val a: Array<Int> = [1, 2, 3]
2361    * }
2362    * ```
2363    */
2364   override fun visitCollectionLiteralExpression(expression: KtCollectionLiteralExpression) {
2365     builder.sync(expression)
2366     builder.block(expressionBreakIndent) {
2367       visitEachCommaSeparated(
2368           expression.getInnerExpressions(),
2369           expression.trailingComma != null,
2370           prefix = "[",
2371           postfix = "]",
2372           wrapInBlock = !options.manageTrailingCommas)
2373     }
2374   }
2375 
2376   override fun visitTryExpression(expression: KtTryExpression) {
2377     builder.sync(expression)
2378     builder.token("try")
2379     builder.space()
2380     visit(expression.tryBlock)
2381     for (catchClause in expression.catchClauses) {
2382       visit(catchClause)
2383     }
2384     visit(expression.finallyBlock)
2385   }
2386 
2387   override fun visitCatchSection(catchClause: KtCatchClause) {
2388     builder.sync(catchClause)
2389     builder.space()
2390     builder.token("catch")
2391     builder.space()
2392     builder.block(ZERO) {
2393       builder.token("(")
2394       builder.block(expressionBreakIndent) {
2395         builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
2396         visit(catchClause.catchParameter)
2397         builder.guessToken(",")
2398       }
2399     }
2400     builder.token(")")
2401     builder.space()
2402     visit(catchClause.catchBody)
2403   }
2404 
2405   override fun visitFinallySection(finallySection: KtFinallySection) {
2406     builder.sync(finallySection)
2407     builder.space()
2408     builder.token("finally")
2409     builder.space()
2410     visit(finallySection.finalExpression)
2411   }
2412 
2413   override fun visitThrowExpression(expression: KtThrowExpression) {
2414     builder.sync(expression)
2415     builder.token("throw")
2416     builder.space()
2417     visit(expression.thrownExpression)
2418   }
2419 
2420   /** Example `RED(0xFF0000)` in an enum class */
2421   override fun visitEnumEntry(enumEntry: KtEnumEntry) {
2422     builder.sync(enumEntry)
2423     builder.block(ZERO) {
2424       visit(enumEntry.modifierList)
2425       builder.token(enumEntry.nameIdentifier?.text ?: fail())
2426       enumEntry.initializerList?.initializers?.forEach { visit(it) }
2427       enumEntry.body?.let {
2428         builder.space()
2429         visit(it)
2430       }
2431     }
2432   }
2433 
2434   /** Example `private typealias TextChangedListener = (string: String) -> Unit` */
2435   override fun visitTypeAlias(typeAlias: KtTypeAlias) {
2436     builder.sync(typeAlias)
2437     builder.block(ZERO) {
2438       visit(typeAlias.modifierList)
2439       builder.token("typealias")
2440       builder.space()
2441       builder.token(typeAlias.nameIdentifier?.text ?: fail())
2442       visit(typeAlias.typeParameterList)
2443 
2444       builder.space()
2445       builder.token("=")
2446       builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
2447       builder.block(expressionBreakIndent) {
2448         visit(typeAlias.getTypeReference())
2449         visit(typeAlias.typeConstraintList)
2450         builder.guessToken(";")
2451       }
2452       builder.forcedBreak()
2453     }
2454   }
2455 
2456   /**
2457    * visitElement is called for almost all types of AST nodes. We use it to keep track of whether
2458    * we're currently inside an expression or not.
2459    *
2460    * @throws FormattingError
2461    */
2462   override fun visitElement(element: PsiElement) {
2463     inExpression.addLast(element is KtExpression || inExpression.last())
2464     val previous = builder.depth()
2465     try {
2466       super.visitElement(element)
2467     } catch (e: FormattingError) {
2468       throw e
2469     } catch (t: Throwable) {
2470       throw FormattingError(builder.diagnostic(Throwables.getStackTraceAsString(t)))
2471     } finally {
2472       inExpression.removeLast()
2473     }
2474     builder.checkClosed(previous)
2475   }
2476 
2477   override fun visitKtFile(file: KtFile) {
2478     markForPartialFormat()
2479     val importListEmpty = file.importList?.text?.isBlank() ?: true
2480 
2481     var isFirst = true
2482     for (child in file.children) {
2483       if (child.text.isBlank()) {
2484         continue
2485       }
2486 
2487       builder.blankLineWanted(
2488           when {
2489             isFirst -> OpsBuilder.BlankLineWanted.NO
2490             child is PsiComment -> continue
2491             child is KtScript && importListEmpty -> OpsBuilder.BlankLineWanted.PRESERVE
2492             else -> OpsBuilder.BlankLineWanted.YES
2493           })
2494 
2495       visit(child)
2496       isFirst = false
2497     }
2498     markForPartialFormat()
2499   }
2500 
2501   override fun visitScript(script: KtScript) {
2502     markForPartialFormat()
2503     var lastChildHadBlankLineBefore = false
2504     var lastChildIsContextReceiver = false
2505     var first = true
2506     for (child in script.blockExpression.children) {
2507       if (child.text.isBlank()) {
2508         continue
2509       }
2510       builder.forcedBreak()
2511       val childGetsBlankLineBefore = child !is KtProperty
2512       if (first) {
2513         builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
2514       } else if (lastChildIsContextReceiver) {
2515         builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
2516       } else if (child !is PsiComment &&
2517           (childGetsBlankLineBefore || lastChildHadBlankLineBefore)) {
2518         builder.blankLineWanted(OpsBuilder.BlankLineWanted.YES)
2519       }
2520       visit(child)
2521       builder.guessToken(";")
2522       lastChildHadBlankLineBefore = childGetsBlankLineBefore
2523       lastChildIsContextReceiver =
2524           child is KtScriptInitializer &&
2525               child.firstChild?.firstChild?.firstChild?.text == "context"
2526       first = false
2527     }
2528     markForPartialFormat()
2529   }
2530 
2531   /**
2532    * markForPartialFormat is used to delineate the smallest areas of code that must be formatted
2533    * together.
2534    *
2535    * When only parts of the code are being formatted, the requested area is expanded until it's
2536    * covered by an area marked by this method.
2537    */
2538   private fun markForPartialFormat() {
2539     if (!inExpression.last()) {
2540       builder.markForPartialFormat()
2541     }
2542   }
2543 
2544   /**
2545    * Emit a [Doc.Token].
2546    *
2547    * @param token the [String] to wrap in a [Doc.Token]
2548    * @param plusIndentCommentsBefore extra block for comments before this token
2549    */
2550   private fun OpsBuilder.token(token: String, plusIndentCommentsBefore: Indent = ZERO) {
2551     token(
2552         token,
2553         Doc.Token.RealOrImaginary.REAL,
2554         plusIndentCommentsBefore,
2555         /* breakAndIndentTrailingComment */ Optional.empty())
2556   }
2557 
2558   /**
2559    * Opens a new level, emits into it and closes it.
2560    *
2561    * This is a helper method to make it easier to keep track of [OpsBuilder.open] and
2562    * [OpsBuilder.close] calls
2563    *
2564    * @param plusIndent the block level to pass to the block
2565    * @param block a code block to be run in this block level
2566    */
2567   private fun OpsBuilder.block(plusIndent: Indent, isEnabled: Boolean = true, block: () -> Unit) {
2568     if (isEnabled) {
2569       open(plusIndent)
2570     }
2571     block()
2572     if (isEnabled) {
2573       close()
2574     }
2575   }
2576 
2577   /** Helper method to sync the current offset to match any element in the AST */
2578   private fun OpsBuilder.sync(psiElement: PsiElement) {
2579     sync(psiElement.startOffset)
2580   }
2581 
2582   /** Prevent subsequent comments from being moved ahead of this point, into parent [Level]s. */
2583   private fun OpsBuilder.fenceComments() {
2584     addAll(FenceCommentsOp.AS_LIST)
2585   }
2586 
2587   /**
2588    * Throws a formatting error
2589    *
2590    * This is used as `expr ?: fail()` to avoid using the !! operator and provide better error
2591    * messages.
2592    */
2593   private fun fail(message: String = "Unexpected"): Nothing {
2594     throw FormattingError(builder.diagnostic(message))
2595   }
2596 
2597   /** Helper function to improve readability */
2598   private fun visit(element: PsiElement?) {
2599     element?.accept(this)
2600   }
2601 
2602   /** Emits a key word followed by a condition, e.g. `if (b)` or `while (c < d )` */
2603   private fun emitKeywordWithCondition(keyword: String, condition: KtExpression?) {
2604     if (condition == null) {
2605       builder.token(keyword)
2606       return
2607     }
2608 
2609     builder.block(ZERO) {
2610       builder.token(keyword)
2611       builder.space()
2612       builder.token("(")
2613       if (options.manageTrailingCommas) {
2614         builder.block(expressionBreakIndent) {
2615           builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
2616           visit(condition)
2617           builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
2618         }
2619       } else {
2620         builder.block(ZERO) { visit(condition) }
2621       }
2622     }
2623     builder.token(")")
2624   }
2625 }
2626