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