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