From 360e2c8a55804e3029f9f78482f5f6e5f445bfbc Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Fri, 29 Dec 2023 21:26:27 +0100 Subject: [PATCH 01/17] Fix operator spacing If an operation reference contains an identifier, then it is used in a DSL notation in which the identifier should be separated with a from a parenthesized expression to avoid confusion with a function call. E.g. format as `every { foo() } returns (bar) andThen (baz)` Offsets for operators having length 2 or more, and not followed by single space were incorrect as length of the operator was not taken into account. Closes #2459 --- .../rules/SpacingAroundOperatorsRule.kt | 91 ++++++++++++++----- .../rules/SpacingAroundOperatorsRuleTest.kt | 39 +++++++- 2 files changed, 100 insertions(+), 30 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt index feacfc439d..87ddecddf4 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt @@ -12,6 +12,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.EXCLEQ import com.pinterest.ktlint.rule.engine.core.api.ElementType.EXCLEQEQEQ import com.pinterest.ktlint.rule.engine.core.api.ElementType.GT import com.pinterest.ktlint.rule.engine.core.api.ElementType.GTEQ +import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER import com.pinterest.ktlint.rule.engine.core.api.ElementType.LT import com.pinterest.ktlint.rule.engine.core.api.ElementType.LTEQ import com.pinterest.ktlint.rule.engine.core.api.ElementType.MINUS @@ -42,31 +43,42 @@ import org.jetbrains.kotlin.psi.KtPrefixExpression @SinceKtlint("0.1", STABLE) public class SpacingAroundOperatorsRule : StandardRule("op-spacing") { - private val tokenSet = - TokenSet.create( - MUL, PLUS, MINUS, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ, - EXCLEQ, ANDAND, OROR, ELVIS, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, ARROW, - ) - override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { - if (tokenSet.contains(node.elementType) && - node.isNotUnaryOperator() && - isNotSpreadOperator(node) && - isNotImport(node) + if (node.isUnaryOperator()) { + // Allow: + // val foo = -1 + return + } + + if (node.isSpreadOperator()) { + // Allow: + // foo(*array) + return + } + + if (node.isImport()) { + // Allow: + // import * + return + } + + if ((node.elementType == LT || node.elementType == GT || node.elementType == MUL) && + node.treeParent.elementType != OPERATION_REFERENCE + ) { + // Allow: + // fun foo(...) + // class Foo { ... } + // Foo<*> + return + } + + if (node.elementType in OPERATORS || + (node.elementType == IDENTIFIER && node.treeParent.elementType == OPERATION_REFERENCE) ) { - if ((node.elementType == LT || node.elementType == GT || node.elementType == MUL) && - node.treeParent.elementType != OPERATION_REFERENCE - ) { - // Do not format parameter types like: - // fun foo(...) - // class Foo { ... } - // Foo<*> - return - } val spacingBefore = node.prevLeaf() is PsiWhiteSpace val spacingAfter = node.nextLeaf() is PsiWhiteSpace when { @@ -84,7 +96,7 @@ public class SpacingAroundOperatorsRule : StandardRule("op-spacing") { } } !spacingAfter -> { - emit(node.startOffset + 1, "Missing spacing after \"${node.text}\"", true) + emit(node.startOffset + node.textLength, "Missing spacing after \"${node.text}\"", true) if (autoCorrect) { node.upsertWhitespaceAfterMe(" ") } @@ -93,15 +105,44 @@ public class SpacingAroundOperatorsRule : StandardRule("op-spacing") { } } - private fun ASTNode.isNotUnaryOperator() = !isPartOf(KtPrefixExpression::class) + private fun ASTNode.isUnaryOperator() = isPartOf(KtPrefixExpression::class) - private fun isNotSpreadOperator(node: ASTNode) = + private fun ASTNode.isSpreadOperator() = // fn(*array) - !(node.elementType == MUL && node.treeParent.elementType == VALUE_ARGUMENT) + elementType == MUL && treeParent.elementType == VALUE_ARGUMENT - private fun isNotImport(node: ASTNode) = + private fun ASTNode.isImport() = // import * - !node.isPartOf(KtImportDirective::class) + isPartOf(KtImportDirective::class) + + private companion object { + private val OPERATORS = + TokenSet.create( + ANDAND, + ARROW, + DIV, + DIVEQ, + ELVIS, + EQ, + EQEQ, + EQEQEQ, + EXCLEQ, + EXCLEQEQEQ, + GT, + GTEQ, + LT, + LTEQ, + MINUS, + MINUSEQ, + MUL, + MULTEQ, + OROR, + PERC, + PERCEQ, + PLUS, + PLUSEQ, + ) + } } public val SPACING_AROUND_OPERATORS_RULE_ID: RuleId = SpacingAroundOperatorsRule().ruleId diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRuleTest.kt index fac21c9895..86da5807bc 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRuleTest.kt @@ -47,7 +47,7 @@ class SpacingAroundOperatorsRuleTest { .hasLintViolations( LintViolation(1, 13, "Missing spacing around \"${operator}\""), LintViolation(2, 13, "Missing spacing before \"${operator}\""), - LintViolation(3, 15, "Missing spacing after \"${operator}\""), + LintViolation(3, 14 + operator.length, "Missing spacing after \"${operator}\""), ).isFormattedAs(formattedCode) } @@ -248,10 +248,10 @@ class SpacingAroundOperatorsRuleTest { """.trimIndent() spacingAroundOperatorsRuleAssertThat(code) .hasLintViolations( - LintViolation(3, 8, "Missing spacing after \"+=\""), - LintViolation(4, 8, "Missing spacing after \"-=\""), - LintViolation(5, 8, "Missing spacing after \"/=\""), - LintViolation(6, 8, "Missing spacing after \"*=\""), + LintViolation(3, 9, "Missing spacing after \"+=\""), + LintViolation(4, 9, "Missing spacing after \"-=\""), + LintViolation(5, 9, "Missing spacing after \"/=\""), + LintViolation(6, 9, "Missing spacing after \"*=\""), ).isFormattedAs(formattedCode) } } @@ -285,4 +285,33 @@ class SpacingAroundOperatorsRuleTest { """.trimIndent() spacingAroundOperatorsRuleAssertThat(code).hasNoLintViolations() } + + @Test + fun `Given a custom DSL`() { + val code = + """ + fun foo() { + every { foo() }returns(bar)andThen(baz) + every { foo() }returns (bar)andThen (baz) + every { foo() } returns(bar) andThen(baz) + } + """.trimIndent() + val formattedCode = + """ + fun foo() { + every { foo() } returns (bar) andThen (baz) + every { foo() } returns (bar) andThen (baz) + every { foo() } returns (bar) andThen (baz) + } + """.trimIndent() + spacingAroundOperatorsRuleAssertThat(code) + .hasLintViolations( + LintViolation(2, 20, "Missing spacing around \"returns\""), + LintViolation(2, 32, "Missing spacing around \"andThen\""), + LintViolation(3, 20, "Missing spacing before \"returns\""), + LintViolation(3, 33, "Missing spacing before \"andThen\""), + LintViolation(4, 28, "Missing spacing after \"returns\""), + LintViolation(4, 41, "Missing spacing after \"andThen\""), + ).isFormattedAs(formattedCode) + } } From 469587570a4e3b88db29b8d0b9e25af75e346989 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Tue, 2 Jan 2024 17:53:19 +0100 Subject: [PATCH 02/17] Prepare 1.2.0-SNAPSHOT (#2484) --- CHANGELOG.md | 10 ++++++++++ gradle.properties | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56ba883ed3..12ac9fa6c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## [Unreleased] + +### Added + +### Removed + +### Fixed + +### Changed + ## [1.1.0] - 2023-12-19 ### 🆕 Features diff --git a/gradle.properties b/gradle.properties index 2278ef0ef0..b34aebc2eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.1.0 +VERSION_NAME=1.2.0-SNAPSHOT POM_GROUP_ID=com.pinterest.ktlint POM_DESCRIPTION=An anti-bikeshedding Kotlin linter with built-in formatter. From c3d1998a37a774e19e2d6dabb4b8a63ae4002258 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 31 Dec 2023 23:57:30 +0800 Subject: [PATCH 03/17] Simplify BOM exclude list --- ktlint-bom/build.gradle.kts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ktlint-bom/build.gradle.kts b/ktlint-bom/build.gradle.kts index 904b6a7b70..68eb6640ff 100644 --- a/ktlint-bom/build.gradle.kts +++ b/ktlint-bom/build.gradle.kts @@ -7,16 +7,13 @@ publishing.publications.named("maven") { from(components["javaPlatform"]) } -val internalNonPublishableProjects: Set by rootProject.extra -val excludeList = internalNonPublishableProjects + "ktlint-test-ruleset-provider-v2-deprecated" - dependencies { logger.lifecycle("Creating dependencies for ktlint-bom") constraints { project.rootProject.subprojects.forEach { subproject -> - if (subproject.name in excludeList) { - logger.lifecycle(" - Ignore dependency '${subproject.name}' and do not add to ktlint-bom") - } else { + subproject.plugins.withId("ktlint-publication") { + // Exclude self project from BOM. + if (subproject == project) return@withId logger.lifecycle(" + Add api dependency on '${subproject.name}' to ktlint-bom") api(subproject) } From 7619ae4b6edd707aed03f2cac43fb392bbafb1dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 20:02:40 +0100 Subject: [PATCH 04/17] fix(deps): update dependency dev.drewhamilton.poko:poko-gradle-plugin to v0.15.2 (#2485) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c3ca43e1c1..09f55376bd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ ec4j = "org.ec4j.core:ec4j-core:0.3.0" picocli = "info.picocli:picocli:4.7.5" logging = "io.github.oshai:kotlin-logging-jvm:5.1.4" slf4j = "org.slf4j:slf4j-simple:2.0.10" -poko = "dev.drewhamilton.poko:poko-gradle-plugin:0.15.1" +poko = "dev.drewhamilton.poko:poko-gradle-plugin:0.15.2" # Use logback-classic as the logger for kotlin-logging / slf4j as it allow changing the log level at runtime. # TODO: Update "renovate.json" once logback-classic is updated to 1.4 (once java8 support for ktlint is dropped) logback = "ch.qos.logback:logback-classic:1.3.14" From 80bdd1eae57663364c1b6e1cd72abfd2315c4ca8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 20:03:30 +0100 Subject: [PATCH 05/17] fix(deps): update dependency org.assertj:assertj-core to v3.25.1 (#2486) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 09f55376bd..952b87dea6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,6 +31,6 @@ logcaptor = "io.github.hakky54:logcaptor:2.9.2" janino = "org.codehaus.janino:janino:3.1.11" # Testing libraries junit5 = "org.junit.jupiter:junit-jupiter:5.10.1" -assertj = "org.assertj:assertj-core:3.25.0" +assertj = "org.assertj:assertj-core:3.25.1" sarif4k = "io.github.detekt.sarif4k:sarif4k:0.5.0" jimfs = "com.google.jimfs:jimfs:1.3.0" From bc4b1802405504fe8659eed183eb79e6e5872544 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Thu, 4 Jan 2024 17:56:16 +0100 Subject: [PATCH 06/17] Improve wrapping of binary expressions (#2479) * Wrapping a binary expression takes precedence on wrapping value argument list - Remove dependency from `binary-expression-wrapping` on `value-argument-wrapping` rules - If a binary expression is used as value argument, and it causes the max line length to be exceeded, then wrap the value argument. - If the left hand side of the binary expression is call expression with a lambda argument, then wrapping the lambda argument has priority over wrapping the other arguments of the call expression. - If the binary expression contains an elvis operator, and does not fit on a single line, than the expression is wrapped before the elvis operator. Closes #2460 --- .../rules/ArgumentListWrappingRule.kt | 6 +- .../rules/BinaryExpressionWrappingRule.kt | 167 ++++++++++------ .../rules/ArgumentListWrappingRuleTest.kt | 77 +++++++- .../rules/BinaryExpressionWrappingRuleTest.kt | 178 ++++++++++++++---- 4 files changed, 330 insertions(+), 98 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt index fec961d5da..faf785c462 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt @@ -22,7 +22,6 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPER import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.indent -import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -111,10 +110,7 @@ public class ArgumentListWrappingRule : // skip lambda arguments node.treeParent?.elementType != FUNCTION_LITERAL && // skip if number of arguments is big (we assume it with a magic number of 8) - node.children().count { it.elementType == VALUE_ARGUMENT } <= 8 && - // skip if part of a value argument list. It depends on the situation whether it is better to wrap the arguments in the list - // or the operators in the binary expression - !node.isPartOf(BINARY_EXPRESSION) + node.children().count { it.elementType == VALUE_ARGUMENT } <= 8 ) { // each argument should be on a separate line if // - at least one of the arguments is diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt index dfb2c362c6..204d9e59c6 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt @@ -2,35 +2,42 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION +import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONDITION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELVIS import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN +import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_LITERAL import com.pinterest.ktlint.rule.engine.core.api.ElementType.LAMBDA_ARGUMENT +import com.pinterest.ktlint.rule.engine.core.api.ElementType.LAMBDA_EXPRESSION +import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE import com.pinterest.ktlint.rule.engine.core.api.ElementType.LONG_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPERATION_REFERENCE import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY -import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT_LIST +import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE +import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT import com.pinterest.ktlint.rule.engine.core.api.IndentConfig import com.pinterest.ktlint.rule.engine.core.api.Rule -import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule -import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule.Mode.REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL +import com.pinterest.ktlint.rule.engine.core.api.children import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf -import com.pinterest.ktlint.rule.engine.core.api.indent +import com.pinterest.ktlint.rule.engine.core.api.isCodeLeaf +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline +import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.leavesOnLine -import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling +import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.noNewLineInClosedRange import com.pinterest.ktlint.rule.engine.core.api.parent import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.prevSibling +import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -50,10 +57,6 @@ public class BinaryExpressionWrappingRule : INDENT_STYLE_PROPERTY, MAX_LINE_LENGTH_PROPERTY, ), - visitorModifiers = - setOf( - RunAfterRule(ARGUMENT_LIST_WRAPPING_RULE_ID, REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED), - ), ), Rule.Experimental { private var indentConfig = IndentConfig.DEFAULT_INDENT_CONFIG @@ -74,11 +77,11 @@ public class BinaryExpressionWrappingRule : emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { when (node.elementType) { - BINARY_EXPRESSION -> visitExpression(node, emit, autoCorrect) + BINARY_EXPRESSION -> visitBinaryExpression(node, emit, autoCorrect) } } - private fun visitExpression( + private fun visitBinaryExpression( node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, autoCorrect: Boolean, @@ -101,15 +104,87 @@ public class BinaryExpressionWrappingRule : true, ) if (autoCorrect) { - expression.upsertWhitespaceBeforeMe(expression.indent().plus(indentConfig.indent)) + expression.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(expression)) + } + } + + node + .takeIf { it.treeParent.elementType == VALUE_ARGUMENT } + ?.takeIf { it.causesMaxLineLengthToBeExceeded() } + ?.let { expression -> + emit( + expression.startOffset, + "Line is exceeding max line length. Break line before expression", + true, + ) + if (autoCorrect) { + expression.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(expression)) } } + // When left hand side is a call expression which causes the max line length to be exceeded then first wrap that expression + node + .children() + .firstOrNull { !it.isCodeLeaf() } + ?.takeIf { it.elementType == CALL_EXPRESSION } + ?.takeIf { it.causesMaxLineLengthToBeExceeded() } + ?.let { callExpression -> visitCallExpression(callExpression, emit, autoCorrect) } + + // The remainder (operation reference plus right hand side) might still cause the max line length to be exceeded node - .findChildByType(OPERATION_REFERENCE) + .takeIf { node.lastChildNode.causesMaxLineLengthToBeExceeded() || node.isPartOfConditionExceedingMaxLineLength() } + ?.findChildByType(OPERATION_REFERENCE) ?.let { operationReference -> visitOperationReference(operationReference, emit, autoCorrect) } } + private fun ASTNode.isPartOfConditionExceedingMaxLineLength() = + // Checks that when binary expression itself fits on the line, but the closing parenthesis or opening brace does not fit. + // // Suppose that X is the last possible character on the + // // line X + // if (leftHandSideExpression && rightHandSideExpression) { + treeParent + .takeIf { it.elementType == CONDITION } + ?.lastChildLeafOrSelf() + ?.nextLeaf { it.isWhiteSpaceWithNewline() } + ?.prevLeaf() + ?.causesMaxLineLengthToBeExceeded() + ?: false + + private fun visitCallExpression( + node: ASTNode, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + autoCorrect: Boolean, + ) { + node + .takeIf { it.elementType == CALL_EXPRESSION } + ?.takeIf { it.treeParent.elementType == BINARY_EXPRESSION } + ?.let { callExpression -> + // Breaking the lambda expression has priority over breaking value arguments + callExpression + .findChildByType(LAMBDA_ARGUMENT) + ?.findChildByType(LAMBDA_EXPRESSION) + ?.findChildByType(FUNCTION_LITERAL) + ?.let { functionLiteral -> + functionLiteral + .findChildByType(LBRACE) + ?.let { lbrace -> + emit(lbrace.startOffset + 1, "Newline expected after '{'", true) + if (autoCorrect) { + lbrace.upsertWhitespaceAfterMe(indentConfig.childIndentOf(lbrace.treeParent)) + } + } + functionLiteral + .findChildByType(RBRACE) + ?.let { rbrace -> + emit(rbrace.startOffset, "Newline expected before '}'", true) + if (autoCorrect) { + rbrace.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node.treeParent)) + } + } + } + } + } + private fun visitOperationReference( node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, @@ -117,68 +192,54 @@ public class BinaryExpressionWrappingRule : ) { node .takeIf { it.elementType == OPERATION_REFERENCE } - ?.takeIf { it.treeParent.elementType == BINARY_EXPRESSION } + ?.takeUnless { + // Allow: + // val foo = "string too long to fit on the line" + + // "more text" + it.nextSibling().isWhiteSpaceWithNewline() + }?.takeIf { it.treeParent.elementType == BINARY_EXPRESSION } ?.takeIf { binaryExpression -> // Ignore binary expression inside raw string literals. Raw string literals are allowed to exceed max-line-length. Wrapping // (each) binary expression inside such a literal seems to create more chaos than it resolves. binaryExpression.parent { it.elementType == LONG_STRING_TEMPLATE_ENTRY } == null - }?.takeIf { it.isOnLineExceedingMaxLineLength() } - ?.let { operationReference -> - if (node.isCallExpressionFollowedByLambdaArgument() || cannotBeWrappedAtOperationReference(operationReference)) { - // Wrapping after operation reference might not be the best place in case of a call expression or just won't work as - // the left hand side still does not fit on a single line - val offset = - operationReference - .prevLeaf { it.isWhiteSpaceWithNewline() } - ?.let { previousNewlineNode -> - previousNewlineNode.startOffset + - previousNewlineNode.text.indexOfLast { it == '\n' } + - 1 + }?.let { operationReference -> + if (operationReference.firstChildNode.elementType == ELVIS) { + operationReference + .prevLeaf { it.isWhiteSpace() } + .takeUnless { it.isWhiteSpaceWithNewline() } + ?.let { + // Wrapping after the elvis operator leads to violating the 'chain-wrapping' rule, so it must wrapped itself + emit(operationReference.startOffset, "Line is exceeding max line length. Break line before '?:'", true) + if (autoCorrect) { + operationReference.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(operationReference)) } - ?: operationReference.startOffset - emit(offset, "Line is exceeding max line length", false) + } } else { operationReference .nextSibling() ?.let { nextSibling -> emit( nextSibling.startOffset, - "Line is exceeding max line length. Break line after operator in binary expression", + "Line is exceeding max line length. Break line after '${operationReference.text}' in binary expression", true, ) if (autoCorrect) { - nextSibling.upsertWhitespaceBeforeMe(operationReference.indent().plus(indentConfig.indent)) + nextSibling.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(operationReference)) } } } } } - private fun ASTNode.isCallExpressionFollowedByLambdaArgument() = - parent { it.elementType == VALUE_ARGUMENT_LIST } - ?.takeIf { it.treeParent.elementType == CALL_EXPRESSION } - ?.nextCodeSibling() - .let { it?.elementType == LAMBDA_ARGUMENT } - - private fun cannotBeWrappedAtOperationReference(operationReference: ASTNode) = - if (operationReference.firstChildNode.elementType == ELVIS) { - true - } else { - operationReference - .takeUnless { it.nextCodeSibling()?.elementType == BINARY_EXPRESSION } - ?.let { - val stopAtOperationReferenceLeaf = operationReference.firstChildLeafOrSelf() - maxLineLength <= - it - .leavesOnLine() - .takeWhile { leaf -> leaf != stopAtOperationReferenceLeaf } - .lengthWithoutNewlinePrefix() - } - ?: false - } - private fun ASTNode.isOnLineExceedingMaxLineLength() = leavesOnLine().lengthWithoutNewlinePrefix() > maxLineLength + private fun ASTNode.causesMaxLineLengthToBeExceeded() = + lastChildLeafOrSelf().let { lastChildLeaf -> + leavesOnLine() + .takeWhile { it.prevLeaf() != lastChildLeaf } + .lengthWithoutNewlinePrefix() + } > maxLineLength + private fun Sequence.lengthWithoutNewlinePrefix() = joinToString(separator = "") { it.text } .dropWhile { it == '\n' } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRuleTest.kt index 52744a7e6c..02106b257e 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRuleTest.kt @@ -843,22 +843,46 @@ class ArgumentListWrappingRuleTest { } @Test - fun `Given a property assignment with a binary expression for which the left hand side operator is a function call`() { + fun `Given a property assignment with a binary expression for which the left hand side operator is a function call then binary expression wrapping takes precedence`() { val code = """ // $MAX_LINE_LENGTH_MARKER $EOL_CHAR val foo1 = foobar(foo * bar) + "foo" - val foo2 = foobar("foo", foo * bar) + "foo" - val foo3 = foobar("fooo", foo * bar) + "foo" + val foo2 = foobar(foo * bar) + "fooo" + val foo3 = foobar("fooooooo", bar) + "foo" + val foo4 = foobar("foooooooo", bar) + "foo" + val foo5 = foobar("fooooooooo", bar) + "foo" + val foo6 = foobar("foooo", foo * bar) + "foo" + val foo7 = foobar("fooooooooooo", foo * bar) + "foo" + """.trimIndent() + val formattedCode = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + val foo1 = foobar(foo * bar) + "foo" + val foo2 = + foobar(foo * bar) + "fooo" + val foo3 = + foobar("fooooooo", bar) + "foo" + val foo4 = + foobar("foooooooo", bar) + "foo" + val foo5 = + foobar("fooooooooo", bar) + + "foo" + val foo6 = + foobar("foooo", foo * bar) + + "foo" + val foo7 = + foobar( + "fooooooooooo", + foo * bar + ) + + "foo" """.trimIndent() argumentListWrappingRuleAssertThat(code) .setMaxLineLength() .addAdditionalRuleProvider { BinaryExpressionWrappingRule() } .addAdditionalRuleProvider { WrappingRule() } - // Although the argument-list-wrapping runs before binary-expression-wrapping, it may not wrap the argument values of a - // function call in case that call is part of a binary expression. It might be better to break the line at the operation - // reference instead. - .hasNoLintViolationsExceptInAdditionalRules() + .isFormattedAs(formattedCode) } @Test @@ -919,4 +943,43 @@ class ArgumentListWrappingRuleTest { // When ValueArgumentCommentRule is not loaded or enabled argumentListWrappingRuleAssertThat(code).hasNoLintViolations() } + + @Test + fun `Issue 2462 - Given a call expression with value argument list inside a binary expression, then first wrap the binary expression`() { + val code = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + fun foo() { + every { foo.bar(bazbazbazbazbazbazbazbazbaz) } returns bar + } + """.trimIndent() + val formattedCode = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + fun foo() { + every { + foo.bar(bazbazbazbazbazbazbazbazbaz) + } returns bar + } + """.trimIndent() + argumentListWrappingRuleAssertThat(code) + .setMaxLineLength() + .addAdditionalRuleProvider { BinaryExpressionWrappingRule() } + .addAdditionalRuleProvider { WrappingRule() } + // Lint violations from BinaryExpressionWrappingRule and WrappingRule are reported during linting only. When formatting, the + // wrapping of the braces of the function literal by the BinaryExpressionWrapping prevents those violations from occurring. + .hasLintViolationsForAdditionalRule( + LintViolation(3, 12, "Newline expected after '{'"), + LintViolation(3, 12, "Missing newline after \"{\""), + LintViolation(3, 50, "Newline expected before '}'"), + // Lint violation below only occurs during linting. Resolving violations above, prevents the next violation from occurring + LintViolation(3, 59, "Line is exceeding max line length. Break line after 'returns' in binary expression"), + ) + // The lint violation below is only reported during lint. When formatting, the violation above is resolved first, and as a + // result this violation will no longer occur. + .hasLintViolations( + LintViolation(3, 21, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(3, 48, "Missing newline before \")\""), + ).isFormattedAs(formattedCode) + } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt index 6a811593d9..1a1b08cad2 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt @@ -30,7 +30,7 @@ class BinaryExpressionWrappingRuleTest { LintViolation(2, 11, "Line is exceeding max line length. Break line between assignment and expression"), // Next violation only happens during linting. When formatting the violation does not occur because fix of previous // violation prevent that the remainder of the line exceeds the maximum - LintViolation(2, 36, "Line is exceeding max line length. Break line after operator in binary expression"), + LintViolation(2, 36, "Line is exceeding max line length. Break line after '&&' in binary expression"), ).isFormattedAs(formattedCode) } @@ -51,7 +51,7 @@ class BinaryExpressionWrappingRuleTest { """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() - .hasLintViolation(3, 30, "Line is exceeding max line length. Break line after operator in binary expression") + .hasLintViolation(3, 30, "Line is exceeding max line length. Break line after '&&' in binary expression") .isFormattedAs(formattedCode) } @@ -74,7 +74,7 @@ class BinaryExpressionWrappingRuleTest { """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() - .hasLintViolation(3, 37, "Line is exceeding max line length. Break line after operator in binary expression") + .hasLintViolation(3, 37, "Line is exceeding max line length. Break line after '&&' in binary expression") .isFormattedAs(formattedCode) } @@ -97,7 +97,7 @@ class BinaryExpressionWrappingRuleTest { LintViolation(2, 13, "Line is exceeding max line length. Break line between assignment and expression"), // Next violation only happens during linting. When formatting the violation does not occur because fix of previous // violation prevent that the remainder of the line exceeds the maximum - LintViolation(2, 38, "Line is exceeding max line length. Break line after operator in binary expression"), + LintViolation(2, 38, "Line is exceeding max line length. Break line after '&&' in binary expression"), ).isFormattedAs(formattedCode) } @@ -118,7 +118,7 @@ class BinaryExpressionWrappingRuleTest { """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() - .hasLintViolation(3, 30, "Line is exceeding max line length. Break line after operator in binary expression") + .hasLintViolation(3, 30, "Line is exceeding max line length. Break line after '&&' in binary expression") .isFormattedAs(formattedCode) } @@ -145,7 +145,8 @@ class BinaryExpressionWrappingRuleTest { """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() - .hasLintViolation(3, 34, "Line is exceeding max line length. Break line after operator in binary expression") + .addAdditionalRuleProvider { ConditionWrappingRule() } + .hasLintViolation(3, 34, "Line is exceeding max line length. Break line after '&&' in binary expression") .isFormattedAs(formattedCode) } @@ -174,12 +175,8 @@ class BinaryExpressionWrappingRuleTest { binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() .hasLintViolations( - // When linting, a violation is reported for each operation reference. While when formatting, the nested binary expression - // is evaluated (working from outside to inside). After wrapping an outer binary expression, the inner binary expressions - // are evaluated and only wrapped again at the operation reference when needed. - LintViolation(3, 1, "Line is exceeding max line length", canBeAutoCorrected = false), - LintViolation(3, 35, "Line is exceeding max line length. Break line after operator in binary expression"), - LintViolation(3, 63, "Line is exceeding max line length. Break line after operator in binary expression"), + LintViolation(3, 63, "Line is exceeding max line length. Break line after '||' in binary expression"), + LintViolation(3, 94, "Line is exceeding max line length. Break line after '&&' in binary expression"), ).isFormattedAs(formattedCode) } @@ -195,27 +192,43 @@ class BinaryExpressionWrappingRuleTest { """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() - .hasLintViolationWithoutAutoCorrect(3, 1, "Line is exceeding max line length") + .hasNoLintViolations() } @Test - fun `Given a binary expression for which wrapping of the operator reference would still violates the max-line-length`() { + fun `Given a binary expression containing an elvis operator`() { val code = """ // $MAX_LINE_LENGTH_MARKER $EOL_CHAR - val foo1 = foo() ?: "fooooooooooo" + + val foo1 = foo() ?: "foooooooooooooooooo" + "bar" // Do not remove blank line below, it is relevant as both the newline of the blank line and the indent before property foo2 have to be accounted for - val foo2 = foo() ?: "foooooooooo" + + val foo2 = foo() ?: "foooooooooooooooooo" + + "bar" + """.trimIndent() + val formattedCode = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + val foo1 = + foo() + ?: "foooooooooooooooooo" + + "bar" + // Do not remove blank line below, it is relevant as both the newline of the blank line and the indent before property foo2 have to be accounted for + + val foo2 = + foo() + ?: "foooooooooooooooooo" + "bar" """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() .hasLintViolations( - LintViolation(2, 1, "Line is exceeding max line length", canBeAutoCorrected = false), LintViolation(2, 12, "Line is exceeding max line length. Break line between assignment and expression"), - ) + LintViolation(2, 18, "Line is exceeding max line length. Break line before '?:'"), + LintViolation(6, 12, "Line is exceeding max line length. Break line between assignment and expression"), + LintViolation(6, 18, "Line is exceeding max line length. Break line before '?:'"), + ).isFormattedAs(formattedCode) } @Test @@ -234,34 +247,64 @@ class BinaryExpressionWrappingRuleTest { } @Test - fun `Given a value argument containing a binary expression`() { + fun `Given a value argument containing a binary expression which causes the line to exceeds tbe maximum line length`() { val code = """ // $MAX_LINE_LENGTH_MARKER $EOL_CHAR - val foobar = Foo(bar(1 * 2 * 3)) + val foobar1 = Foo(1 * 2 * 3 * 4) + val foobar2 = Foo(bar(1 * 2 * 3)) + val foobar3 = Foo(bar("bar" + "bazzzzzzzzzzz")) + val foobar4 = Foo(bar("bar" + "bazzzzzzzzzzzz")) """.trimIndent() val formattedCode = """ // $MAX_LINE_LENGTH_MARKER $EOL_CHAR - val foobar = Foo( + val foobar1 = Foo( + 1 * 2 * 3 * 4 + ) + val foobar2 = Foo( bar(1 * 2 * 3) ) + val foobar3 = Foo( + bar( + "bar" + "bazzzzzzzzzzz" + ) + ) + val foobar4 = Foo( + bar( + "bar" + + "bazzzzzzzzzzzz" + ) + ) """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .addAdditionalRuleProvider { ArgumentListWrappingRule() } + .addAdditionalRuleProvider { IndentationRule() } .addAdditionalRuleProvider { WrappingRule() } .addRequiredRuleProviderDependenciesFrom(StandardRuleSetProvider()) .setMaxLineLength() .hasLintViolations( // Although violations below are reported by the Linter, they will not be enforced by the formatter. After the // ArgumentListWrapping rule has wrapped the argument, there is no more need to wrap the expression as well. - LintViolation(2, 25, "Line is exceeding max line length. Break line after operator in binary expression"), - LintViolation(2, 29, "Line is exceeding max line length. Break line after operator in binary expression"), + LintViolation(4, 23, "Line is exceeding max line length. Break line before expression"), + LintViolation(4, 30, "Line is exceeding max line length. Break line after '+' in binary expression"), + LintViolation(5, 23, "Line is exceeding max line length. Break line before expression"), + LintViolation(5, 30, "Line is exceeding max line length. Break line after '+' in binary expression"), ).hasLintViolationsForAdditionalRules( - LintViolation(2, 18, "Argument should be on a separate line (unless all arguments can fit a single line)"), - LintViolation(2, 22, "Argument should be on a separate line (unless all arguments can fit a single line)"), - LintViolation(2, 31, "Missing newline before \")\""), + LintViolation(2, 19, "Argument should be on a separate line (unless all arguments can fit a single line)"), LintViolation(2, 32, "Missing newline before \")\""), + LintViolation(3, 19, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(3, 23, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(3, 32, "Missing newline before \")\""), + LintViolation(3, 33, "Missing newline before \")\""), + LintViolation(4, 19, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(4, 23, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(4, 46, "Missing newline before \")\""), + LintViolation(4, 47, "Missing newline before \")\""), + LintViolation(5, 19, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(5, 23, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(5, 47, "Missing newline before \")\""), + LintViolation(5, 48, "Missing newline before \")\""), ).isFormattedAs(formattedCode) } @@ -296,12 +339,21 @@ class BinaryExpressionWrappingRuleTest { } @Test - fun `Given a call expression followed by lambda argument`() { + fun `Given a call expression containing an binary expression value argument, followed by a lambda on the same line, then wrap the lambda`() { val code = """ - // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR fun foo() { - require(bar != "bar") { "some longgggggggggggggggggg message" } + require(bar != "barrrrrrrr") { "some longgggggggggggggggggg message" } + } + """.trimIndent() + val formattedCode = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + fun foo() { + require(bar != "barrrrrrrr") { + "some longgggggggggggggggggg message" + } } """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) @@ -309,7 +361,33 @@ class BinaryExpressionWrappingRuleTest { .addAdditionalRuleProvider { ArgumentListWrappingRule() } .addAdditionalRuleProvider { WrappingRule() } .addRequiredRuleProviderDependenciesFrom(StandardRuleSetProvider()) - .hasLintViolationWithoutAutoCorrect(3, 1, "Line is exceeding max line length") + .isFormattedAs(formattedCode) + } + + @Test + fun `Given an elvis expression exceeding the line length`() { + val code = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + val foo = foobar ?: throw UnsupportedOperationException("foobar") + """.trimIndent() + val formattedCode = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + val foo = + foobar + ?: throw UnsupportedOperationException( + "foobar" + ) + """.trimIndent() + binaryExpressionWrappingRuleAssertThat(code) + .setMaxLineLength() + .addAdditionalRuleProvider { ArgumentListWrappingRule() } + .addAdditionalRuleProvider { MaxLineLengthRule() } + .hasLintViolations( + LintViolation(2, 11, "Line is exceeding max line length. Break line between assignment and expression", true), + LintViolation(2, 18, "Line is exceeding max line length. Break line before '?:'", true), + ).isFormattedAs(formattedCode) } @Test @@ -321,12 +399,46 @@ class BinaryExpressionWrappingRuleTest { bar ?: throw UnsupportedOperationException("foobar") """.trimIndent() + binaryExpressionWrappingRuleAssertThat(code) + .setMaxLineLength() + .addAdditionalRuleProvider { MaxLineLengthRule() } + .hasLintViolationForAdditionalRule(4, 56, "Exceeded max line length (55)", false) + .hasNoLintViolationsExceptInAdditionalRules() + } + + @Test + fun `Issue 2462 - Given a call expression with value argument list inside a binary expression, then first wrap the binary expression`() { + val code = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + fun foo() { + every { foo.bar(bazbazbazbazbazbazbazbazbaz) } returns bar + } + """.trimIndent() + val formattedCode = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + fun foo() { + every { + foo.bar(bazbazbazbazbazbazbazbazbaz) + } returns bar + } + """.trimIndent() binaryExpressionWrappingRuleAssertThat(code) .setMaxLineLength() .addAdditionalRuleProvider { ArgumentListWrappingRule() } .addAdditionalRuleProvider { WrappingRule() } - .addAdditionalRuleProvider { MaxLineLengthRule() } - .addRequiredRuleProviderDependenciesFrom(StandardRuleSetProvider()) - .hasLintViolationWithoutAutoCorrect(4, 1, "Line is exceeding max line length") + // Lint violations from ArgumentListWrappingRule and WrappingRule are reported during linting only. When formatting, the + // wrapping of the braces of the function literal by the BinaryExpressionWrapping prevents those violations from occurring. + .hasLintViolationsForAdditionalRule( + LintViolation(3, 12, "Missing newline after \"{\""), + LintViolation(3, 21, "Argument should be on a separate line (unless all arguments can fit a single line)"), + LintViolation(3, 48, "Missing newline before \")\""), + ).hasLintViolations( + LintViolation(3, 12, "Newline expected after '{'"), + LintViolation(3, 50, "Newline expected before '}'"), + // Lint violation below only occurs during linting. Resolving violations above, prevents the next violation from occurring + LintViolation(3, 59, "Line is exceeding max line length. Break line after 'returns' in binary expression"), + ).isFormattedAs(formattedCode) } } From f5fbd36aaf8313f8751fd458121b3f24acce15cd Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Fri, 5 Jan 2024 17:15:02 +0100 Subject: [PATCH 07/17] Move configuration setting documentation to rule (#2489) For settings which are tied to a specific rule, it is more clear to have the setting documented at the same place as the rule. Other generic settings are kept at the configuration page. --- .../docs/rules/configuration-ktlint.md | 239 ++---------------- .../release-latest/docs/rules/experimental.md | 9 + .../release-latest/docs/rules/standard.md | 216 ++++++++++++---- .../docs/rules/configuration-ktlint.md | 239 ++---------------- .../snapshot/docs/rules/experimental.md | 9 + documentation/snapshot/docs/rules/standard.md | 152 +++++++++-- .../rules/ChainMethodContinuationRule.kt | 2 +- .../standard/rules/ClassSignatureRule.kt | 6 +- 8 files changed, 360 insertions(+), 512 deletions(-) diff --git a/documentation/release-latest/docs/rules/configuration-ktlint.md b/documentation/release-latest/docs/rules/configuration-ktlint.md index cbb3c00a23..3c83ea787e 100644 --- a/documentation/release-latest/docs/rules/configuration-ktlint.md +++ b/documentation/release-latest/docs/rules/configuration-ktlint.md @@ -13,7 +13,7 @@ By default, the `ktlint_official` code style is applied. Alternatively, the code ktlint_code_style = ktlint_official ``` -## Disabled rules +## Disable rule(s) Rule sets and individual rules can be disabled / enabled with a separate property per rule (set). @@ -39,221 +39,26 @@ ktlint_custom-rule-set_custom-rule = disabled # Disables the `custom-rule` rule !!! note The *rule* properties are applied after applying the *rule set* properties and take precedence. So if a rule set is disabled but a specific rule of that rule set is enabled, then the rule will be executed. - -## Final newline - -By default, a final newline is required at the end of the file. - -```ini -[*.{kt,kts}] -insert_final_newline = true -``` - -This setting only takes effect when rule `final-newline` is enabled. - -## Force multiline chained methods based on number of chain operators - -Setting `ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than` forces a chained method to be wrapped at each chain operator (`.` or `?.`) in case it contains the specified minimum number of chain operators even in case the entire chained method fits on a single line. Use value `unset` (default) to disable this setting. - -!!! note - By default, chained methods are wrapped when an expression contains 4 or more chain operators in an expression. Note that if a chained method contains nested expressions the chain operators of the inner expression are not taken into account. - -```ini -[*.{kt,kts}] -ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than=unset -``` - -This setting only takes effect when rule `chain-method-continution` is enabled. - -## Force multiline function signature based on number of parameters - -Setting `ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than` forces a multiline function signature in case the function contains the specified minimum number of parameters even in case the function signature would fit on a single line. Use value `unset` (default) to disable this setting. - -!!! note - By default, the `ktlint_official` code style wraps parameters of functions having at least 2 parameters. For other code styles, this setting is disabled by default. - -```ini -[*.{kt,kts}] -ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than=unset -``` - -This setting only takes effect when rule `function-signature` is enabled. - -## Wrapping the expression body of a function - -Setting `ktlint_function_signature_body_expression_wrapping` determines if and when the expression body of a function is wrapped to a new line. This setting can be set to value `default`, `multiline` or `always`. - -!!! note - In this context `default` means the default behavior of the IntelliJ IDEA formatter. If not set explicitly, this style is used by code styles `intellij_idea` and `android_studio`. Code style `ktlint_official` uses style `multiline` when this setting has no value. - -When set to `default`, the first line of a body expression is appended to the function signature as long as the max line length is not exceeded. - -```kotlin title="ktlint_function_signature_body_expression_wrapping=default" -// Given that the function signature has to be written as a single line function signature -fun someFunction(a: Any, b: Any): String = "some-result" - .uppercase() - -// Given that the function signature has to be written as a multiline function signature -fun someFunction( - a: Any, - b: Any -): String = "some-result" - .uppercase() -``` - -When set to `multiline`, the body expression starts on a separate line in case it is a multiline expression. A single line body expression is wrapped only when it does not fit on the same line as the function signature. - -```kotlin title="ktlint_function_signature_body_expression_wrapping=multiline" -// Given a single line body expression and -// a the function signature that has to be written as a single line function signature and -// it does not exceed the max line length -fun someFunction(a: Any, b: Any): String = "some-result".uppercase() - -// Given a single line body expression and -// a the function signature that has to be written as a multiline function signature and -// it does not exceed the max line length -fun someFunction( - a: Any, - b: Any -): String = "some-result".uppercase() - -// Given a single line body expression then always wrap it to a separate line -fun someFunction(a: Any, b: Any): String = - "some-result" - .uppercase() -fun someFunction( - a: Any, - b: Any -): String = - "some-result" - .uppercase() -``` - -When set to `always` the body expression is always wrapped to a separate line. - -```kotlin title="ktlint_function_signature_body_expression_wrapping=always" -fun someFunction(a: Any, b: Any): String = - "some-result".uppercase() -fun functionWithAVeryLongName( - a: Any, - b: Any -): String = - "some-result" - .uppercase() -``` - -This setting only takes effect when rule `function-signature` is enabled. - -## Ignore identifiers enclosed in backticks - -By default, the identifiers enclosed in backticks are not ignored. - -According to [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods) it is acceptable to write method names in natural language. When using natural language, the description tends to be longer. This property allows lines containing an identifier between backticks to be longer than the maximum line length. (Since 0.41.0) - -```kotlin -@Test -fun `Given a test with a very loooooooooooooooooooooong test description`() { - -} -``` - -```ini -[*.{kt,kts}] -ktlint_ignore_back_ticked_identifier = false -``` - -This setting only takes effect when rule `max-line-length` is enabled. - -## Import layouts - -By default, the same imports are allowed as in IntelliJ IDEA. The import path can be a full path, e.g. "java.util.List.*" as well as wildcard path, e.g. "kotlin.**". - -The layout can be composed by the following symbols: - -* `*` - 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. - -Examples: -```kotlin -ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines -ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list -ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout -``` - -Wildcard imports can be allowed for specific import paths (Comma-separated list, use "**" as wildcard for package and all subpackages). This setting overrides the no-wildcard-imports rule. This setting is best be used for allowing wildcard imports from libraries like Ktor where extension functions are used in a way that creates a lot of imports. - -```ini -[*.{kt,kts}] -ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.** -``` - -This setting only takes effect when rule `no-wildcard-imports` is enabled. - -## Indent size & style - -By default, indenting is done with 4 spaces per indent level. Code style `android_studio` uses a tab per indent level. - -```ini -[*.{kt,kts}] -indent_size = 4 # possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely) -indent_style = space # or "tab" -``` - -Those settings are used by multiple rules of which rule `indent` is the most important. - -## Max line length - -By default, the maximum line length is not set. The `android` code style sets the max line length to 100 (per Android Kotlin Style Guide). - -```ini -[*.{kt,kts}] -max_line_length = off # Use "off" to ignore max line length or a positive number to set max line length -``` - -This setting is used by multiple rules of which rule `max-line-length` is the most important. - -## Trailing comma on call site - -KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma_on_call_site` to *enforce* the usage of the trailing comma at call site when enabled. IntelliJ IDEA uses this property to *allow* the use of trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - -!!! note - In KtLint 0.48.x the default value for using the trailing comma on call site has been changed to `true` except when codestyle `android` is used. - - Although the [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) leaves it to the developer's discretion to use trailing commas on the call site, it also states that usage of trailing commas has several benefits: - - * It makes version-control diffs cleaner – as all the focus is on the changed value. - * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. - * It simplifies code generation, for example, for object initializers. The last element can also have a comma. - -Example: -```ini -[*.{kt,kts}] -ij_kotlin_allow_trailing_comma_on_call_site = false -``` - -This setting only takes effect when rule `trailing-comma-on-call-site` is enabled. - -## Trailing comma on declaration site - -KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma` to *enforce* the usage of the trailing comma at declaration site when enabled. IntelliJ IDEA uses this property to *allow* the use of trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - -!!! note - In KtLint 0.48.x the default value for using the trailing comma on declaration site has been changed to `true` except when codestyle `android` is used. - - The [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) encourages the usage of trailing commas on the declaration site, but leaves it to the developer's discretion to use trailing commas on the call site. But next to this, it also states that usage of trailing commas has several benefits: - - * It makes version-control diffs cleaner – as all the focus is on the changed value. - * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. - * It simplifies code generation, for example, for object initializers. The last element can also have a comma. - -Example: -```ini -[*.{kt,kts}] -ij_kotlin_allow_trailing_comma = false # Only used for declaration site -``` - -This setting only takes effect when rule `trailing-comma-on-declaration-site` is enabled. +## Rule specific configuration settings + +The configuration settings below are used to configure the behavior of a specific rule. As of that, those settings only take effect when the corresponding rule is enabled. See description of rule for more information about the setting. + +| Configuration setting | Rule | +|:------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------| +| ij_kotlin_allow_trailing_comma | [trailing-comma-on-declaration-site](../rules/standard/#trailing-comma-on-declaration-site) | +| ij_kotlin_allow_trailing_comma_on_call_site | [trailing-comma-on-call-site](../rules/standard/#trailing-comma-on-call-site) | +| ij_kotlin_packages_to_use_import_on_demand | [no-wildcard-imports](../rules/standard/#no-wildcard-imports) | +| indent_size | [indent](../rules/standard/#indentation) | | +| indent_style | [indent](../rules/standard/#indentation) | | + | +| insert_final_newline | [final-newline](../rules/standard/#final-newline) | | +| ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than | [chain-method-continuation](../rules/experimental/#chain-method-continuation) | +| ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [class-signature](../rules/experimental/#class-signature) | +| ktlint_ignore_back_ticked_identifier | [max-line-length](../rules/standard/#max-line-length) | +| ktlint_function_naming_ignore_when_annotated_with | [function-naming](../rules/standard/#function-naming) | +| ktlint_function_signature_body_expression_wrapping | [function-signature](../rules/standard/#function-signature) | +| ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [function-signature](../rules/standard/#function-signature) | +| max_line_length | [max-line-length](../rules/standard/#max-line-length) and several other rules | ## Overriding Editorconfig properties for specific directories @@ -266,4 +71,4 @@ ktlint_standard_import-ordering = disabled ktlint_standard_indent = disabled ``` -Note that the `import-ordering` rule is disabled for *all* packages including the `api` sub package. Next to this the `indent` rule is disabled for the `api` package and its sub packages. +Note that in example above the `import-ordering` rule is disabled for *all* packages including the `api` sub package. Next to this the `indent` rule is disabled for the `api` package and its sub packages. diff --git a/documentation/release-latest/docs/rules/experimental.md b/documentation/release-latest/docs/rules/experimental.md index c40dcc7524..28730e37ac 100644 --- a/documentation/release-latest/docs/rules/experimental.md +++ b/documentation/release-latest/docs/rules/experimental.md @@ -125,6 +125,11 @@ This rule can be configured with `.editorconfig` property [`ktlint_chain_method_ .bar() ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than`
Force wrapping of chained methods in case an expression contains at least the specified number of chain operators. If a chained method contains nested expressions, the chain operators of the inner expression are not taken into account. Use value `unset` (default) to disable this setting. | 4 | 4 | 4 | + + Rule id: `chain-method-continuation` (`standard` rule set) !!! Note @@ -331,6 +336,10 @@ The other code styles allow an infinite amount of parameters on the same line (a class Foo6(a: Any, b: Any, c: Any) : FooBar(a, c) ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than`
Force wrapping of the parameters of the class signature in case it contains at least the specified number of parameters, even in case the entire class signature would fit on a single line. Use value `unset` to disable this setting. | 1 | `unset` | `unset` | + Rule id: `class-signature` (`standard` rule set) ## Condition wrapping diff --git a/documentation/release-latest/docs/rules/standard.md b/documentation/release-latest/docs/rules/standard.md index 6049bf39ff..375b5c35d3 100644 --- a/documentation/release-latest/docs/rules/standard.md +++ b/documentation/release-latest/docs/rules/standard.md @@ -164,15 +164,15 @@ Rule id: `filename` (`standard` rule set) Ensures consistent usage of a newline at the end of each file. -This rule can be configured with `.editorconfig` property [`insert_final_newline`](../configuration-ktlint/#final-newline). +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:----------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `insert_final_newline` | `true` | `true` | `true` | Rule id: `final-newline` (`standard` rule set) ## Function signature -Rewrites the function signature to a single line when possible (e.g. when not exceeding the `max_line_length` property) or a multiline signature otherwise. In case of function with a body expression, the body expression is placed on the same line as the function signature when not exceeding the `max_line_length` property. - -In `ktlint-official` code style, a function signature is always rewritten to a multiline signature in case the function has 2 or more parameters. This number of parameters can be set via `.editorconfig` property `ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than`. +Rewrites the function signature to a single line when possible (e.g. when not exceeding the `max_line_length` property) or a multiline signature otherwise. !!! note Wrapping of parameters is also influenced by the `parameter-list-wrapping` rule. @@ -244,6 +244,84 @@ In `ktlint-official` code style, a function signature is always rewritten to a m .uppercase() ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_function_signature_body_expression_wrapping`
Determines how to wrap the body of function in case it is an expression. Use `default` to wrap the body expression only when the first line of the expression does not fit on the same line as the function signature. Use `multiline` to force wrapping of body expressions that consists of multiple lines. Use `always` to force wrapping of body expression always. | `multiline` | `default` | `default` | +| `ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than`
Forces a multiline function signature in case the function contains the specified minimum number of parameters even in case the function signature would fit on a single line. Use value `unset` (default) to disable this setting. | 2 | `unset` | `unset` | + +=== "[:material-heart:](#) default" + + When `ktlint_function_signature_body_expression_wrapping` is set to `default`, the first line of a body expression is appended to the function signature as long as the max line length is not exceeded. + + ```kotlin title="ktlint_function_signature_body_expression_wrapping=default" + // Given that the function signature has to be written as a single line + // function signature and that the function has a multiline body expression + fun someFunction(a: Any, b: Any): String = "some-result" + .uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a multiline body expression + fun someFunction( + a: Any, + b: Any + ): String = "some-result" + .uppercase() + ``` + +=== "[:material-heart:](#) multiline" + + When `ktlint_function_signature_body_expression_wrapping` is set to `multiline`, the body expression starts on a separate line in case it is a multiline expression. A single line body expression is wrapped only when it does not fit on the same line as the function signature. + + ```kotlin title="ktlint_function_signature_body_expression_wrapping=multiline" + // Given that the function signature has to be written as a single line + // function signature and that the function has a single line body expression + // that fits on the same line as the function signature. + fun someFunction(a: Any, b: Any): String = "some-result".uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a single line body expression + // that fits on the same line as the function signature. + fun someFunction( + a: Any, + b: Any + ): String = "some-result".uppercase() + + // Given that the function signature has to be written as a single line + // function signature and that the function has a multiline body expression + fun someFunction(a: Any, b: Any): String = + "some-result" + .uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a multiline body expression + fun someFunction( + a: Any, + b: Any + ): String = + "some-result" + .uppercase() + ``` + +=== "[:material-heart:](#) always" + + When `ktlint_function_signature_body_expression_wrapping` is set to `always` the body expression is always wrapped to a separate line. + + ```kotlin title="ktlint_function_signature_body_expression_wrapping=always" + // Given that the function signature has to be written as a single line + // function signature and that the function has a single line body expression + fun someFunction(a: Any, b: Any): String = + "some-result".uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a multiline body expression + fun functionWithAVeryLongName( + a: Any, + b: Any + ): String = + "some-result" + .uppercase() + ``` + Rule id: `function-signature` (`standard` rule set) ## If else bracing @@ -335,6 +413,11 @@ Indentation formatting - respects `.editorconfig` `indent_size` with no continua !!! note This rule handles indentation for many different language constructs which can not be summarized with a few examples. See the [unit tests](https://github.com/pinterest/ktlint/blob/master/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt) for more details. +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `indent_size`
The size of an indentation level when `indent_style` is set to `space`. Use value `unset` to ignore indentation. | 4 | 4 | 4 | +| `indent_style`
Style of indentation. Set this value to `space` or `tab`. | `space` | `space` | `space` | + Rule id: `indent` (`standard` rule set) ## Naming @@ -411,11 +494,15 @@ Enforce naming of function. fun do_something() {} ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_function_naming_ignore_when_annotated_with`
Ignore functions that are annotated with values in this setting. This value is a comma separated list of names without the '@' prefix. | `unset` | `unset` | `unset` | + !!! note - When using Compose, you might want to suppress the `function-naming` rule by setting `.editorconfig` property `ktlint_function_naming_ignore_when_annotated_with=Composable`. Furthermore, you can use a dedicated ktlint ruleset like [Compose Rules](https://mrmans0n.github.io/compose-rules/ktlint/) for checking naming conventions for Composable functions. + When using Compose, you might want to configure the `function-naming` rule with `.editorconfig` property `ktlint_function_naming_ignore_when_annotated_with=Composable`. Furthermore, you can use a dedicated ktlint ruleset like [Compose Rules](https://mrmans0n.github.io/compose-rules/ktlint/) for checking naming conventions for Composable functions. !!! note - Functions in files which import a class from package `org.junit`, `org.testng` or `kotlin.test` are considered to be test functions. Functions in such classes are allowed to have underscores in the name. Also, function names enclosed between backticks do not need to adhere to the normal naming convention. + Functions in files which import a class from package `io.kotest`, `junit.framework`, `kotlin.test`, `org.junit`, or `org.testng` are considered to be test functions. Functions in such classes are allowed to have underscores in the name. Also, function names enclosed between backticks do not need to adhere to the normal naming convention. This rule can also be suppressed with the IntelliJ IDEA inspection suppression `FunctionName`. @@ -819,7 +906,7 @@ Rule id: `ktlint-suppression` (`standard` rule set) ## Max line length -Ensures that lines do not exceed the given length of `.editorconfig` property `max_line_length` (see [EditorConfig](../configuration-ktlint/) section for more). This rule does not apply in a number of situations. For example, in the case a line exceeds the maximum line length due to a comment that disables ktlint rules then that comment is being ignored when validating the length of the line. The `.editorconfig` property `ktlint_ignore_back_ticked_identifier` can be set to ignore identifiers which are enclosed in backticks, which for example is very useful when you want to allow longer names for unit tests. +Ensures that lines do not exceed the maximum length of a line. This rule does not apply in a number of situations. The `.editorconfig` property `ktlint_ignore_back_ticked_identifier` can be set to ignore identifiers which are enclosed in backticks, which for example is very useful when you want to allow longer names for unit tests. === "[:material-heart:](#) Ktlint" @@ -851,6 +938,12 @@ Ensures that lines do not exceed the given length of `.editorconfig` property `m "foooooooooooooooooooooooooooooooooooooooo" ``` + +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:--------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_ignore_back_ticked_identifier`
Defines whether the backticked identifier (``) should be ignored. | `false` | `false` | `false` | +| `max_line_length`
Maximum length of a (regular) line. | 140 | `off` | `100` | + Rule id: `max-line-length` (`standard` rule set) ## Modifier order @@ -1172,7 +1265,7 @@ Rule id: `no-unused-imports` (`standard` rule set) ## No wildcard imports -No wildcard imports except imports listed in `.editorconfig` property `ij_kotlin_packages_to_use_import_on_demand`. +No wildcard imports except whitelisted imports. === "[:material-heart:](#) Ktlint" @@ -1186,6 +1279,10 @@ No wildcard imports except imports listed in `.editorconfig` property `ij_kotlin import foobar.* ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:------------------------------------------------------------------------------------------|:---------------:|:------------------------------------------:|:-----------------------------------------------:| +| `ij_kotlin_packages_to_use_import_on_demand`
Defines allowed wildcard imports. | - | `java.util.*,`
`kotlinx.android.synthetic.**` | `java.util.*,`
`kotlinx.android.synthetic.**` | + !!! warning In case property `ij_kotlin_packages_to_use_import_on_demand` is not explicitly set, it allows wildcards imports like `java.util.*` by default to keep in sync with IntelliJ IDEA behavior. To disallow *all* wildcard imports, add property below to your `.editorconfig`: ```editorconfig @@ -1193,6 +1290,21 @@ No wildcard imports except imports listed in `.editorconfig` property `ij_kotlin ij_kotlin_packages_to_use_import_on_demand = unset ``` +Configuration setting `ij_kotlin_packages_to_use_import_on_demand` is a comma separated string of import paths. This can be a full path, e.g. "java.util.List.*", or a wildcard path, e.g. "kotlin.**". Use "**" as wildcard for package and all subpackages. + +The layout can be composed by the following symbols: + +* `*` - 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. + +Examples: +```kotlin +ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines +ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list +ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout +``` + Rule id: `no-wildcard-imports` (`standard` rule set) ## Spacing @@ -1932,9 +2044,6 @@ Rule id: `string-template-indent` (`standard` rule set) Consistent removal (default) or adding of trailing commas on call site. -!!! important - KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma_on_call_site` to configure the rule. When this property is enabled, KtLint *enforces* the usage of the trailing comma at call site while IntelliJ IDEA default formatter only *allows* to use the trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - === "[:material-heart:](#) Ktlint" ```kotlin @@ -1956,17 +2065,18 @@ Consistent removal (default) or adding of trailing commas on call site. ),) // it's weird to insert "," between unwrapped (continued) parenthesis ``` -!!! note - In KtLint 0.48.x the default value for using the trailing comma on call site has been changed to `true` except when codestyle `android` is used. +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ij_kotlin_allow_trailing_comma_on_call_site`
Defines whether a trailing comma (or no trailing comma) should be enforced on the calling site, e.g. argument-list, when-entries, lambda-arguments, indices, etc. When set, IntelliJ IDEA uses this property to allow usage of a trailing comma by discretion of the developer. KtLint however uses this setting to enforce consistent usage of the trailing comma when set. | `true` | `true` | `false` | +!!! note Although the [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) leaves it to the developer's discretion to use trailing commas on the call site, it also states that usage of trailing commas has several benefits: * It makes version-control diffs cleaner – as all the focus is on the changed value. * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. * It simplifies code generation, for example, for object initializers. The last element can also have a comma. -!!! note - Trailing comma on call site is automatically disabled if the [Wrapping](#wrapping) rule (or, before version `0.45.0`, the [Indentation](#indentation) rule) is disabled or not loaded. Because it cannot provide proper formatting with unwrapped calls. (see [dependencies](./dependencies.md)). + KtLint values *consistent* formatting more than a per-situation decision, and therefore uses this setting to *enforce/disallow* usage of trailing comma's on the calling site. Rule id: `trailing-comma-on-call-site` (`standard` rule set) @@ -1974,9 +2084,6 @@ Rule id: `trailing-comma-on-call-site` (`standard` rule set) Consistent removal (default) or adding of trailing commas on declaration site. -!!! important - KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma` to configure the rule. When this property is enabled, KtLint *enforces* the usage of the trailing comma at declaration site while IntelliJ IDEA default formatter only *allows* to use the trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - === "[:material-heart:](#) Ktlint" ```kotlin @@ -1996,17 +2103,18 @@ Consistent removal (default) or adding of trailing commas on declaration site. ),) // it's weird to insert "," between unwrapped (continued) parenthesis ``` -!!! note - In KtLint 0.48.x the default value for using the trailing comma on declaration site has been changed to `true` except when codestyle `android` is used. +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ij_kotlin_allow_trailing_comma`
Defines whether a trailing comma (or no trailing comma) should be enforced on the defining site, e.g. parameter-list, type-argument-list, lambda-value-parameters, enum-entries, etc. When set, IntelliJ IDEA uses this property to allow usage of a trailing comma by discretion of the developer. KtLint however uses this setting to enforce consistent usage of the trailing comma when set. | `true` | `true` | `false` | +!!! note The [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) encourages the usage of trailing commas on the declaration site, but leaves it to the developer's discretion to use trailing commas on the call site. But next to this, it also states that usage of trailing commas has several benefits: * It makes version-control diffs cleaner – as all the focus is on the changed value. * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. * It simplifies code generation, for example, for object initializers. The last element can also have a comma. -!!! note - Trailing comma on declaration site is automatically disabled if the [Wrapping](#wrapping) rule (or, before version `0.45.0`, the [Indentation](#indentation) rule) is disabled or not loaded. Because it cannot provide proper formatting with unwrapped declarations. (see [dependencies](./dependencies.md)). + KtLint values *consistent* formatting more than a per-situation decision, and therefore uses this setting to *enforce/disallow* usage of trailing comma's in declarations. Rule id: `trailing-comma-on-declaration-site` (`standard` rule set) @@ -2041,14 +2149,15 @@ Disallows comments to be placed at certain locations inside a type argument (lis > ``` -Note: Although code sample below might look ok, it is semantically and programmatically unclear to which element `some comment 1` refers. As of that comments are only allowed when starting on a separate line. -```kotlin -fun Foo< - out Bar1, // some comment 1 - // some comment 2 - out Bar2, // some comment +!!! note + In some projects it is an accepted practice to use EOL comments to document the parameter *before* the comma as is shown below: + ```kotlin + fun Foo< + out Bar1, // some comment + out Bar2, // some other comment >.foo() {} -``` + ``` + Although this code sample might look ok, it is semantically and programmatically unclear to which type `some comment` refers. From the developer perspective it might be clear that it belongs to type `Bar1`. From the parsers perspective, it does belong to type `Bar2`. Rule id: `type-argument-comment` (`standard` rule set) @@ -2082,14 +2191,15 @@ Disallows comments to be placed at certain locations inside a type parameter (li > ``` -Note: Although code sample below might look ok, it is semantically and programmatically unclear to which element `some comment 1` refers. As of that comments are only allowed when starting on a separate line. -```kotlin -class Foo< - out Bar1, // some comment 1 - // some comment 2 - out Bar2, // some comment +!!! note + In some projects it is an accepted practice to use EOL comments to document the parameter *before* the comma as is shown below: + ```kotlin + class Foo< + out Bar1, // some comment + out Bar2, // some other comment > -``` + ``` + Although this code sample might look ok, it is semantically and programmatically unclear on which parameter `some comment` refers. From the developer perspective it might be clear that it belongs to type `Bar1`. From the parsers perspective, it does belong to type `Bar2`. Rule id: `type-parameter-comment` (`standard` rule set) @@ -2142,14 +2252,15 @@ Disallows comments to be placed at certain locations inside a value argument (li ) ``` -Note: Although code sample below might look ok, it is semantically and programmatically unclear to which element `some comment 1` refers. As of that comments are only allowed when starting on a separate line. -```kotlin -class Foo< - out Bar1, // some comment 1 - // some comment 2 - out Bar2, // some comment - > -``` +!!! note + In a lot of projects it is an accepted practice to use EOL comments to document the parameter *before* the comma as is shown below: + ```kotlin + fun foo( + bar1: Bar1, // some comment + bar2: Bar2, // some other comment + ) + ``` + Although this code sample might look ok, it is semantically and programmatically unclear on which parameter `some comment` refers. From the developer perspective it might be clear that it belongs to parameter `bar1`. From the parsers perspective, it does belong to parameter `bar2`. This might lead to [unexpected behavior in Intellij IDEA](https://github.com/pinterest/ktlint/issues/2445#issuecomment-1863432022). Rule id: `value-argument-comment` (`standard` rule set) @@ -2190,14 +2301,15 @@ Disallows comments to be placed at certain locations inside a value argument (li ) ``` -Note: Although code sample below might look ok, it is semantically and programmatically unclear to which element `some comment 1` refers. As of that comments are only allowed when starting on a separate line. -```kotlin -class Foo( - bar: Bar1, // some comment 1 - // some comment 2 - bar2: Bar2, // some comment -) -``` +!!! note + In a lot of projects it is an accepted practice to use EOL comments to document the parameter *before* the comma as is shown below: + ```kotlin + class Foo( + bar1: Bar1, // some comment + bar2: Bar2, // some other comment + ) + ``` + Although this code sample might look ok, it is semantically and programmatically unclear on which parameter `some comment` refers. From the developer perspective it might be clear that it belongs to parameter `bar1`. From the parsers perspective, it does belong to parameter `bar2`. This might lead to [unexpected behavior in Intellij IDEA](https://github.com/pinterest/ktlint/issues/2445#issuecomment-1863432022). Rule id: `value-parameter-comment` (`standard` rule set) diff --git a/documentation/snapshot/docs/rules/configuration-ktlint.md b/documentation/snapshot/docs/rules/configuration-ktlint.md index 62c21ec450..3c83ea787e 100644 --- a/documentation/snapshot/docs/rules/configuration-ktlint.md +++ b/documentation/snapshot/docs/rules/configuration-ktlint.md @@ -13,7 +13,7 @@ By default, the `ktlint_official` code style is applied. Alternatively, the code ktlint_code_style = ktlint_official ``` -## Disabled rules +## Disable rule(s) Rule sets and individual rules can be disabled / enabled with a separate property per rule (set). @@ -39,221 +39,26 @@ ktlint_custom-rule-set_custom-rule = disabled # Disables the `custom-rule` rule !!! note The *rule* properties are applied after applying the *rule set* properties and take precedence. So if a rule set is disabled but a specific rule of that rule set is enabled, then the rule will be executed. - -## Final newline - -By default, a final newline is required at the end of the file. - -```ini -[*.{kt,kts}] -insert_final_newline = true -``` - -This setting only takes effect when rule `final-newline` is enabled. - -## Force multiline chained methods based on number of chain operators - -Setting `ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than` forces a chained method to be wrapped at each chain operator (`.` or `?.`) in case it contains the specified minimum number of chain operators even in case the entire chained method fits on a single line. Use value `unset` (default) to disable this setting. - -!!! note - By default, chained methods are wrapped when an expression contains 4 or more chain operators in an expression. Note that if a chained method contains nested expressions the chain operators of the inner expression are not taken into account. - -```ini -[*.{kt,kts}] -ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than=unset -``` - -This setting only takes effect when rule `chain-method-continution` is enabled. - -## Force multiline function signature based on number of parameters - -Setting `ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than` forces a multiline function signature in case the function contains the specified minimum number of parameters even in case the function signature would fit on a single line. Use value `unset` (default) to disable this setting. - -!!! note - By default, the `ktlint_official` code style wraps parameters of functions having at least 2 parameters. For other code styles, this setting is disabled by default. - -```ini -[*.{kt,kts}] -ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than=unset -``` - -This setting only takes effect when rule `function-signature` is enabled. - -## Wrapping the expression body of a function - -Setting `ktlint_function_signature_body_expression_wrapping` determines if and when the expression body of a function is wrapped to a new line. This setting can be set to value `default`, `multiline` or `always`. - -!!! note - In this context `default` means the default behavior of the IntelliJ IDEA formatter. If not set explicitly, this style is used by code styles `intellij_idea` and `android_studio`. Code style `ktlint_official` uses style `multiline` when this setting has no value. - -When set to `default`, the first line of a body expression is appended to the function signature as long as the max line length is not exceeded. - -```kotlin title="ktlint_function_signature_body_expression_wrapping=default" -// Given that the function signature has to be written as a single line function signature -fun someFunction(a: Any, b: Any): String = "some-result" - .uppercase() - -// Given that the function signature has to be written as a multiline function signature -fun someFunction( - a: Any, - b: Any -): String = "some-result" - .uppercase() -``` - -When set to `multiline`, the body expression starts on a separate line in case it is a multiline expression. A single line body expression is wrapped only when it does not fit on the same line as the function signature. - -```kotlin title="ktlint_function_signature_body_expression_wrapping=multiline" -// Given a single line body expression and -// a the function signature that has to be written as a single line function signature and -// it does not exceed the max line length -fun someFunction(a: Any, b: Any): String = "some-result".uppercase() - -// Given a single line body expression and -// a the function signature that has to be written as a multiline function signature and -// it does not exceed the max line length -fun someFunction( - a: Any, - b: Any -): String = "some-result".uppercase() - -// Given a single line body expression then always wrap it to a separate line -fun someFunction(a: Any, b: Any): String = - "some-result" - .uppercase() -fun someFunction( - a: Any, - b: Any -): String = - "some-result" - .uppercase() -``` - -When set to `always` the body expression is always wrapped to a separate line. - -```kotlin title="ktlint_function_signature_body_expression_wrapping=always" -fun someFunction(a: Any, b: Any): String = - "some-result".uppercase() -fun functionWithAVeryLongName( - a: Any, - b: Any -): String = - "some-result" - .uppercase() -``` - -This setting only takes effect when rule `function-signature` is enabled. - -## Ignore identifiers enclosed in backticks - -By default, the identifiers enclosed in backticks are not ignored. - -According to [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods) it is acceptable to write method names in natural language. When using natural language, the description tends to be longer. This property allows lines containing an identifier between backticks to be longer than the maximum line length. (Since 0.41.0) - -```kotlin -@Test -fun `Given a test with a very loooooooooooooooooooooong test description`() { - -} -``` - -```ini -[*.{kt,kts}] -ktlint_ignore_back_ticked_identifier = false -``` - -This setting only takes effect when rule `max-line-length` is enabled. - -## Import layouts - -By default, the same imports are allowed as in IntelliJ IDEA. The import path can be a full path, e.g. "java.util.List.*" as well as wildcard path, e.g. "kotlin.**". - -The layout can be composed by the following symbols: - -* `*` - 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. - -Examples: -```kotlin -ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines -ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list -ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout -``` - -Wildcard imports can be allowed for specific import paths (Comma-separated list, use "**" as wildcard for package and all subpackages). This setting overrides the no-wildcard-imports rule. This setting is best be used for allowing wildcard imports from libraries like Ktor where extension functions are used in a way that creates a lot of imports. - -```ini -[*.{kt,kts}] -ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.** -``` - -This setting only takes effect when rule `no-wildcard-imports` is enabled. - -## Indent size & style - -By default, indenting is done with 4 spaces per indent level. Code style `android_studio` uses a tab per indent level. - -```ini -[*.{kt,kts}] -indent_size = 4 # possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely) -indent_style = space # or "tab" -``` - -Those settings are used by multiple rules of which rule `indent` is the most important. - -## Max line length - -By default, the maximum line length is not set. The `android` code style sets the max line length to 100 (per Android Kotlin Style Guide). - -```ini -[*.{kt,kts}] -max_line_length = off # Use "off" to ignore max line length or a positive number to set max line length -``` - -This setting is used by multiple rules of which rule `max-line-length` is the most important. - -## Trailing comma on call site - -KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma_on_call_site` to *enforce* the usage of the trailing comma at call site when enabled. IntelliJ IDEA uses this property to *allow* the use of trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - -!!! note - In KtLint 0.48.x the default value for using the trailing comma on call site has been changed to `true` except when codestyle `android` is used. - - Although the [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) leaves it to the developer's discretion to use trailing commas on the call site, it also states that usage of trailing commas has several benefits: - - * It makes version-control diffs cleaner – as all the focus is on the changed value. - * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. - * It simplifies code generation, for example, for object initializers. The last element can also have a comma. - -Example: -```ini -[*.{kt,kts}] -ij_kotlin_allow_trailing_comma_on_call_site = false -``` - -This setting only takes effect when rule `trailing-comma-on-call-site` is enabled. - -## Trailing comma on declaration site - -KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma` to *enforce* the usage of the trailing comma at declaration site when enabled. IntelliJ IDEA uses this property to *allow* the use of trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - -!!! note - In KtLint 0.48.x the default value for using the trailing comma on declaration site has been changed to `true` except when codestyle `android` is used. - - The [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) encourages the usage of trailing commas on the declaration site, but leaves it to the developer's discretion to use trailing commas on the call site. But next to this, it also states that usage of trailing commas has several benefits: - - * It makes version-control diffs cleaner – as all the focus is on the changed value. - * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. - * It simplifies code generation, for example, for object initializers. The last element can also have a comma. - -Example: -```ini -[*.{kt,kts}] -ij_kotlin_allow_trailing_comma = false # Only used for declaration site -``` - -This setting only takes effect when rule `trailing-comma-on-declaration-site` is enabled. +## Rule specific configuration settings + +The configuration settings below are used to configure the behavior of a specific rule. As of that, those settings only take effect when the corresponding rule is enabled. See description of rule for more information about the setting. + +| Configuration setting | Rule | +|:------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------| +| ij_kotlin_allow_trailing_comma | [trailing-comma-on-declaration-site](../rules/standard/#trailing-comma-on-declaration-site) | +| ij_kotlin_allow_trailing_comma_on_call_site | [trailing-comma-on-call-site](../rules/standard/#trailing-comma-on-call-site) | +| ij_kotlin_packages_to_use_import_on_demand | [no-wildcard-imports](../rules/standard/#no-wildcard-imports) | +| indent_size | [indent](../rules/standard/#indentation) | | +| indent_style | [indent](../rules/standard/#indentation) | | + | +| insert_final_newline | [final-newline](../rules/standard/#final-newline) | | +| ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than | [chain-method-continuation](../rules/experimental/#chain-method-continuation) | +| ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [class-signature](../rules/experimental/#class-signature) | +| ktlint_ignore_back_ticked_identifier | [max-line-length](../rules/standard/#max-line-length) | +| ktlint_function_naming_ignore_when_annotated_with | [function-naming](../rules/standard/#function-naming) | +| ktlint_function_signature_body_expression_wrapping | [function-signature](../rules/standard/#function-signature) | +| ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [function-signature](../rules/standard/#function-signature) | +| max_line_length | [max-line-length](../rules/standard/#max-line-length) and several other rules | ## Overriding Editorconfig properties for specific directories @@ -266,4 +71,4 @@ ktlint_standard_import-ordering = disabled ktlint_standard_indent = disabled ``` -Note that the `import-ordering` rule is disabled for *all* packages including the `api` sub package. Next to this the `indent` rule is disabled for the `api` package and its sub packages. +Note that in example above the `import-ordering` rule is disabled for *all* packages including the `api` sub package. Next to this the `indent` rule is disabled for the `api` package and its sub packages. diff --git a/documentation/snapshot/docs/rules/experimental.md b/documentation/snapshot/docs/rules/experimental.md index c40dcc7524..28730e37ac 100644 --- a/documentation/snapshot/docs/rules/experimental.md +++ b/documentation/snapshot/docs/rules/experimental.md @@ -125,6 +125,11 @@ This rule can be configured with `.editorconfig` property [`ktlint_chain_method_ .bar() ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than`
Force wrapping of chained methods in case an expression contains at least the specified number of chain operators. If a chained method contains nested expressions, the chain operators of the inner expression are not taken into account. Use value `unset` (default) to disable this setting. | 4 | 4 | 4 | + + Rule id: `chain-method-continuation` (`standard` rule set) !!! Note @@ -331,6 +336,10 @@ The other code styles allow an infinite amount of parameters on the same line (a class Foo6(a: Any, b: Any, c: Any) : FooBar(a, c) ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than`
Force wrapping of the parameters of the class signature in case it contains at least the specified number of parameters, even in case the entire class signature would fit on a single line. Use value `unset` to disable this setting. | 1 | `unset` | `unset` | + Rule id: `class-signature` (`standard` rule set) ## Condition wrapping diff --git a/documentation/snapshot/docs/rules/standard.md b/documentation/snapshot/docs/rules/standard.md index 1025073d08..375b5c35d3 100644 --- a/documentation/snapshot/docs/rules/standard.md +++ b/documentation/snapshot/docs/rules/standard.md @@ -164,15 +164,15 @@ Rule id: `filename` (`standard` rule set) Ensures consistent usage of a newline at the end of each file. -This rule can be configured with `.editorconfig` property [`insert_final_newline`](../configuration-ktlint/#final-newline). +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:----------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `insert_final_newline` | `true` | `true` | `true` | Rule id: `final-newline` (`standard` rule set) ## Function signature -Rewrites the function signature to a single line when possible (e.g. when not exceeding the `max_line_length` property) or a multiline signature otherwise. In case of function with a body expression, the body expression is placed on the same line as the function signature when not exceeding the `max_line_length` property. - -In `ktlint-official` code style, a function signature is always rewritten to a multiline signature in case the function has 2 or more parameters. This number of parameters can be set via `.editorconfig` property `ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than`. +Rewrites the function signature to a single line when possible (e.g. when not exceeding the `max_line_length` property) or a multiline signature otherwise. !!! note Wrapping of parameters is also influenced by the `parameter-list-wrapping` rule. @@ -244,6 +244,84 @@ In `ktlint-official` code style, a function signature is always rewritten to a m .uppercase() ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_function_signature_body_expression_wrapping`
Determines how to wrap the body of function in case it is an expression. Use `default` to wrap the body expression only when the first line of the expression does not fit on the same line as the function signature. Use `multiline` to force wrapping of body expressions that consists of multiple lines. Use `always` to force wrapping of body expression always. | `multiline` | `default` | `default` | +| `ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than`
Forces a multiline function signature in case the function contains the specified minimum number of parameters even in case the function signature would fit on a single line. Use value `unset` (default) to disable this setting. | 2 | `unset` | `unset` | + +=== "[:material-heart:](#) default" + + When `ktlint_function_signature_body_expression_wrapping` is set to `default`, the first line of a body expression is appended to the function signature as long as the max line length is not exceeded. + + ```kotlin title="ktlint_function_signature_body_expression_wrapping=default" + // Given that the function signature has to be written as a single line + // function signature and that the function has a multiline body expression + fun someFunction(a: Any, b: Any): String = "some-result" + .uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a multiline body expression + fun someFunction( + a: Any, + b: Any + ): String = "some-result" + .uppercase() + ``` + +=== "[:material-heart:](#) multiline" + + When `ktlint_function_signature_body_expression_wrapping` is set to `multiline`, the body expression starts on a separate line in case it is a multiline expression. A single line body expression is wrapped only when it does not fit on the same line as the function signature. + + ```kotlin title="ktlint_function_signature_body_expression_wrapping=multiline" + // Given that the function signature has to be written as a single line + // function signature and that the function has a single line body expression + // that fits on the same line as the function signature. + fun someFunction(a: Any, b: Any): String = "some-result".uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a single line body expression + // that fits on the same line as the function signature. + fun someFunction( + a: Any, + b: Any + ): String = "some-result".uppercase() + + // Given that the function signature has to be written as a single line + // function signature and that the function has a multiline body expression + fun someFunction(a: Any, b: Any): String = + "some-result" + .uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a multiline body expression + fun someFunction( + a: Any, + b: Any + ): String = + "some-result" + .uppercase() + ``` + +=== "[:material-heart:](#) always" + + When `ktlint_function_signature_body_expression_wrapping` is set to `always` the body expression is always wrapped to a separate line. + + ```kotlin title="ktlint_function_signature_body_expression_wrapping=always" + // Given that the function signature has to be written as a single line + // function signature and that the function has a single line body expression + fun someFunction(a: Any, b: Any): String = + "some-result".uppercase() + + // Given that the function signature has to be written as a multiline + // function signature and that the function has a multiline body expression + fun functionWithAVeryLongName( + a: Any, + b: Any + ): String = + "some-result" + .uppercase() + ``` + Rule id: `function-signature` (`standard` rule set) ## If else bracing @@ -335,6 +413,11 @@ Indentation formatting - respects `.editorconfig` `indent_size` with no continua !!! note This rule handles indentation for many different language constructs which can not be summarized with a few examples. See the [unit tests](https://github.com/pinterest/ktlint/blob/master/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt) for more details. +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `indent_size`
The size of an indentation level when `indent_style` is set to `space`. Use value `unset` to ignore indentation. | 4 | 4 | 4 | +| `indent_style`
Style of indentation. Set this value to `space` or `tab`. | `space` | `space` | `space` | + Rule id: `indent` (`standard` rule set) ## Naming @@ -411,11 +494,15 @@ Enforce naming of function. fun do_something() {} ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_function_naming_ignore_when_annotated_with`
Ignore functions that are annotated with values in this setting. This value is a comma separated list of names without the '@' prefix. | `unset` | `unset` | `unset` | + !!! note - When using Compose, you might want to suppress the `function-naming` rule by setting `.editorconfig` property `ktlint_function_naming_ignore_when_annotated_with=Composable`. Furthermore, you can use a dedicated ktlint ruleset like [Compose Rules](https://mrmans0n.github.io/compose-rules/ktlint/) for checking naming conventions for Composable functions. + When using Compose, you might want to configure the `function-naming` rule with `.editorconfig` property `ktlint_function_naming_ignore_when_annotated_with=Composable`. Furthermore, you can use a dedicated ktlint ruleset like [Compose Rules](https://mrmans0n.github.io/compose-rules/ktlint/) for checking naming conventions for Composable functions. !!! note - Functions in files which import a class from package `org.junit`, `org.testng` or `kotlin.test` are considered to be test functions. Functions in such classes are allowed to have underscores in the name. Also, function names enclosed between backticks do not need to adhere to the normal naming convention. + Functions in files which import a class from package `io.kotest`, `junit.framework`, `kotlin.test`, `org.junit`, or `org.testng` are considered to be test functions. Functions in such classes are allowed to have underscores in the name. Also, function names enclosed between backticks do not need to adhere to the normal naming convention. This rule can also be suppressed with the IntelliJ IDEA inspection suppression `FunctionName`. @@ -819,7 +906,7 @@ Rule id: `ktlint-suppression` (`standard` rule set) ## Max line length -Ensures that lines do not exceed the given length of `.editorconfig` property `max_line_length` (see [EditorConfig](../configuration-ktlint/) section for more). This rule does not apply in a number of situations. For example, in the case a line exceeds the maximum line length due to a comment that disables ktlint rules then that comment is being ignored when validating the length of the line. The `.editorconfig` property `ktlint_ignore_back_ticked_identifier` can be set to ignore identifiers which are enclosed in backticks, which for example is very useful when you want to allow longer names for unit tests. +Ensures that lines do not exceed the maximum length of a line. This rule does not apply in a number of situations. The `.editorconfig` property `ktlint_ignore_back_ticked_identifier` can be set to ignore identifiers which are enclosed in backticks, which for example is very useful when you want to allow longer names for unit tests. === "[:material-heart:](#) Ktlint" @@ -851,6 +938,12 @@ Ensures that lines do not exceed the given length of `.editorconfig` property `m "foooooooooooooooooooooooooooooooooooooooo" ``` + +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:--------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ktlint_ignore_back_ticked_identifier`
Defines whether the backticked identifier (``) should be ignored. | `false` | `false` | `false` | +| `max_line_length`
Maximum length of a (regular) line. | 140 | `off` | `100` | + Rule id: `max-line-length` (`standard` rule set) ## Modifier order @@ -1172,7 +1265,7 @@ Rule id: `no-unused-imports` (`standard` rule set) ## No wildcard imports -No wildcard imports except imports listed in `.editorconfig` property `ij_kotlin_packages_to_use_import_on_demand`. +No wildcard imports except whitelisted imports. === "[:material-heart:](#) Ktlint" @@ -1186,6 +1279,10 @@ No wildcard imports except imports listed in `.editorconfig` property `ij_kotlin import foobar.* ``` +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:------------------------------------------------------------------------------------------|:---------------:|:------------------------------------------:|:-----------------------------------------------:| +| `ij_kotlin_packages_to_use_import_on_demand`
Defines allowed wildcard imports. | - | `java.util.*,`
`kotlinx.android.synthetic.**` | `java.util.*,`
`kotlinx.android.synthetic.**` | + !!! warning In case property `ij_kotlin_packages_to_use_import_on_demand` is not explicitly set, it allows wildcards imports like `java.util.*` by default to keep in sync with IntelliJ IDEA behavior. To disallow *all* wildcard imports, add property below to your `.editorconfig`: ```editorconfig @@ -1193,6 +1290,21 @@ No wildcard imports except imports listed in `.editorconfig` property `ij_kotlin ij_kotlin_packages_to_use_import_on_demand = unset ``` +Configuration setting `ij_kotlin_packages_to_use_import_on_demand` is a comma separated string of import paths. This can be a full path, e.g. "java.util.List.*", or a wildcard path, e.g. "kotlin.**". Use "**" as wildcard for package and all subpackages. + +The layout can be composed by the following symbols: + +* `*` - 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. + +Examples: +```kotlin +ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines +ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list +ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout +``` + Rule id: `no-wildcard-imports` (`standard` rule set) ## Spacing @@ -1932,9 +2044,6 @@ Rule id: `string-template-indent` (`standard` rule set) Consistent removal (default) or adding of trailing commas on call site. -!!! important - KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma_on_call_site` to configure the rule. When this property is enabled, KtLint *enforces* the usage of the trailing comma at call site while IntelliJ IDEA default formatter only *allows* to use the trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - === "[:material-heart:](#) Ktlint" ```kotlin @@ -1956,17 +2065,18 @@ Consistent removal (default) or adding of trailing commas on call site. ),) // it's weird to insert "," between unwrapped (continued) parenthesis ``` -!!! note - In KtLint 0.48.x the default value for using the trailing comma on call site has been changed to `true` except when codestyle `android` is used. +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ij_kotlin_allow_trailing_comma_on_call_site`
Defines whether a trailing comma (or no trailing comma) should be enforced on the calling site, e.g. argument-list, when-entries, lambda-arguments, indices, etc. When set, IntelliJ IDEA uses this property to allow usage of a trailing comma by discretion of the developer. KtLint however uses this setting to enforce consistent usage of the trailing comma when set. | `true` | `true` | `false` | +!!! note Although the [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) leaves it to the developer's discretion to use trailing commas on the call site, it also states that usage of trailing commas has several benefits: * It makes version-control diffs cleaner – as all the focus is on the changed value. * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. * It simplifies code generation, for example, for object initializers. The last element can also have a comma. -!!! note - Trailing comma on call site is automatically disabled if the [Wrapping](#wrapping) rule (or, before version `0.45.0`, the [Indentation](#indentation) rule) is disabled or not loaded. Because it cannot provide proper formatting with unwrapped calls. (see [dependencies](./dependencies.md)). + KtLint values *consistent* formatting more than a per-situation decision, and therefore uses this setting to *enforce/disallow* usage of trailing comma's on the calling site. Rule id: `trailing-comma-on-call-site` (`standard` rule set) @@ -1974,9 +2084,6 @@ Rule id: `trailing-comma-on-call-site` (`standard` rule set) Consistent removal (default) or adding of trailing commas on declaration site. -!!! important - KtLint uses the IntelliJ IDEA `.editorconfig` property `ij_kotlin_allow_trailing_comma` to configure the rule. When this property is enabled, KtLint *enforces* the usage of the trailing comma at declaration site while IntelliJ IDEA default formatter only *allows* to use the trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values *consistent* formatting more than a per-situation decision. - === "[:material-heart:](#) Ktlint" ```kotlin @@ -1996,17 +2103,18 @@ Consistent removal (default) or adding of trailing commas on declaration site. ),) // it's weird to insert "," between unwrapped (continued) parenthesis ``` -!!! note - In KtLint 0.48.x the default value for using the trailing comma on declaration site has been changed to `true` except when codestyle `android` is used. +| Configuration setting | ktlint_official | intellij_idea | android_studio | +|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:| +| `ij_kotlin_allow_trailing_comma`
Defines whether a trailing comma (or no trailing comma) should be enforced on the defining site, e.g. parameter-list, type-argument-list, lambda-value-parameters, enum-entries, etc. When set, IntelliJ IDEA uses this property to allow usage of a trailing comma by discretion of the developer. KtLint however uses this setting to enforce consistent usage of the trailing comma when set. | `true` | `true` | `false` | +!!! note The [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html#trailing-commas) encourages the usage of trailing commas on the declaration site, but leaves it to the developer's discretion to use trailing commas on the call site. But next to this, it also states that usage of trailing commas has several benefits: * It makes version-control diffs cleaner – as all the focus is on the changed value. * It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements. * It simplifies code generation, for example, for object initializers. The last element can also have a comma. -!!! note - Trailing comma on declaration site is automatically disabled if the [Wrapping](#wrapping) rule (or, before version `0.45.0`, the [Indentation](#indentation) rule) is disabled or not loaded. Because it cannot provide proper formatting with unwrapped declarations. (see [dependencies](./dependencies.md)). + KtLint values *consistent* formatting more than a per-situation decision, and therefore uses this setting to *enforce/disallow* usage of trailing comma's in declarations. Rule id: `trailing-comma-on-declaration-site` (`standard` rule set) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt index 5ddf6d1bdd..ba52fbe4b0 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt @@ -488,7 +488,7 @@ public class ChainMethodContinuationRule : type = PropertyType.LowerCasingPropertyType( "ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than", - "Force wrapping of chained methods in case and expression contains at least the specified number of chain " + + "Force wrapping of chained methods in case an expression contains at least the specified number of chain " + "operators. By default this parameter is set to 4.", PropertyType.PropertyValueParser.POSITIVE_INT_VALUE_PARSER, setOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "unset"), diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt index 96a27a96b4..30f06b9de0 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt @@ -689,9 +689,9 @@ public class ClassSignatureRule : type = PropertyType.LowerCasingPropertyType( "ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than", - "Force wrapping the parameters of the class signature in case it contains at least the specified " + - "number of parameters even in case the entire Class signature would fit on a single line. " + - "By default this parameter is not enabled.", + "Force wrapping of the parameters of the class signature in case it contains at least the specified " + + "number of parameters, even in case the entire class signature would fit on a single line. " + + "Use value 'unset' to disable this setting.", PropertyType.PropertyValueParser.POSITIVE_INT_VALUE_PARSER, setOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "unset"), ), From 784263faadd7d69a9a27c327f75e3693bc5218b9 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Fri, 5 Jan 2024 17:35:27 +0100 Subject: [PATCH 08/17] Fix links in documentation (#2490) --- .../docs/rules/configuration-ktlint.md | 30 ++++++++--------- .../docs/rules/configuration-ktlint.md | 32 +++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/documentation/release-latest/docs/rules/configuration-ktlint.md b/documentation/release-latest/docs/rules/configuration-ktlint.md index 3c83ea787e..aa06850cb9 100644 --- a/documentation/release-latest/docs/rules/configuration-ktlint.md +++ b/documentation/release-latest/docs/rules/configuration-ktlint.md @@ -43,22 +43,22 @@ ktlint_custom-rule-set_custom-rule = disabled # Disables the `custom-rule` rule The configuration settings below are used to configure the behavior of a specific rule. As of that, those settings only take effect when the corresponding rule is enabled. See description of rule for more information about the setting. -| Configuration setting | Rule | -|:------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------| -| ij_kotlin_allow_trailing_comma | [trailing-comma-on-declaration-site](../rules/standard/#trailing-comma-on-declaration-site) | -| ij_kotlin_allow_trailing_comma_on_call_site | [trailing-comma-on-call-site](../rules/standard/#trailing-comma-on-call-site) | -| ij_kotlin_packages_to_use_import_on_demand | [no-wildcard-imports](../rules/standard/#no-wildcard-imports) | -| indent_size | [indent](../rules/standard/#indentation) | | -| indent_style | [indent](../rules/standard/#indentation) | | +| Configuration setting | Rule | +|:------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------| +| ij_kotlin_allow_trailing_comma | [trailing-comma-on-declaration-site](../standard/#trailing-comma-on-declaration-site) | +| ij_kotlin_allow_trailing_comma_on_call_site | [trailing-comma-on-call-site](../standard/#trailing-comma-on-call-site) | +| ij_kotlin_packages_to_use_import_on_demand | [no-wildcard-imports](../standard/#no-wildcard-imports) | +| indent_size | [indent](../standard/#indentation) | | +| indent_style | [indent](../standard/#indentation) | | | -| insert_final_newline | [final-newline](../rules/standard/#final-newline) | | -| ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than | [chain-method-continuation](../rules/experimental/#chain-method-continuation) | -| ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [class-signature](../rules/experimental/#class-signature) | -| ktlint_ignore_back_ticked_identifier | [max-line-length](../rules/standard/#max-line-length) | -| ktlint_function_naming_ignore_when_annotated_with | [function-naming](../rules/standard/#function-naming) | -| ktlint_function_signature_body_expression_wrapping | [function-signature](../rules/standard/#function-signature) | -| ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [function-signature](../rules/standard/#function-signature) | -| max_line_length | [max-line-length](../rules/standard/#max-line-length) and several other rules | +| insert_final_newline | [final-newline](../standard/#final-newline) | | +| ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than | [chain-method-continuation](../experimental/#chain-method-continuation) | +| ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [class-signature](../experimental/#class-signature) | +| ktlint_ignore_back_ticked_identifier | [max-line-length](../standard/#max-line-length) | +| ktlint_function_naming_ignore_when_annotated_with | [function-naming](../standard/#function-naming) | +| ktlint_function_signature_body_expression_wrapping | [function-signature](../standard/#function-signature) | +| ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [function-signature](../standard/#function-signature) | +| max_line_length | [max-line-length](../standard/#max-line-length) and several other rules | ## Overriding Editorconfig properties for specific directories diff --git a/documentation/snapshot/docs/rules/configuration-ktlint.md b/documentation/snapshot/docs/rules/configuration-ktlint.md index 3c83ea787e..304004fdce 100644 --- a/documentation/snapshot/docs/rules/configuration-ktlint.md +++ b/documentation/snapshot/docs/rules/configuration-ktlint.md @@ -43,22 +43,22 @@ ktlint_custom-rule-set_custom-rule = disabled # Disables the `custom-rule` rule The configuration settings below are used to configure the behavior of a specific rule. As of that, those settings only take effect when the corresponding rule is enabled. See description of rule for more information about the setting. -| Configuration setting | Rule | -|:------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------| -| ij_kotlin_allow_trailing_comma | [trailing-comma-on-declaration-site](../rules/standard/#trailing-comma-on-declaration-site) | -| ij_kotlin_allow_trailing_comma_on_call_site | [trailing-comma-on-call-site](../rules/standard/#trailing-comma-on-call-site) | -| ij_kotlin_packages_to_use_import_on_demand | [no-wildcard-imports](../rules/standard/#no-wildcard-imports) | -| indent_size | [indent](../rules/standard/#indentation) | | -| indent_style | [indent](../rules/standard/#indentation) | | - | -| insert_final_newline | [final-newline](../rules/standard/#final-newline) | | -| ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than | [chain-method-continuation](../rules/experimental/#chain-method-continuation) | -| ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [class-signature](../rules/experimental/#class-signature) | -| ktlint_ignore_back_ticked_identifier | [max-line-length](../rules/standard/#max-line-length) | -| ktlint_function_naming_ignore_when_annotated_with | [function-naming](../rules/standard/#function-naming) | -| ktlint_function_signature_body_expression_wrapping | [function-signature](../rules/standard/#function-signature) | -| ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [function-signature](../rules/standard/#function-signature) | -| max_line_length | [max-line-length](../rules/standard/#max-line-length) and several other rules | +| Configuration setting | Rule | +|:------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------| +| ij_kotlin_allow_trailing_comma | [trailing-comma-on-declaration-site](../standard/#trailing-comma-on-declaration-site) | +| ij_kotlin_allow_trailing_comma_on_call_site | [trailing-comma-on-call-site](../standard/#trailing-comma-on-call-site) | +| ij_kotlin_packages_to_use_import_on_demand | [no-wildcard-imports](../standard/#no-wildcard-imports) | +| indent_size | [indent](../standard/#indentation) | | +| indent_style | [indent](../standard/#indentation) | | +| +| insert_final_newline | [final-newline](../standard/#final-newline) | | +| ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than | [chain-method-continuation](../experimental/#chain-method-continuation) | +| ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [class-signature](../experimental/#class-signature) | +| ktlint_ignore_back_ticked_identifier | [max-line-length](../standard/#max-line-length) | +| ktlint_function_naming_ignore_when_annotated_with | [function-naming](../standard/#function-naming) | +| ktlint_function_signature_body_expression_wrapping | [function-signature](../standard/#function-signature) | +| ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [function-signature](../standard/#function-signature) | +| max_line_length | [max-line-length](../standard/#max-line-length) and several other rules | ## Overriding Editorconfig properties for specific directories From 6a6cc1713ee159db06904720da92864dd4caa319 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sat, 6 Jan 2024 21:09:57 +0100 Subject: [PATCH 09/17] Resolve conflict between parameter-list-spacing and parameter-list-wrapping (#2491) * Resolve conflict between parameter-list-spacing and parameter-list-wrapping Closes #2488 --- .../api/ktlint-ruleset-standard.api | 1 + .../rules/ParameterListSpacingRule.kt | 42 ++++++++++++++++++- .../rules/ParameterListSpacingRuleTest.kt | 29 +++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api index 309354f79c..c549fb5949 100644 --- a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api +++ b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api @@ -626,6 +626,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/PackageNameRuleKt public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V + public fun beforeFirstNode (Lcom/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfig;)V public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRule.kt index 40817d11de..f16ca6a585 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRule.kt @@ -13,7 +13,11 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE import com.pinterest.ktlint.rule.engine.core.api.children +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline +import com.pinterest.ktlint.rule.engine.core.api.lineLengthWithoutNewlinePrefix import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.nextSibling @@ -32,7 +36,18 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement */ @SinceKtlint("0.46", EXPERIMENTAL) @SinceKtlint("1.0", STABLE) -public class ParameterListSpacingRule : StandardRule("parameter-list-spacing") { +public class ParameterListSpacingRule : + StandardRule( + id = "parameter-list-spacing", + usesEditorConfigProperties = + setOf(MAX_LINE_LENGTH_PROPERTY), + ) { + private var maxLineLength = MAX_LINE_LENGTH_PROPERTY.defaultValue + + override fun beforeFirstNode(editorConfig: EditorConfig) { + maxLineLength = editorConfig[MAX_LINE_LENGTH_PROPERTY] + } + override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, @@ -201,6 +216,13 @@ public class ParameterListSpacingRule : StandardRule("parameter-list-spacing") { // Bar, // ) Unit + } else if (whiteSpaceAfterColon.hasTypeReferenceWhichDoesNotFitOnSameLineAsColon()) { + // Allow the type to be wrapped to the next line when the type does not fit on same line as colon: + // class Foo( + // val someReallyLongFieldNameUsedInMyClass: + // SomeReallyLongDependencyClass + // ) + Unit } else if (whiteSpaceAfterColon.isNotSingleSpace()) { replaceWithSingleSpace(whiteSpaceAfterColon, emit, autoCorrect) } @@ -270,6 +292,24 @@ public class ParameterListSpacingRule : StandardRule("parameter-list-spacing") { this ?.findChildByType(TYPE_REFERENCE) ?.findChildByType(MODIFIER_LIST) + + private fun ASTNode.hasTypeReferenceWhichDoesNotFitOnSameLineAsColon() = + takeIf { it.isWhiteSpaceWithNewline() } + ?.nextCodeSibling() + ?.takeIf { it.elementType == TYPE_REFERENCE } + ?.let { typeReference -> + val length = + // length of previous line + lineLengthWithoutNewlinePrefix() + + // single space before type reference + 1 - + // length of current indent before typeReference + this.text.substringAfterLast("\n").length + + // length of line containing typeReference + typeReference.lineLengthWithoutNewlinePrefix() + length > maxLineLength + } + ?: false } public val PARAMETER_LIST_SPACING_RULE_ID: RuleId = ParameterListSpacingRule().ruleId diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRuleTest.kt index ec6b2788ae..c78ad85c5d 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRuleTest.kt @@ -1,5 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.test.KtLintAssertThat.Companion.EOL_CHAR +import com.pinterest.ktlint.test.KtLintAssertThat.Companion.MAX_LINE_LENGTH_MARKER import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule import com.pinterest.ktlint.test.LintViolation import org.junit.jupiter.api.Test @@ -479,6 +481,33 @@ class ParameterListSpacingRuleTest { parameterListSpacingRuleAssertThat(code).hasNoLintViolations() } + @Test + fun `Issue 2488 - Given a parameter with type reference which does not fit on a single line`() { + val code = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + class Foo( + val foooooooooooo: + Foooooooooooo, + val fooooooooooooX: + Foooooooooooo, + ) + """.trimIndent() + val formattedCode = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + class Foo( + val foooooooooooo: Foooooooooooo, + val fooooooooooooX: + Foooooooooooo, + ) + """.trimIndent() + parameterListSpacingRuleAssertThat(code) + .setMaxLineLength() + .hasLintViolation(3, 23, "Expected a single space") + .isFormattedAs(formattedCode) + } + private companion object { const val TOO_MANY_SPACES = " " } From 67572ea25042150058431d1eaa7d1a95cf1d04c0 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sun, 7 Jan 2024 15:26:04 +0100 Subject: [PATCH 10/17] Update release testing procedure --- RELEASE_TESTING.MD | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RELEASE_TESTING.MD b/RELEASE_TESTING.MD index 9bf9300973..051bf22d36 100644 --- a/RELEASE_TESTING.MD +++ b/RELEASE_TESTING.MD @@ -40,7 +40,7 @@ Before releasing a new version of KtLint, the release candidate is tested on a s ``` 4. Create an alias or script to run the latest released version of ktlint (note that this script will print the version as reference to which version is used): ```shell - alias ktlint-prev="ktlint-0.50.0 $@" # Replace with the latest release version + alias ktlint-prev="ktlint-1.1.0 $@" # Replace with the latest release version ``` Note that `~/git/ktlint` is the directory in which the ktlint project is checked out and that `~/git/ktlint/ktlint` refers to the `ktlint` CLI-module. 5. Create an alias or script to run the latest development-version of ktlint (note that this script will print the version and the datetime of compilation as reference to which version is used): @@ -112,8 +112,8 @@ Formatting projects in which ktlint is not used may result in a huge amount of f ```shell ./exec-in-each-project.sh "git add --all && git commit -m \"Format with previous ktlint version -F\"" ``` - Repeat step 3 and 4 until no files are changed anymore. Starting from 0.50, all changes should be resolved in one run as format internally reruns 3 times in case new violations are introduced which can be autocorrected as well. -5. Check that besides the `baseline.xml` no files are changed (in step 1 and 2 all violations which could be autocorrected have already been committed). Remaining violations which could not be autocorrected are saved in the `baseline.xml` which is stored outside the project directories. + Repeat step 3 and 4 until no files are changed anymore. Although ktlint reruns up to 3 times in case new violations are introduced, it can still happen that not all violations have been fixed with a single invocation. +5. Check that besides the `baseline.xml` no files are changed (in step 3 and 4 all violations which could be autocorrected have already been committed). Remaining violations which could not be autocorrected are saved in the `baseline.xml` which is stored outside the project directories. ```shell ./exec-in-each-project.sh "git status" ``` @@ -127,8 +127,8 @@ Formatting projects in which ktlint is not used may result in a huge amount of f ktlint-dev --baseline=baseline.xml --relative --reporter=plain-summary # Do not call this command via the "./exec-in-each-project.sh" script as we want to use the one combined baseline.xml file for all projects. ``` Inspect the output roughly (detailed inspection is done when formatting): - * Is the amount of logging messages comparable to before? If not, are the changes intended? - * Are violations related to rules that have actually been added or changed? + * Is the amount of logging messages comparable to before? If not, are the changes intended? + * Are violations related to rules that have actually been added or changed? 7. Format with *latest development* version: ```shell ktlint-dev -F --baseline=baseline.xml --relative # Do not call this command via the "./exec-in-each-project.sh" script as we want to use the one combined baseline.xml file for all projects. From d9d3e529f2cf76f9361586159d0c5babcb2b49d5 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sun, 7 Jan 2024 15:27:08 +0100 Subject: [PATCH 11/17] Do not wrap binary expression value argument if it is already preceded by a newline Closes #2492 --- .../rules/BinaryExpressionWrappingRule.kt | 17 ++++++++++++++++- .../rules/BinaryExpressionWrappingRuleTest.kt | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt index 204d9e59c6..5609e1a514 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule.kt @@ -108,9 +108,24 @@ public class BinaryExpressionWrappingRule : } } + // Prefer to wrap the entire binary expression to a newline instead of wrapping the binary expression at the operation reference. + // E.g. prefer: + // fooBar( + // "foooooo" + "bar", + // ) + // instead of + // fooBar("foooooo" + + // "bar") node .takeIf { it.treeParent.elementType == VALUE_ARGUMENT } - ?.takeIf { it.causesMaxLineLengthToBeExceeded() } + ?.takeUnless { + // Allow + // fooBar( + // "tooLongToFitOnSingleLine" + + // "bar", + // ) + node.prevLeaf().isWhiteSpaceWithNewline() + }?.takeIf { it.causesMaxLineLengthToBeExceeded() } ?.let { expression -> emit( expression.startOffset, diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt index 1a1b08cad2..8482a001b8 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleTest.kt @@ -441,4 +441,20 @@ class BinaryExpressionWrappingRuleTest { LintViolation(3, 59, "Line is exceeding max line length. Break line after 'returns' in binary expression"), ).isFormattedAs(formattedCode) } + + @Test + fun `Issue 2492 - Given a binary expression as value argument on a separate line`() { + val code = + """ + // $MAX_LINE_LENGTH_MARKER $EOL_CHAR + val foo = + foo( + "longgggggggggggggg" + + "foo", + ) + """.trimIndent() + binaryExpressionWrappingRuleAssertThat(code) + .setMaxLineLength() + .hasNoLintViolations() + } } From 86a8ac40fbec17c13d311d01af804c5f86e1cb45 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Mon, 8 Jan 2024 18:49:28 +0100 Subject: [PATCH 12/17] Prepare 1.1.1 release --- CHANGELOG.md | 40 +++++++++++++++++++++++++++++++++++----- RELEASE_TESTING.MD | 8 ++++---- gradle.properties | 2 +- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12ac9fa6c2..91321ca0f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,44 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). -## [Unreleased] +## [1.1.1] - 2024-01-08 -### Added +### 🆕 Features -### Removed +None -### Fixed +### 🔧 Fixes -### Changed + +* Fix incorrect generateEditorConfig example in documentation - [#2444](https://github.com/pinterest/ktlint/pull/2444), by @stay7 + +* Fix insert of suppression on binary expression - [#2463](https://github.com/pinterest/ktlint/pull/2463), by @paul-dingemans + +* Loosen dependency between chain-method-continuation and argument-list-wrapping - [#2468](https://github.com/pinterest/ktlint/pull/2468), by @paul-dingemans + +* Keep arrow when both parameter list and block of function literal are empty - [#2469](https://github.com/pinterest/ktlint/pull/2469), by @paul-dingemans + +* Improve wrapping of binary expressions - [#2479](https://github.com/pinterest/ktlint/pull/2479), by @paul-dingemans + +* Resolve conflict between parameter-list-spacing and parameter-list-wrapping - [#2491](https://github.com/pinterest/ktlint/pull/2491), by @paul-dingemans + +* Do not wrap binary expression value argument if it is already preceded by a newline - [#2493](https://github.com/pinterest/ktlint/pull/2493), by @paul-dingemans + +* Fix operator spacing - [#2473](https://github.com/pinterest/ktlint/pull/2473), by @paul-dingemans + +* Run `argument-list-wrapping`, `class-signature` and `function-signature` when comment rules are disabled - [#2466](https://github.com/pinterest/ktlint/pull/2466), by @paul-dingemans + +### 📦 Dependencies + +* fix(deps): update kotlin monorepo to v1.9.22 - [#2456](https://github.com/pinterest/ktlint/pull/2456), by @renovate[bot] + +* chore(deps): update actions/setup-python action to v5 - [#2417](https://github.com/pinterest/ktlint/pull/2417), by @renovate[bot] + +* fix(deps): update dependency org.slf4j:slf4j-simple to v2.0.10 - [#2470](https://github.com/pinterest/ktlint/pull/2470), by @renovate[bot] + +* fix(deps): update dependency dev.drewhamilton.poko:poko-gradle-plugin to v0.15.2 - [#2485](https://github.com/pinterest/ktlint/pull/2485), by @renovate[bot] + +* fix(deps): update dependency org.assertj:assertj-core to v3.25.1 - [#2486](https://github.com/pinterest/ktlint/pull/2486), by @renovate[bot] ## [1.1.0] - 2023-12-19 @@ -2272,6 +2301,7 @@ set in `[*{kt,kts}]` section). ## 0.1.0 - 2016-07-27 +[1.1.1]: https://github.com/pinterest/ktlint/compare/1.1.1...1.1.0 [1.1.0]: https://github.com/pinterest/ktlint/compare/1.0.1...1.1.0 [1.0.1]: https://github.com/pinterest/ktlint/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/pinterest/ktlint/compare/0.50.0...1.0.0 diff --git a/RELEASE_TESTING.MD b/RELEASE_TESTING.MD index 051bf22d36..c831c3ab5f 100644 --- a/RELEASE_TESTING.MD +++ b/RELEASE_TESTING.MD @@ -134,13 +134,13 @@ Formatting projects in which ktlint is not used may result in a huge amount of f ktlint-dev -F --baseline=baseline.xml --relative # Do not call this command via the "./exec-in-each-project.sh" script as we want to use the one combined baseline.xml file for all projects. ``` Inspect the output carefully: - * If you see an error like below, then this version obviously may *not* be released. It is best to fix this error before continuing with testing and validating! + * If you see an error like below, then this version obviously may *not* be released. It is best to fix this error before continuing with testing and validating! ```plain Internal Error (...) in file '...' at position '0:0. Please create a ticket at https://github.com/pinterest/ktlint/issues ... ``` - * Ideally, no violations are shown. This means that all violations have been autocorrected. - * Violations which could not be autocorrected should be validated for correctness but do not block the release as most likely this is intended behavior. - * If a violation is shown which is not marked as being "can not be autocorrected" this means that during autocorrect of another violation a new violations has been introduced. This should be fixed before releasing especially when the next format introduces the original violation again which of course would result in an endless loop. + * Ideally, no violations are shown. This means that all violations have been autocorrected. + * Violations which could not be autocorrected should be validated for correctness but do not block the release as most likely this is intended behavior. + * If a violation is shown which is not marked as being "can not be autocorrected" this means that during autocorrect of another violation a new violations has been introduced. This should be fixed before releasing especially when the next format introduces the original violation again which of course would result in an endless loop. 8. Inspect all fixed violations, Of course inspection similar violations tens of times does not make sense. At least check different types of violations a couple of times. Commit changes which do not need to be inspected again: ```shell ./exec-in-each-project.sh "git add --all && git commit -m \"Fixed with latest development version\"" diff --git a/gradle.properties b/gradle.properties index b34aebc2eb..e8a86d2e4a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.2.0-SNAPSHOT +VERSION_NAME=1.1.1 POM_GROUP_ID=com.pinterest.ktlint POM_DESCRIPTION=An anti-bikeshedding Kotlin linter with built-in formatter. From a573af8281b89d75bf55161bb8aed5e69ddb0db2 Mon Sep 17 00:00:00 2001 From: Ktlint Release Workflow <> Date: Mon, 8 Jan 2024 18:08:36 +0000 Subject: [PATCH 13/17] Updated refs to latest (1.1.1) release --- documentation/release-latest/docs/install/cli.md | 4 ++-- documentation/release-latest/docs/install/integrations.md | 6 +++--- .../release-latest/docs/rules/configuration-ktlint.md | 2 +- documentation/snapshot/docs/install/cli.md | 4 ++-- documentation/snapshot/docs/install/integrations.md | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/documentation/release-latest/docs/install/cli.md b/documentation/release-latest/docs/install/cli.md index 1ca18d2568..3cd56c7f00 100644 --- a/documentation/release-latest/docs/install/cli.md +++ b/documentation/release-latest/docs/install/cli.md @@ -12,7 +12,7 @@ All releases of `ktlint` can be downloaded from the [releases](https://github.co A particular version of `ktlint` can be downloaded with next command which also changes the file to an executable in directory `/usr/local/bin`: ```sh title="Download" -curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.1.0/ktlint && chmod a+x ktlint && sudo mv ktlint /usr/local/bin/ +curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.1.1/ktlint && chmod a+x ktlint && sudo mv ktlint /usr/local/bin/ ``` !!! tip "Curl not installed or behind proxy" @@ -209,6 +209,6 @@ Options `--stdin` and `--patterns-from-stdin` are mutually exclusive, only one o Microsoft Windows is not able to run the `ktlint` command directly. Ktlint can be run in following ways on Microsoft Windows: -1. Use the `ktlint.bat` batch file provided as part of the [release](https://github.com/pinterest/ktlint/releases/tag/1.1.0). Add the batch file to your `%PATH%` environment variable for easy access +1. Use the `ktlint.bat` batch file provided as part of the [release](https://github.com/pinterest/ktlint/releases/tag/1.1.1). Add the batch file to your `%PATH%` environment variable for easy access 2. Run `ktlint` using Git Bash 3. Run as `java -jar ktlint` diff --git a/documentation/release-latest/docs/install/integrations.md b/documentation/release-latest/docs/install/integrations.md index 4e22567f7e..8d455fc8c0 100644 --- a/documentation/release-latest/docs/install/integrations.md +++ b/documentation/release-latest/docs/install/integrations.md @@ -56,7 +56,7 @@ See [cli usage](../cli) for arguments that can be supplied to `ktlint`. com.pinterest.ktlint ktlint-cli - 1.1.0 + 1.1.1 @@ -117,7 +117,7 @@ configurations { } dependencies { - ktlint("com.pinterest.ktlint:ktlint-cli:1.1.0") { + ktlint("com.pinterest.ktlint:ktlint-cli:1.1.1") { attributes { attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL)) } @@ -167,7 +167,7 @@ The configuration below, defines following task: val ktlint by configurations.creating dependencies { - ktlint("com.pinterest.ktlint:ktlint-cli:1.1.0") { + ktlint("com.pinterest.ktlint:ktlint-cli:1.1.1") { attributes { attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) } diff --git a/documentation/release-latest/docs/rules/configuration-ktlint.md b/documentation/release-latest/docs/rules/configuration-ktlint.md index aa06850cb9..304004fdce 100644 --- a/documentation/release-latest/docs/rules/configuration-ktlint.md +++ b/documentation/release-latest/docs/rules/configuration-ktlint.md @@ -50,7 +50,7 @@ The configuration settings below are used to configure the behavior of a specifi | ij_kotlin_packages_to_use_import_on_demand | [no-wildcard-imports](../standard/#no-wildcard-imports) | | indent_size | [indent](../standard/#indentation) | | | indent_style | [indent](../standard/#indentation) | | - | +| | insert_final_newline | [final-newline](../standard/#final-newline) | | | ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than | [chain-method-continuation](../experimental/#chain-method-continuation) | | ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than | [class-signature](../experimental/#class-signature) | diff --git a/documentation/snapshot/docs/install/cli.md b/documentation/snapshot/docs/install/cli.md index 1ca18d2568..3cd56c7f00 100644 --- a/documentation/snapshot/docs/install/cli.md +++ b/documentation/snapshot/docs/install/cli.md @@ -12,7 +12,7 @@ All releases of `ktlint` can be downloaded from the [releases](https://github.co A particular version of `ktlint` can be downloaded with next command which also changes the file to an executable in directory `/usr/local/bin`: ```sh title="Download" -curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.1.0/ktlint && chmod a+x ktlint && sudo mv ktlint /usr/local/bin/ +curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.1.1/ktlint && chmod a+x ktlint && sudo mv ktlint /usr/local/bin/ ``` !!! tip "Curl not installed or behind proxy" @@ -209,6 +209,6 @@ Options `--stdin` and `--patterns-from-stdin` are mutually exclusive, only one o Microsoft Windows is not able to run the `ktlint` command directly. Ktlint can be run in following ways on Microsoft Windows: -1. Use the `ktlint.bat` batch file provided as part of the [release](https://github.com/pinterest/ktlint/releases/tag/1.1.0). Add the batch file to your `%PATH%` environment variable for easy access +1. Use the `ktlint.bat` batch file provided as part of the [release](https://github.com/pinterest/ktlint/releases/tag/1.1.1). Add the batch file to your `%PATH%` environment variable for easy access 2. Run `ktlint` using Git Bash 3. Run as `java -jar ktlint` diff --git a/documentation/snapshot/docs/install/integrations.md b/documentation/snapshot/docs/install/integrations.md index 4e22567f7e..8d455fc8c0 100644 --- a/documentation/snapshot/docs/install/integrations.md +++ b/documentation/snapshot/docs/install/integrations.md @@ -56,7 +56,7 @@ See [cli usage](../cli) for arguments that can be supplied to `ktlint`. com.pinterest.ktlint ktlint-cli - 1.1.0 + 1.1.1 @@ -117,7 +117,7 @@ configurations { } dependencies { - ktlint("com.pinterest.ktlint:ktlint-cli:1.1.0") { + ktlint("com.pinterest.ktlint:ktlint-cli:1.1.1") { attributes { attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL)) } @@ -167,7 +167,7 @@ The configuration below, defines following task: val ktlint by configurations.creating dependencies { - ktlint("com.pinterest.ktlint:ktlint-cli:1.1.0") { + ktlint("com.pinterest.ktlint:ktlint-cli:1.1.1") { attributes { attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) } From 8ee14f313dc0ceb83f9260b9f103148775593f07 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:04:16 +0000 Subject: [PATCH 14/17] fix(deps): update dependency org.slf4j:slf4j-simple to v2.0.11 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 952b87dea6..2398e2c6af 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ dokka = "org.jetbrains.dokka:dokka-gradle-plugin:1.9.10" ec4j = "org.ec4j.core:ec4j-core:0.3.0" picocli = "info.picocli:picocli:4.7.5" logging = "io.github.oshai:kotlin-logging-jvm:5.1.4" -slf4j = "org.slf4j:slf4j-simple:2.0.10" +slf4j = "org.slf4j:slf4j-simple:2.0.11" poko = "dev.drewhamilton.poko:poko-gradle-plugin:0.15.2" # Use logback-classic as the logger for kotlin-logging / slf4j as it allow changing the log level at runtime. # TODO: Update "renovate.json" once logback-classic is updated to 1.4 (once java8 support for ktlint is dropped) From 0c3f3024b948d52a4833a21cb9a39b29f6d400db Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Mon, 8 Jan 2024 21:13:05 +0100 Subject: [PATCH 15/17] Set 1.2.0-SNAPSHOT --- CHANGELOG.md | 10 ++++++++++ gradle.properties | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91321ca0f0..89149614db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## [Unreleased] + +### Added + +### Removed + +### Fixed + +### Changed + ## [1.1.1] - 2024-01-08 ### 🆕 Features diff --git a/gradle.properties b/gradle.properties index e8a86d2e4a..b34aebc2eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.1.1 +VERSION_NAME=1.2.0-SNAPSHOT POM_GROUP_ID=com.pinterest.ktlint POM_DESCRIPTION=An anti-bikeshedding Kotlin linter with built-in formatter. From 7354181dfbb5fa918f617421787c02e9c3f66349 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:54:05 +0000 Subject: [PATCH 16/17] chore(deps): update plugin org.gradle.toolchains.foojay-resolver-convention to v0.8.0 --- build-logic/settings.gradle.kts | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index a16890ba6a..7ad2edfa9f 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -7,5 +7,5 @@ dependencyResolutionManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/settings.gradle.kts b/settings.gradle.kts index a147144890..5e2d066865 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,7 +16,7 @@ dependencyResolutionManagement { plugins { `gradle-enterprise` - id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } gradleEnterprise { From 8f2af743d9cac6595e55f9e5ed68c7b6a660ca2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:29:27 +0000 Subject: [PATCH 17/17] fix(deps): update dependency io.github.oshai:kotlin-logging-jvm to v6 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2398e2c6af..32342f9121 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,7 @@ kotlin-plugin-dev = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", vers dokka = "org.jetbrains.dokka:dokka-gradle-plugin:1.9.10" ec4j = "org.ec4j.core:ec4j-core:0.3.0" picocli = "info.picocli:picocli:4.7.5" -logging = "io.github.oshai:kotlin-logging-jvm:5.1.4" +logging = "io.github.oshai:kotlin-logging-jvm:6.0.3" slf4j = "org.slf4j:slf4j-simple:2.0.11" poko = "dev.drewhamilton.poko:poko-gradle-plugin:0.15.2" # Use logback-classic as the logger for kotlin-logging / slf4j as it allow changing the log level at runtime.