square / kotlinpoet

A Kotlin API for generating .kt source files.
https://square.github.io/kotlinpoet/
Apache License 2.0
3.9k stars 289 forks source link

Single-expression function transformation messes up the indentation #1757

Closed fejesjoco closed 11 months ago

fejesjoco commented 11 months ago

Describe the bug As I described in other issues already, sometimes I have to build large function bodies with my own indentation. It works well in most cases, except when KotlinPoet decides to transform my function into a single-expression function, it doesn't correctly handle the indentation level.

To Reproduce This is the generating code:

  val cb = buildCodeBlock {
    add(
      """
        myWhateverController.invokeWithTransactionFactory {
          longMethodCall(1, 2, 3) {
            %S
          }
        }
      """.trimIndent(),
      "X".repeat(50)
    )
  }

  val longType = ClassName("a".repeat(50), "B").nestedClass("c".repeat(50))
  FileSpec.builder("com.test", "HelloWorld")
    .addType(
      TypeSpec.classBuilder("HelloWorld")
        .addFunction(FunSpec.builder("hello1").returns(longType).addCode(cb).build())
        .addFunction(FunSpec.builder("hello2").returns(longType).addCode("return ").addCode(cb).build())
        .build()
    )
    .build()
    .writeTo(System.out)

It prints this:

package com.test

import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.B

public class HelloWorld {
  public fun hello1(): B.cccccccccccccccccccccccccccccccccccccccccccccccccc {
    myWhateverController.invokeWithTransactionFactory {
      longMethodCall(1, 2, 3) {
        "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      }
    }
  }

  public fun hello2(): B.cccccccccccccccccccccccccccccccccccccccccccccccccc =
      myWhateverController.invokeWithTransactionFactory {
    longMethodCall(1, 2, 3) {
      "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    }
  }
}

The first function looks perfect. You can see how in the second function the indent level goes backwards in the middle.

ktfmt formats this as such, without the backward indentation:

package com.test

import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.B

public class HelloWorld {
  public fun hello1(): B.cccccccccccccccccccccccccccccccccccccccccccccccccc {
    myWhateverController.invokeWithTransactionFactory {
      longMethodCall(1, 2, 3) { "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
    }
  }

  public fun hello2(): B.cccccccccccccccccccccccccccccccccccccccccccccccccc =
    myWhateverController.invokeWithTransactionFactory {
      longMethodCall(1, 2, 3) { "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
    }
}

Expected behavior Single-expression function bodies should be wrapped nicely as well.

Additional context When creating a single-expression function results in a line long enough, it gets wrapped. That wrapping only applies to the long line, and then KotlinPoet continues with the original indent level of the function. But with idiomatic formatting, the wrapping should act more like opening and closing a control flow structure, with nested indentation.

JakeWharton commented 11 months ago

Thanks. This is tracked by #1300.