pinterest / ktlint

An anti-bikeshedding Kotlin linter with built-in formatter

Home Page:https://pinterest.github.io/ktlint/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Alphabetical import ordering enabled by default is a problem in Android Studio/IntelliJ

dimsuz opened this issue · comments

In our project ktlint 0.34.0 now reports a lot of incorrect import ordering, although we use a default import layou of IDE. All our developers have "Optimize imports" enabled by default, but this still creates an order like the following (which seems to be default one of Kotlin IDE plugin):

import ru.example.a
import ru.example.b
import ru.example.c
import javax.inject.Inject

Notice that the last one comes in non alphabetical order.
The unfortunate thing is that this cannot be changed.

Import order editing is there only for Java CodeStyle, kotlin plugin seems not to provide it. Correct me if I'm wrong.

If you think that this is still something that should be default, then please close.

But currently I suspect that many Android Studio/IntelliJ users will be disabling this rule, otherwise they'll have an endless conflict between IDE autoformatter/ktlint.

@dimsuz as of 0.34.0 you can disable rules via the .editorconfig: https://github.com/pinterest/ktlint#custom-editorconfig-properties

So in this case it would be disabled_rules=import-ordering

Thanks for the hint, will do that! Just wanted to report this in case having it as a default is up for review.

Thanks for the report! Of course anything and everything is up for discussion; I will close this ticket for now but I will keep an eye out for feedback if this rule proves controversial.

You also can follow this issue from Kotlin side: https://youtrack.jetbrains.com/issue/KT-10974

Thanks, voted!

Adding a property to .editorconfig -- which itself is flagged by yet other linters as invalid -- in order to suppress this lint issue is rather funny. I believe this ticket should be reopened. As it stands, any project with at least a single IntelliJ user (for OpenSource projects I'd argue actually all projects are impacted) has to disable this rule.

Hi Jan,

Just to get more information, can you give an example of where the IntelliJ optimized imports ends up not in Alphabetical order? I understand it's frustrating to have ktlint disagree with IDEA, it's not clear to me whether the IDEA ordering is a bug or desired behavior. Thanks!

fyi: IDEA puts java/javax imports at the bottom, contrary to ktlint expectations.

Yep, I have provided an example of invalid order in the original post, that is where it did go wrong for us.

Thanks. I just added a comment onto https://youtrack.jetbrains.com/issue/KT-10974 to ask if putting all the java and javax imports at the bottom is a bug or desired behavior. There are a number of instances inside of ktlint rules where we do things to explicitly match IntelliJ's formatting, but if this is a bug, I'm not sure I want to build the same bug into ktlint.

It does invalidate the claim on the ktlint README that...

...it makes Intellij IDEA's built-in formatter produce 100% ktlint-compatible code.

If this is 'expected behavior', calling the cli suggested ktlint --apply-to-idea-project becomes mostly useless since a 98% match still means going to the terminal to do ktlint -F, as no one is going alphabetize imports manually (as opposed to other non-automatically fixed issues which can be fixed by user intervention with moderate effort).

It does invalidate the claim on the ktlint README that...

...it makes Intellij IDEA's built-in formatter produce 100% ktlint-compatible code.

If this is 'expected behavior', calling the cli suggested ktlint --apply-to-idea-project becomes mostly useless since a 98% match still means going to the terminal to do ktlint -F, as no one is going alphabetize imports manually (as opposed to other non-automatically fixed issues which can be fixed by user intervention with moderate effort).

Sure. I understand your frustration, I just want to make sure the IDEA Optimize Imports ordering is not going to change before mimicing the same behavior in ktlint.

Just want to add that if the rule is changed it shouldn't be changed for --android:

Import statements for classes, functions, and properties are grouped together in a single list and ASCII sorted.

https://developer.android.com/kotlin/style-guide#structure

For what it is worth, the Google Java style guide used to have the java, javax, and some others separate from library imports. Now (I don't remember when it changed), they just have them grouped by non-static and static. From https://google.github.io/styleguide/javaguide.html#s3.3.3-import-ordering-and-spacing:

Imports are ordered as follows:

  1. All static imports in a single block.
  2. All non-static imports in a single block.

If there are both static and non-static imports, a single blank line separates the two blocks. There are no other blank lines between import statements.

Within each block the imported names appear in ASCII sort order. (Note: this is not the same as the import statements being in ASCII sort order, since '.' sorts before ';'.)

Which the Google Checkstyle stylesheet also uses.

I don't see import order mentioned on https://kotlinlang.org/docs/reference/coding-conventions.html

And Google's Kotlin style guide (not yet public) also matches Java and the Android Kotlin guide in that regard.

Just want to add that if the rule is changed it shouldn't be changed for --android:

Import statements for classes, functions, and properties are grouped together in a single list and ASCII sorted.

https://developer.android.com/kotlin/style-guide#structure

One thing we're also considering is breaking the android-specific rules into a different ruleset rather than using a flag.

It does invalidate the claim on the ktlint README that...

...it makes Intellij IDEA's built-in formatter produce 100% ktlint-compatible code.

If this is 'expected behavior', calling the cli suggested ktlint --apply-to-idea-project becomes mostly useless since a 98% match still means going to the terminal to do ktlint -F, as no one is going alphabetize imports manually (as opposed to other non-automatically fixed issues which can be fixed by user intervention with moderate effort).

Sure. I understand your frustration, I just want to make sure the IDEA Optimize Imports ordering is not going to change before mimicing the same behavior in ktlint.

That bug was open in 2016 it looks like, not sure it is going to be resolved quickly. This issue caused quite a bit of headache for us today. Is there any reason not to keep inline with Intellij until if/when it changes?

Our whole backend team has this problem, so it would really be great if we could turn off this inspection by ktlint by some configuration option (at the very least) :)

@VaclavDedik check this comment to the way how to disable it: #527 (comment)

We definitely have this issue too.

How painful would it be to add a configuration option for ktlint to handle this case? Even if we have to remove it later once IntelliJ sorts this out.

@greenkiwi There's a way to disable this check, see the first comment above.

@dimsuz - We've disabled the check. It would be great to have the option to have it match the IntelliJ order. i.e. still check, just with java/javax at the end.

Any further changes on this? Seems like the kotlin.* imports are being put at the bottom as well.

The following code block will cause ktlint to report incorrect order

import kotlinx.coroutines.CoroutineDispatcher
import kotlin.coroutines.CoroutineContext

object DirectDispatcher : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        block.run()
    }
}

This seems to be the default IJ import ordering for koltin and it mismatches ktlint.

Official Kotlin code style also does not specify anything around import ordering

@tasomaniac while that might be true, IJ and Android Studio do enforce ordering, so we should match that, otherwise this becomes a pain point for everyone using these IDEs and ktlint.

I'm surprised Pinterest doesn't use Android Studio. I imagine the percentage of Kotlin developers not using IJ or Android Studio is pretty low, the rest of us are all impacted by this bug.

The Kotlin lang Coding Conventions don't mention anything about import ordering.
The Android Kotlin style guide does mention import ordering: ASCII sorted.

It is merely because Kotlin usage is large on the Android platform that many ktlint users will probably work in Android Studio and therefore they are limited by this ktlint rule, or have to disable it.

@tasomaniac while that might be true, IJ and Android Studio do enforce ordering, so we should match that, otherwise this becomes a pain point for everyone using these IDEs and ktlint.

If ktlint wants to work for everyone (i.e. be IDE, tooling and style guide agnostic), then it should not be opinionated about import ordering, because as said before neither any import order is officially standard, nor do all IDEs allow configuration of Kotlin import ordering rules. But that's fundamentally against ktlint: ktlint is about being opinionated about Kotlin code style. So I think there are (at least) two alternatives:

  1. Do nothing, keep this as it is: let many users disable rules that don't work for them.
  2. Provide custom ktlint rules for IDEs (e.g. IJ and AS), platforms (e.g. Android) and official style guides. That way ktlint can still be opinionated about its users' Kotlin style, within the limits of the users' choice of IDE, platform and style guide.

@erikhuizinga I would personally go after second point - for me main advantage of ktlint is that it runs out of the box without tedious configuration or tunning.

I use kotlin but not on Android and I don't really care about Android Kotlin style guide.

I use kotlin but not on Android and I don't really care about Android Kotlin style guide.

I use Kotlin but only on Android, so I only care about the Android Kotlin style guide. So I'm in the other camp. But we're on the same page: we both want it to work out of the box. I think this is enough reason to at least consider supporting both camps with as little config possible out of the box.

I understand desire to keep configuration as simple as possible, but it doesn't really help with adoption of ktlint.
Why don't have current behavior as default one (to keep it compatible with previous versions) and provide other options for people that want to use them?

Please keep in mind that ktlint already has the android flag which changes the behavior of some rules. When the Android flag is not enabled, it is supposed to follow the official Kotlin style instead of the Android one. That's what I understood.

I think there are two ways to proceed here:
a) Allow ktlint customization so its ImportOrderingRule sorts exactly like IDEA does. For instance, add a property like kotlin_version=1.3.50, so it matches the default Kotlin implementation which is https://github.com/JetBrains/kotlin/blob/d765bebcfc5f741d560e96bf1ffe3dc668f57df0/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ImportsUtils.kt#L37-L58
Of course, Kotlin implementation might change over time, that is why I suggest to add property like kotlin_version rather than a configuration "which packages to sort top or down"

b) Implement a custom rule (outside of ktlint) which is basically a copy-paste of the current Ktlint rule + IDEA-like sorter.

Frankly speaking, I would prefer a) (==embedded to Ktlint) since it would enable all ktlint users to use that transparently.

@tasomaniac , @shashachu , what do you think?

PS. It does bother me, because my current approach is disabling the rule :(

You can also disable Android Studios optimize imports.

There is two way to do that.
Run the action Show Reformat File Dialog and uncheck optimize imports

image

Otherwise, you can edit the $HOME/Library/Preferences/AndroidStudio3.5/options/other.xml and set LayoutCode.optimizeImports to false. Major drawback is this is per developer/Android-Studio version

Here is sed if that helps anyone at all.

sed -i '' \
  's/\<property name\="LayoutCode\.optimizeImports" value\="true" \/\>/\<property name\="LayoutCode\.optimizeImports" value\="false" \/\>/g' \
  "$HOME/Library/Preferences/AndroidStudio3.5/options/other.xml"

@marukami does that disable more than just ordering? Other import optimisations that come to mind are grouping/ungrouping of imports and replacing star imports with direct imports. Does this setting en-/disable that too?

We will get to a year soon with the same issue, a lot of projects having to disable the rule manually and still not removing it from ktlint because...?
I think there is absolutely no excuse not to drop this rule or make it opt-in instead of opt-out, as it still invalidates the sentence:

...it makes Intellij IDEA's built-in formatter produce 100% ktlint-compatible code.

I agree. It also invalidates the fact that ktlint was "anti-bikeshedding"

There's a PR opened in IDEA that allows defining Import Layout for Kotlin (similar to Java) - JetBrains/kotlin#3336
Hopefully when it lands, this issue can finally be resolved.

When that pr land in kotlin 1.4, it means kotlin code style wont force anyone for alphabetical order and let project specific configurations.
That means, ktlint alphabetical order become obsolete. This rule can be removed or it can start supporting same order as Idea formatter per project.

So, it's very annoying today, and it will still be annoying later.
Is the kind of rule that everyone hates, but someone is really reluctant to drop...
A shame.

I don't see how this rule would annoy anyone anymore, once the import order can be configured in IntelliJ. ktlint --apply-to-idea-project just needs to configure it correctly and after that the auto formatter and ktlint are in agreement again. And until the fix is ready on both sides, the rule can be ignored.

A linting rule for imports is good in general, since it prevents weird diffs. So I would not like to see this rule being dropped completely. And the most basic, out-of-the-box rule for import ordering is definitely the alphabetical one.

[Opinion ahead]
Also, I would argue that if this rule clashes with an existing code style, then it is not the end of the world not to comply with that code style guide, as far as import ordering is concerned. Import ordering rules are primarily there to prevent weird diffs from happening. The few cases where one actually needs to read the import section of a file are not significantly improved, because the java.** and kotlin.** blocks are separated. Any import ordering rule would do, as long as everyone in the project uses it. And if any rule will do, then the simplest one is a very good choice.

Hi all, thanks for the feedback. @romtsn is working on a diff to address this via configs (default will be to match the IDEA ordering) and we will target the 0.37.0 release, which is scheduled to go out next week. Appreciate your patience.

In the meantime, for those using jlleitschuh gradle plugin:

apply plugin: "org.jlleitschuh.gradle.ktlint"

ktlint {
    version = "0.36.0"
    android = true
    // See https://github.com/pinterest/ktlint/issues/527
    disabledRules = ["import-ordering"]
}

For those watching at home, this is also fixed on the IDEA side in Kotlin plugin 1.4 - JetBrains/kotlin#3336

I guess this is related, but if it's not, I can create a new issue for it.

When using ktlint 0.40.0, it tries to move my kotlinx.* imports to above both java.* and kotlin.* imports, which looks really weird to me. This is the diff it generated by running ktlint -F:

@@ -15,14 +15,14 @@ import android.service.notification.StatusBarNotification
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
 import androidx.core.content.edit
-import java.security.SecureRandom
-import java.util.concurrent.locks.ReentrantLock
-import kotlin.concurrent.withLock
-import kotlin.text.Regex
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
+import java.security.SecureRandom
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+import kotlin.text.Regex

I'm not sure if this is intentional (to match intellij behavior, for example), or a bug.

@fishy Yes, this is intentional as we are trying to match intellij default behavior, where java, javax and kotlin imports are coming after all others.

@fishy Yes, this is intentional as we are trying to match intellij default behavior, where java, javax and kotlin imports are coming after all others.

Thanks! But maybe kotlinx.* should also be part of that exception list to be put at the bottom? (consider we also have javax.* in the group)

oh never mind just saw that intellij also doesn't add kotlinx there.

I just found out with the "Import Layout" settings in IDEA/AS, you can simply enforce lexicographical import-ordering by removing all entries from "Import Layout" settings for Kotlin aside from "import all other imports" and disable the option "Import aliases separately".

Then IDEA/AS orders imports just the same as ktlint does by default with --android flag (tested with the unofficial ktlint-plugin in AS, which includes ktlint 0.42.1 atm according to changelog).

Although my settings look like this in Preferences / Editor / Code Style / Kotlin / Imports, Android Studio still puts aliased imports first which is a flagged by the import-ordering rule.

Screenshot 2022-09-16 at 12 05 11

I'm unable to get AS to organize imports the way ktlint expects it.

Android Studio Dolphin 2021.3.1
ktlint 0.47.1

Ktlint 1.0.1

file .editorconfig

[*.{kt,kts}]
ktlint_standard_import-ordering = disabled