ktfmt icon indicating copy to clipboard operation
ktfmt copied to clipboard

>1 annotation is kept on same line, contrary to style guide

Open bethcutler opened this issue 4 years ago • 13 comments
trafficstars

Original code:

  @Inject @Foo
  var bar: Bar

ktfmt removes line break:

  @Inject @Foo var bar: Bar

Android Kotlin style guide says annotations can only be on the same line as the declaration if there is only annotation (and without arguments): https://developer.android.com/kotlin/style-guide#annotations

bethcutler avatar May 03 '21 22:05 bethcutler

This is also true for annotations with arguments:

  @Retention(SOURCE) @Target(FUNCTION, PROPERTY_SETTER, FIELD) annotation class Global

(See again the Android style guide, which suggests even a single annotation with an argument should be on a separate line.)

If this is the preferred ktfmt style, we might consider changing the Android style guide. But I think it's good to have a discussion about it at least.

bethcutler avatar Jun 04 '21 18:06 bethcutler

cc @strulovich

cgrushko avatar Jun 07 '21 21:06 cgrushko

If I recall correctly how it got to this, we had some issues with parameters. This looks very awkward when we break after annotations:

fun doIt(
  @Anno1 @Anno2 item1,
  @Anno3 @Anno4 item2) { ... }

Since it becomes:

fun doIt(
  @Anno1 @Anno2
  item1,
  @Anno3 @Anno4
  item2) { ... }

The second one looks very unnatural to me and I don't think people code this way. I'm open for updating the style, but then we need to split it according to the annotated element: field, variable, parameter, etc.

strulovich avatar Jun 08 '21 19:06 strulovich

I think the example parameters in the previous comment are kind of a best-case scenario for the existing behavior because things line up neatly: every parameter has annotations of exactly the same length. When that's not the case, such as when annotating controller methods or payload classes with an annotation-based web framework, I think it's less obvious that breaking more aggressively would make things look unnatural.

For example, ktfmt does this:

fun doIt(
    @Annotation1 @Annotation2("some arguments") item1: Int,
    item2: String,
    @Annotation3 @Annotation4 item3: Double,
    @Annotation5
    @Annotation6("some arguments like say a bit of API documentation that cause line wrapping")
    item4: Boolean,
    @Annotation1 item5: Int
) { ... }

as opposed to the proposed behavior in this issue, which would left-align the arguments:

fun doIt(
    @Annotation1
    @Annotation2("some arguments")
    item1: Int,
    item2: String,
    @Annotation3
    @Annotation4
    item3: Double,
    @Annotation5
    @Annotation6("some arguments like say a bit of API documentation that cause line wrapping")
    item4: Boolean,
    @Annotation1
    item5: Int
) { ... }

There's a workaround which I've taken to using in cases where I find the existing formatting especially hard to read. You get line breaks if you put a // comment after the last annotation, like so:

fun doIt(
    @Annotation1
    @Annotation2("some arguments") //
    item1: Int,
    item2: String,
    @Annotation3
    @Annotation4 //
    item3: Double
) { ... }

It's ugly but in some cases less ugly than the default.

sgrimm avatar Oct 12 '22 23:10 sgrimm

@strulovich FWIW, I prefer annotations broken out per-line for parameters as well (like @sgrimm). But I would not be opposed to leaving them inline if it'd mean getting annotations for functions consistently on the previous line.

In a recent comparison between ktfmt and kotlin-auto-formatter, this was the primary issue we found where ktfmt produced lower-quality code.

rattrayalex avatar Dec 22 '22 01:12 rattrayalex

is there any progress?

savl-2021 avatar Jun 29 '23 11:06 savl-2021

There's no active work on this.

@rattrayalex, I'd appreciate a few real examples of where it produces uglier code as reference.

strulovich avatar Jun 29 '23 13:06 strulovich

image

@strulovich

savl-2021 avatar Jun 29 '23 13:06 savl-2021

@strulovich I'm not developing in this area actively right now, but if you run ktfmt over this repo you should see a number of diffs related to annotations.

rattrayalex avatar Jun 29 '23 14:06 rattrayalex

@strulovich I've come across the following:

@PlanningEntity
class LoadJob {
    @PlanningId lateinit var id: String

    @InverseRelationShadowVariable(sourceVariableName = "tour") var vehicle: Vehicle? = null

    @PreviousElementShadowVariable(sourceVariableName = "tour") var previousLoadJob: LoadJob? = null

    @NextElementShadowVariable(sourceVariableName = "tour") var nextLoadJob: LoadJob? = null
}

which definitely reads worse than

@PlanningEntity
class LoadJob {
    @PlanningId
    lateinit var id: String

    @InverseRelationShadowVariable(sourceVariableName = "tour")
    var vehicle: Vehicle? = null

    @PreviousElementShadowVariable(sourceVariableName = "tour")
    var previousLoadJob: LoadJob? = null

    @NextElementShadowVariable(sourceVariableName = "tour")
    var nextLoadJob: LoadJob? = null
}

In this case I'd strongly consider using the workaround with trailing comments provided by @sgrimm

greyhairredbear avatar Jul 20 '23 21:07 greyhairredbear

Moved from ktlint to ktfmt and this was one of the major eyesores for me.
I feel like in-line for parameters is fine (readable) but in-line for top-level fields / methods should be 1-per line.

mobeigi avatar Nov 29 '23 15:11 mobeigi

We started using ktfmt, and this is the only thing that I don't like about it. Whenever there are multiple annotations, or annotations with parameters, or multiple annotated properties or parameters, I prefer annotations on their own lines. Since that's so many cases, just having them always on their own lines would be fine too.

Here's another related issue regarding annotations with parameters: https://github.com/facebook/ktfmt/issues/316 and https://github.com/facebook/ktfmt/issues/409

darioseidl avatar Jun 24 '24 17:06 darioseidl

Agreed on strongly preferring multiple annotations to be split out one per line. Simple, no-params, single annotations can be kept on the same line, but I'd personally also prefer them on a separate line if possible, for consistency and readability.

rock3r avatar Aug 15 '24 11:08 rock3r