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