diff --git a/README.md b/README.md index 55dbb2c32b..7e9539e02e 100644 --- a/README.md +++ b/README.md @@ -95,11 +95,11 @@ disabled_rules=no-wildcard-imports,experimental:annotation,my-custom-ruleset:my- # "*" - wildcard. There must be at least one entry of a single wildcard to match all other imports. Matches anything after a specified symbol/import as well. # "|" - blank line. Supports only single blank lines between imports. No blank line is allowed in the beginning or end of the layout. # "^" - alias import, e.g. "^android.*" will match all android alias imports, "^" will match all other alias imports. -# import paths - these can be full paths, e.g. "java.util.List" as well as wildcard paths, e.g. "kotlin.*" +# import paths - these can be full paths, e.g. "java.util.List.*" as well as wildcard paths, e.g. "kotlin.**" # Examples: kotlin_imports_layout=ascii # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines kotlin_imports_layout=idea # default IntelliJ IDEA style, same as "ascii", but with "java", "javax", "kotlin" and alias imports in the end of the imports list -kotlin_imports_layout=android.*,|,^org.junit.*,kotlin.io.Closeable,|,*,^ # custom imports layout +kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,**,^ # custom imports layout # Alternatively ij_kotlin_imports_layout name can be used, in order to set an imports layout for both ktlint and IDEA via a single property # Note: this is not yet implemented on IDEA side, so it only takes effect for ktlint ij_kotlin_imports_layout=* diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt index 42923c69aa..fcb058c711 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt @@ -25,12 +25,13 @@ import org.jetbrains.kotlin.psi.KtImportDirective * * custom - defined by the following set of tokens. Tokens can be combined together in a group, groups/tokens must be comma separated: * * "*" - wildcard symbol, can be used as follows: * 1. Single, meaning matching any import ( in IDEA) - * 2. After an import path, e.g. "java.*" or "kotlin.io.*" + * 2. After an import path, e.g. "java.*" or "kotlin.io.Closeable.*" + * 3. Doubled after an import path, e.g. "java.**" or "kotlin.io.**", meaning "with subpackages" * * "|" - blank line symbol. Only supported single blank lines between imports. Multiple blank lines will be ignored. Blank lines are not allowed outside of import list. * * "^" - alias symbol, can be used as follows: * 1. In front of an import path, meaning matching all alias imports from this path, e.g. "^android.*" * 2. Alone, meaning matching any alias import - "^" ( in IDEA) - * * import paths - these can be full paths, e.g. "java.util.List" as well as wildcard paths, e.g. "kotlin.*" + * * import paths - these can be full paths, e.g. "java.util.List.*" as well as wildcard paths meaning "with subpackages", e.g. "kotlin.**" * * In case the custom property is not provided, the rule defaults to "ascii" style in case of "android" flag supplied, or to "idea" otherwise. */ @@ -64,7 +65,7 @@ public class ImportOrderingRule : * * https://github.com/JetBrains/kotlin/blob/ffdab473e28d0d872136b910eb2e0f4beea2e19c/idea/formatter/src/org/jetbrains/kotlin/idea/core/formatter/KotlinCodeStyleSettings.java#L87-L91 */ - private val IDEA_PATTERN = parseImportsLayout("*,java.*,javax.*,kotlin.*,^") + private val IDEA_PATTERN = parseImportsLayout("*,java.**,javax.**,kotlin.**,^") private const val IDEA_ERROR_MESSAGE = "Imports must be ordered in lexicographic order without any empty lines in-between " + "with \"java\", \"javax\", \"kotlin\" and aliases in the end" diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportLayoutParser.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportLayoutParser.kt index 2f9beadd19..d49725a62b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportLayoutParser.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/internal/importordering/ImportLayoutParser.kt @@ -5,7 +5,7 @@ internal const val WILDCARD_CHAR = "*" internal const val ALIAS_CHAR = "^" /** - * Adopted from https://github.com/JetBrains/intellij-community/blob/70fd799e94246f2c0fe924763ed892765c0dff9a/java/java-impl/src/com/intellij/psi/codeStyle/JavaPackageEntryTableAccessor.java#L25 + * Adapted from https://github.com/JetBrains/intellij-kotlin/blob/73b5a484198f02518c9ece2fb453d27cead680fb/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinPackageEntryTableAccessor.kt#L27-L43 */ internal fun parseImportsLayout(importsLayout: String): List { val importsList = importsLayout.split(",").onEach { it.trim() } @@ -29,7 +29,8 @@ internal fun parseImportsLayout(importsLayout: String): List { import = import.substring(1).trim() hasAlias = true } - if (import.endsWith(WILDCARD_CHAR)) { + if (import.endsWith(WILDCARD_CHAR + WILDCARD_CHAR)) { // java.** + import = import.substringBeforeLast(WILDCARD_CHAR) withSubpackages = true } return@map if (import == WILDCARD_CHAR) { // * diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportLayoutParserTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportLayoutParserTest.kt index 1859bbdce4..154f777869 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportLayoutParserTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportLayoutParserTest.kt @@ -14,21 +14,22 @@ class ImportLayoutParserTest { @Test(expected = IllegalArgumentException::class) fun `pattern without single wildcard is not allowed`() { - parseImportsLayout("java.util.List") + parseImportsLayout("java.util.List.*") } @Test fun `parses correctly`() { val expected = listOf( - PatternEntry("android", withSubpackages = true, hasAlias = false), + PatternEntry("android.*", withSubpackages = true, hasAlias = false), PatternEntry.BLANK_LINE_ENTRY, - PatternEntry("org.junit", withSubpackages = true, hasAlias = false), + PatternEntry("org.junit.*", withSubpackages = true, hasAlias = false), PatternEntry.BLANK_LINE_ENTRY, - PatternEntry("android", withSubpackages = true, hasAlias = true), + PatternEntry("android.*", withSubpackages = true, hasAlias = true), PatternEntry.ALL_OTHER_IMPORTS_ENTRY, + PatternEntry("kotlin.io.Closeable.*", withSubpackages = false, hasAlias = false), PatternEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY ) - val actual = parseImportsLayout("android.*,|,org.junit.*,|,^android.*,*,^") + val actual = parseImportsLayout("android.**,|,org.junit.**,|,^android.**,*,kotlin.io.Closeable.*,^") assertThat(actual).isEqualTo(expected) } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleCustomTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleCustomTest.kt index d954367de0..ed3d1e74e5 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleCustomTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/importordering/ImportOrderingRuleCustomTest.kt @@ -146,7 +146,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "android.*,|,org.junit.*,|,net.*,|,org.*,|,java.*,|,com.*,|,javax.*,|,*" + "android.**,|,org.junit.**,|,net.**,|,org.**,|,java.**,|,com.**,|,javax.**,|,*" ) assertThat( @@ -195,7 +195,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "android.*,|,org.junit.*,|,net.*,|,org.*,|,java.*,|,com.*,|,javax.*,|,*" + "android.**,|,org.junit.**,|,net.**,|,org.**,|,java.**,|,com.**,|,javax.**,|,*" ) assertThat( @@ -220,7 +220,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "java.*,|,|,|,kotlin.*,*" + "java.**,|,|,|,kotlin.**,*" ) assertThat( @@ -257,7 +257,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "java.*,|,|,|,kotlin.*,*" + "java.**,|,|,|,kotlin.**,*" ) assertThat( @@ -282,7 +282,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "^kotlin.*,^android.*,android.*,|,*,^" + "^kotlin.**,^android.**,android.**,|,*,^" ) assertThat( @@ -316,7 +316,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "^kotlin.*,^android.*,android.*,|,^,*" + "^kotlin.**,^android.**,android.**,|,^,*" ) assertThat( @@ -340,7 +340,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "kotlin.io.Closeable,kotlin.*,*" + "kotlin.io.Closeable.*,kotlin.**,*" ) assertThat( @@ -374,7 +374,7 @@ class ImportOrderingRuleCustomTest { """.trimIndent() val testFile = writeCustomImportsOrderingConfig( - "kotlin.io.Closeable,kotlin.*,*" + "kotlin.io.Closeable.*,kotlin.**,*" ) assertThat(