diff --git a/documentation/snapshot/docs/api/custom-integration.md b/documentation/snapshot/docs/api/custom-integration.md index 51049a1578..668e35f5fa 100644 --- a/documentation/snapshot/docs/api/custom-integration.md +++ b/documentation/snapshot/docs/api/custom-integration.md @@ -4,26 +4,26 @@ The `Ktlint Rule Engine` is the central entry point for custom integrations with the `Ktlint API`. See [basic API Consumer](https://github.com/pinterest/ktlint/blob/master/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/KtlintApiConsumer.kt) for a basic example on how to invoke the `Ktlint Rule Engine`. This example also explains how the logging of the `Ktlint Rule Engine` can be configured to your needs. -The `KtLintRuleEngine` instance only needs to be created once for the entire lifetime of your application. Reusing the same instance results in better performance due to caching. +The `KtLintRuleEngine` instance only needs to be created once for the entire lifetime of your application. Reusing the same instance results in better performance due to caching. ```kotlin title="Creating the KtLintRuleEngine" val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, - ) + KtLintRuleEngine( + ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, + ) ``` ### Rule provider -The `KtLintRuleEngine` must be configured with at least one `RuleProvider`. A `RuleProvider` is a lambda which upon request of the `KtLintRuleEngine` provides a new instance of a specific rule. You can either provide any of the standard rules provided by KtLint or with your own custom rules, or with a combination of both. +The `KtLintRuleEngine` must be configured with at least one `RuleProvider`. A `RuleProvider` is a lambda which upon request of the `KtLintRuleEngine` provides a new instance of a specific rule. You can either provide any of the standard rules provided by KtLint, or your own custom rules, or a combination of both. ```kotlin title="Creating a set of RuleProviders" val KTLINT_API_CONSUMER_RULE_PROVIDERS = - setOf( - // Can provide custom rules - RuleProvider { NoVarRule() }, - // but also reuse rules from KtLint rulesets - RuleProvider { IndentationRule() }, - ) + setOf( + // Can provide custom rules + RuleProvider { NoVarRule() }, + // but also reuse rules from KtLint rulesets + RuleProvider { IndentationRule() }, + ) ``` ### Editor config: defaults & overrides @@ -32,29 +32,29 @@ When linting and formatting files, the `KtlintRuleEngine` takes the `.editorconf ```kotlin title="Specifying the editorConfigOverride" val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, - editorConfigOverride = EditorConfigOverride.from( - INDENT_STYLE_PROPERTY to IndentConfig.IndentStyle.SPACE, - INDENT_SIZE_PROPERTY to 4 - ) + KtLintRuleEngine( + ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, + editorConfigOverride = EditorConfigOverride.from( + INDENT_STYLE_PROPERTY to IndentConfig.IndentStyle.SPACE, + INDENT_SIZE_PROPERTY to 4 ) + ) ``` The `editorConfigOverride` property takes an `EditorConfigProperty` as key. KtLint defines several such properties, but they can also be defined as part of a custom rule. The `editorConfigDefaults` property is more cumbersome to define as it is based directly on the data format of the `ec4j` library which is used for parsing the `.editorconfig` file. -The defaults can be loaded from a path or a directory. If a path to a file is specified, the name of the file does not necessarily have to end with `.editorconfig`. If a path to a directory is specified, the directory should contain a file with name `.editorconfig`. Note that the `propertyTypes` have to be derived from the same collection of rule providers that are specified in the `ruleProviders` property of the `KtLintRuleEngine`. +The defaults can be loaded from a path or a directory. If a path to a file is specified, the name of the file does not necessarily have to end with `.editorconfig`. If a path to a directory is specified, the directory should contain a file with name `.editorconfig`. Note that the `propertyTypes` have to be derived from the same collection of rule providers that are specified in the `ruleProviders` property of the `KtLintRuleEngine`. ```kotlin title="Specifying the editorConfigDefaults using an '.editorconfig' file" val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, - editorConfigDefaults = EditorConfigDefaults.load( - path = Paths.get("/some/path/to/editorconfig/file/or/directory"), - propertyTypes = KTLINT_API_CONSUMER_RULE_PROVIDERS.propertyTypes(), - ) + KtLintRuleEngine( + ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, + editorConfigDefaults = EditorConfigDefaults.load( + path = Paths.get("/some/path/to/editorconfig/file/or/directory"), + propertyTypes = KTLINT_API_CONSUMER_RULE_PROVIDERS.propertyTypes(), + ) ) ``` If you want to include all RuleProviders of the Ktlint project than you can easily retrieve the collection using `StandardRuleSetProvider().getRuleProviders()`. @@ -63,20 +63,20 @@ The `EditorConfigDefaults` property can also be specified programmatically as is ```kotlin title="Specifying the editorConfigDefaults programmatically" val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, - editorConfigDefaults = EditorConfigDefaults( - org.ec4j.core.model.EditorConfig - .builder() - // .. add relevant properties - .build() - ) + KtLintRuleEngine( + ruleProviders = KTLINT_API_CONSUMER_RULE_PROVIDERS, + editorConfigDefaults = EditorConfigDefaults( + org.ec4j.core.model.EditorConfig + .builder() + // .. add relevant properties + .build() ) + ) ``` ### Lint & format -Once the `KtLintRuleEngine` has been defined, it is ready to be invoked for each file or code snippet that has to be linted or formatted. The the `lint` and `format` functions take a `Code` instance as parameter. Such an instance can either be created from a file +Once the `KtLintRuleEngine` has been defined, it is ready to be invoked for code that has to be linted or formatted. The `lint` and `format` functions take a `Code` instance as parameter. Such an instance can either be created from a file ```kotlin title="Code from file" val code = Code.fromFile( File("/some/path/to/file") @@ -91,23 +91,107 @@ val code = Code.fromSnippet( ) ``` -The `lint` function is invoked with a lambda which is called each time a `LintError` is found and does not return a result. -```kotlin title="Specifying the editorConfigDefaults programmatically" +The `lint` function is invoked with an optional lambda. Once linting is complete, the lambda will be called for each `LintError` which is found. +```kotlin title="Invoking lint" ktLintRuleEngine - .lint(codeFile) { lintError -> - // handle - } + .lint(code) { lintError -> + // handle + } ``` -The `format` function is invoked with a lambda which is called each time a `LintError` is found and returns the formatted code as result. Note that the `LintError` should be inspected for errors that could not be autocorrected. -```kotlin title="Specifying the editorConfigDefaults programmatically" +The `format` function is invoked with a lambda. The lambda is called for each `LintError` which is found. If the `LintError` can be autocorrected, the return value of the lambda instructs the rule whether this specific `LintError` is to be autocorrected, or not. If the `LintError` can not be autocorrected, the return result of the lambda is ignored. The formatted code is returned as result of the function. + +The new `format` function allows the API Consumer to decide which LintError is to be autocorrected, or not. This is most interesting for API Consumers that let their user interactively decide per `LintError` how it has to be handled. For example see the `ktlint-intellij-plugin` which in 'manual' mode displays all lint violations, which allows the user to decide which `LintError` is to be autocorrected. + +!!! note + The difference with the legacy version of the `format` is subtle. It takes two parameters (a `LintError` and `Boolean` denoting whether the `LintError` is corrected), and it does not return a value. + +```kotlin title="Invoke format (preferred, starting from Ktlint 1.3)" +val formattedCode = + ktLintRuleEngine + .format(code) { lintError -> + if (lintError.canBeAutoCorrected) { + // Return AutocorrectDecision.ALLOW_AUTOCORRECT to execute the autocorrect of this lintError if this is supported by the rule. + // Return AutocorrectDecision.NO_AUTOCORRECT if the LintError should not be corrected even if is supported by the rule. + } else { + // In case the LintError can not be autocorrected, the return value of the lambda will be ignored. + // For clarity reasons it is advised to return AutocorrectDecision.NO_AUTOCORRECT in case the LintError can not be autocorrected. + AutocorrectDecision.NO_AUTOCORRECT + } + } +``` + +!!! warning + Rules need to implement the interface `RuleAutocorrectApproveHandler` in order to let the API Consumer decide whether a `LintError` is to be autocorrected, or not. This interface is implemented for all rules provided via the Ktlint project starting from version 1.3. However, external rulesets may not have implemented this interface on their rulesets though. Contact the maintainer of such a ruleset to implement this interface. + +The (legacy) `format` function is invoked with an optional lambda. Once formatting is complete, the lambda will be called for each `LintError` which is found. The (legacy) `format` function fixes all `LintErrors` for which an autocorrect is available. The formatted code is returned as result of the function. + +```kotlin title="Invoke format (deprecated as of Ktlint 1.3, will be removed in Ktlint 2.0)" +// Up until Ktlint 1.2.1 the format was invoked with a lambda having two parameters and not returning a result. This function will be removed in Ktlint 2.0 val formattedCode = ktLintRuleEngine - .format(codeFile) { lintError -> - // handle + .format(code) { lintError, corrected -> + // handle } ``` +### Rule & RuleAutocorrectApproveHandler + +!!! note + Providers of custom rules are strongly encouraged to implement `RuleAutocorrectApproveHandler` interface as described below. The `ktlint-intellij-plugin`, which will be updated soon after the 1.3 release of Ktlint, make use of this new functionality. If your ruleset is used by users of the plugin, it is very likely that they want to be able to autocorrect individual `LintErrors` or to format a block of code (e.g. a selection) in a file. This functionality will only be available for rules that have implemented this interface. + +In Ktlint 1.3 the `RuleAutocorrectApproveHandler` interface is added. This interface adds the ability that the API Consumer decides per `LintError` whether it needs to autocorrected, or not. In Ktlint 2.0 the methods `beforeVisitChildNodes` and `afterVisitChildNodes` of the `Rule` class will be replaced with the new versions which are now added to the `RuleAutocorrectApproveHandler` interface as is shown below (the signature for `afterVisitChildNodes` is changed similarly): + + + + + + +
+ +```kotlin title="Deprecated signature in `Rule` class" +public open fun beforeVisitChildNodes( + node: ASTNode, + autoCorrect: Boolean, + emit: ( + offset: Int, + errorMessage: String, + canBeAutoCorrected: Boolean + ) -> Unit, +) +``` + + + +```kotlin title="New signature in `RuleAutocorrectApproveHandler` interface" +public fun beforeVisitChildNodes( + node: ASTNode, + emit: ( + offset: Int, + errorMessage: String, + canBeAutoCorrected: Boolean + ) -> AutocorrectDecision, +) +``` + +
+ +The `autoCorrect` parameter is no longer passed to the method. Instead, the `emit` lambda now returns the value `AutocorrectDecision.ALLOW_AUTOCORRECT` or `AutocorrectDecision.NO_AUTOCORRECT`. + +In case a `LintError` is detected, and can be autocorrected, the `LintError` can be processed as shown below: + +```kotlin +emit(node.startOffset, "some detail message", true) + .ifAutocorrectAllowed { + // Autocorrect the LintError + } +``` + +In case the `LintError` can not be autocorrected, if suffices to emit the violation only: +```kotlin +emit(node.startOffset, "some detail message", false) +``` + ## Logging Ktlint uses the `io.github.oshai:kotlin-logging` which is a `slf4j` wrapper. As API consumer you can choose which logging framework you want to use and configure that framework to your exact needs. The [basic API Consumer](https://github.com/pinterest/ktlint/blob/master/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/KtlintApiConsumer.kt) contains an example with `org.slf4j:slf4j-simple` as logging provider and a customized configuration which shows logging at `DEBUG` level for all classes except one specific class which only displays logging at `WARN` level. diff --git a/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/KtlintApiConsumer.kt b/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/KtlintApiConsumer.kt index 1df59112c6..6819c6bf43 100644 --- a/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/KtlintApiConsumer.kt +++ b/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/KtlintApiConsumer.kt @@ -8,6 +8,7 @@ import com.pinterest.ktlint.rule.engine.api.EditorConfigDefaults import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride import com.pinterest.ktlint.rule.engine.api.EditorConfigPropertyRegistry import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.IndentConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EXPERIMENTAL_RULES_EXECUTION_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY @@ -112,7 +113,7 @@ public fun main() { """.trimIndent() } apiConsumerKtLintRuleEngine - .format(codeFile) + .format(codeFile) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } .also { LOGGER.info { "Code formatted by KtLint:\n$it" } } diff --git a/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/rules/NoVarRule.kt b/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/rules/NoVarRule.kt index 97ff82f22c..4f198fbcfa 100644 --- a/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/rules/NoVarRule.kt +++ b/ktlint-api-consumer/src/main/kotlin/com/example/ktlint/api/consumer/rules/NoVarRule.kt @@ -1,7 +1,9 @@ package com.example.ktlint.api.consumer.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -9,14 +11,18 @@ public class NoVarRule : Rule( ruleId = RuleId("$CUSTOM_RULE_SET_ID:no-var"), about = About(), - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == ElementType.VAR_KEYWORD) { emit(node.startOffset, "Unexpected var, use val instead", false) + // In case that LintError can be autocorrected, use syntax below + // .ifAutocorrectAllowed { + // // Fix + // } } } } diff --git a/ktlint-api-consumer/src/test/kotlin/com/pinterest/ktlint/api/consumer/ApiTestRunner.kt b/ktlint-api-consumer/src/test/kotlin/com/pinterest/ktlint/api/consumer/ApiTestRunner.kt deleted file mode 100644 index f4d03dc12c..0000000000 --- a/ktlint-api-consumer/src/test/kotlin/com/pinterest/ktlint/api/consumer/ApiTestRunner.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.pinterest.ktlint.api.consumer - -import com.pinterest.ktlint.logger.api.initKtLintKLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import java.nio.file.FileVisitResult -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.SimpleFileVisitor -import java.nio.file.attribute.BasicFileAttributes -import kotlin.io.path.Path -import kotlin.io.path.copyTo -import kotlin.io.path.createDirectories -import kotlin.io.path.relativeToOrSelf - -private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger() - -class ApiTestRunner( - private val tempDir: Path, -) { - fun prepareTestProject(testProjectName: String): Path { - val testProjectPath = TEST_PROJECTS_PATHS.resolve(testProjectName) - assert(Files.exists(testProjectPath)) { - "Test project $testProjectName does not exist!" - } - - return tempDir.resolve(testProjectName).also { testProjectPath.copyRecursively(it) } - } - - private fun Path.copyRecursively(dest: Path) { - Files.walkFileTree( - this, - object : SimpleFileVisitor() { - override fun preVisitDirectory( - dir: Path, - attrs: BasicFileAttributes, - ): FileVisitResult { - val relativeDir = dir.relativeToOrSelf(this@copyRecursively) - dest.resolve(relativeDir).createDirectories() - return FileVisitResult.CONTINUE - } - - override fun visitFile( - file: Path, - attrs: BasicFileAttributes, - ): FileVisitResult { - val relativeFile = file.relativeToOrSelf(this@copyRecursively) - val destinationFile = dest.resolve(relativeFile) - LOGGER.trace { "Copy '$relativeFile' to '$destinationFile'" } - file.copyTo(destinationFile) - return FileVisitResult.CONTINUE - } - }, - ) - } - - companion object { - private val TEST_PROJECTS_PATHS: Path = Path("src", "test", "resources", "api") - } -} diff --git a/ktlint-api-consumer/src/test/kotlin/com/pinterest/ktlint/api/consumer/KtLintRuleEngineTest.kt b/ktlint-api-consumer/src/test/kotlin/com/pinterest/ktlint/api/consumer/KtLintRuleEngineTest.kt index 902043080e..74d1f804d9 100644 --- a/ktlint-api-consumer/src/test/kotlin/com/pinterest/ktlint/api/consumer/KtLintRuleEngineTest.kt +++ b/ktlint-api-consumer/src/test/kotlin/com/pinterest/ktlint/api/consumer/KtLintRuleEngineTest.kt @@ -1,31 +1,37 @@ package com.pinterest.ktlint.api.consumer +import com.pinterest.ktlint.api.consumer.KtLintRuleEngineTest.RuleWithAutocorrectApproveHandler.Companion.RULE_WITH_AUTOCORRECT_APPROVE_HANDLER +import com.pinterest.ktlint.api.consumer.KtLintRuleEngineTest.RuleWithoutAutocorrectApproveHandler.Companion.RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER import com.pinterest.ktlint.rule.engine.api.Code -import com.pinterest.ktlint.rule.engine.api.EditorConfigDefaults import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine import com.pinterest.ktlint.rule.engine.api.LintError +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EXPERIMENTAL_RULES_EXECUTION_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecution import com.pinterest.ktlint.rule.engine.core.api.editorconfig.createRuleExecutionEditorConfigProperty -import com.pinterest.ktlint.rule.engine.core.api.editorconfig.ec4j.toPropertyBuilderWithValue +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed +import com.pinterest.ktlint.rule.engine.core.util.safeAs import com.pinterest.ktlint.ruleset.standard.rules.FilenameRule +import com.pinterest.ktlint.ruleset.standard.rules.INDENTATION_RULE_ID import com.pinterest.ktlint.ruleset.standard.rules.IndentationRule import com.pinterest.ktlint.test.KtlintTestFileSystem import org.assertj.core.api.Assertions.assertThat -import org.ec4j.core.model.EditorConfig -import org.ec4j.core.model.Glob -import org.ec4j.core.model.Section import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir import java.io.File +import java.io.FileWriter import java.nio.file.Path /** @@ -35,6 +41,21 @@ import java.nio.file.Path */ class KtLintRuleEngineTest { private val ktlintTestFileSystem = KtlintTestFileSystem() + private val ktLintRuleEngine = + KtLintRuleEngine( + ruleProviders = + setOf( + RuleProvider { IndentationRule() }, + RuleProvider { RuleWithAutocorrectApproveHandler() }, + RuleProvider { RuleWithoutAutocorrectApproveHandler() }, + ), + editorConfigOverride = + EditorConfigOverride.from( + RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER.createRuleExecutionEditorConfigProperty() to RuleExecution.enabled, + RULE_WITH_AUTOCORRECT_APPROVE_HANDLER.createRuleExecutionEditorConfigProperty() to RuleExecution.enabled, + ), + fileSystem = ktlintTestFileSystem.fileSystem, + ) @AfterEach fun tearDown() { @@ -44,113 +65,77 @@ class KtLintRuleEngineTest { @Nested inner class `Lint with KtLintRuleEngine` { @Test - fun `Given a file that does not contain an error`( + fun `Given a file containing errors found by standard and custom rules`( @TempDir tempDir: Path, ) { - val dir = ApiTestRunner(tempDir).prepareTestProject("no-code-style-error") - - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { IndentationRule() }, - ), - fileSystem = ktlintTestFileSystem.fileSystem, + val filePath = "$tempDir/Code.kt" + FileWriter(filePath).use { + it.write( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), ) + } val lintErrors = mutableListOf() ktLintRuleEngine.lint( - code = Code.fromFile(File("$dir/Main.kt")), - callback = { lintErrors.add(it) }, - ) + code = Code.fromFile(File(filePath)), + ) { lintErrors.add(it) } - assertThat(lintErrors).isEmpty() + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) } @Test - fun `Given a kotlin code snippet that does not contain an error`() { - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { IndentationRule() }, - ), - fileSystem = ktlintTestFileSystem.fileSystem, - ) - + fun `Given a kotlin code snippet containing errors found by standard and custom rules`() { val lintErrors = mutableListOf() ktLintRuleEngine.lint( code = Code.fromSnippet( """ - fun main() { - println("Hello world!") - } + fun bar() { + // foo + // bar + } """.trimIndent(), ), - callback = { lintErrors.add(it) }, - ) + ) { lintErrors.add(it) } - assertThat(lintErrors).isEmpty() + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) } @Test - fun `Given a kotlin script code snippet that does not contain an error`() { - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { IndentationRule() }, - ), - fileSystem = ktlintTestFileSystem.fileSystem, - ) - + fun `Given a kotlin script code snippet containing errors found by standard and custom rules`() { val lintErrors = mutableListOf() ktLintRuleEngine.lint( code = Code.fromSnippet( """ plugins { - id("foo") - id("bar") - } + // foo + // bar + } """.trimIndent(), script = true, ), - callback = { lintErrors.add(it) }, - ) - - assertThat(lintErrors).isEmpty() - } + ) { lintErrors.add(it) } - @Test - fun `Given a code snippet that violates a custom rule prefixed by a rule set id`() { - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { NoVarRule() }, - ), - editorConfigOverride = - EditorConfigOverride.from( - NoVarRule.NO_VAR_RULE_ID.createRuleExecutionEditorConfigProperty() to RuleExecution.enabled, - ), - fileSystem = ktlintTestFileSystem.fileSystem, - ) - - val lintErrors = mutableListOf() - ktLintRuleEngine.lint( - code = - Code.fromSnippet( - """ - var foo = "foo" - """.trimIndent(), - ), - callback = { lintErrors.add(it) }, + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), ) - - assertThat(lintErrors).isNotEmpty } @Test @@ -171,68 +156,80 @@ class KtLintRuleEngineTest { var foo = "foo" """.trimIndent(), ), - callback = { lintErrors.add(it) }, - ) + ) { lintErrors.add(it) } assertThat(lintErrors).isEmpty() } } @Nested - inner class `Format with KtLintRuleEngine` { + inner class `Format (legacy) with KtLintRuleEngine` { @Test fun `Given a file that does not contain an error`( @TempDir tempDir: Path, ) { - val dir = ApiTestRunner(tempDir).prepareTestProject("no-code-style-error") - - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { IndentationRule() }, - ), - fileSystem = ktlintTestFileSystem.fileSystem, + val filePath = "$tempDir/Code.kt" + FileWriter(filePath).use { + it.write( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), ) + } - val original = File("$dir/Main.kt").readText() - + val lintErrors = mutableListOf() val actual = + @Suppress("DEPRECATION") ktLintRuleEngine.format( - code = Code.fromFile(File("$dir/Main.kt")), - ) + code = Code.fromFile(File(filePath)), + ) { lintError, _ -> lintErrors.add(lintError) } - assertThat(actual).isEqualTo(original) + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + """ + fun bar() { + // FOO + // BAR + } + """.trimIndent(), + ) } @Test fun `Given a kotlin code snippet that does contain an indentation error`() { - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { IndentationRule() }, - ), - fileSystem = ktlintTestFileSystem.fileSystem, - ) - + val lintErrors = mutableListOf() val actual = + @Suppress("DEPRECATION") ktLintRuleEngine.format( code = Code.fromSnippet( """ - fun main() { - println("Hello world!") - } + fun bar() { + // foo + // bar + } """.trimIndent(), ), - ) + ) { lintError, _ -> lintErrors.add(lintError) } + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) assertThat(actual).isEqualTo( """ - fun main() { - println("Hello world!") + fun bar() { + // FOO + // BAR } """.trimIndent(), ) @@ -240,76 +237,428 @@ class KtLintRuleEngineTest { @Test fun `Given a kotlin script code snippet that does contain an indentation error`() { - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { IndentationRule() }, - ), - fileSystem = ktlintTestFileSystem.fileSystem, - ) - + val lintErrors = mutableListOf() val actual = + @Suppress("DEPRECATION") ktLintRuleEngine.format( code = Code.fromSnippet( """ plugins { - id("foo") - id("bar") - } + // foo + // bar + } """.trimIndent(), script = true, ), - ) + ) { lintError, _ -> lintErrors.add(lintError) } + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) assertThat(actual).isEqualTo( """ plugins { - id("foo") - id("bar") + // FOO + // BAR } """.trimIndent(), ) } + } - @Test - fun `Given a kotlin code snippet that does contain multiple indentation errors then only format errors found in given range`() { - val ktLintRuleEngine = - KtLintRuleEngine( - ruleProviders = - setOf( - RuleProvider { IndentationRule() }, - ), - fileSystem = ktlintTestFileSystem.fileSystem, + @Nested + inner class `Format with KtLintRuleEngine` { + @Nested + inner class `Given a file that does not contain an error` { + @Test + fun `Given defaultAutocorrect is not set`( + @TempDir + tempDir: Path, + ) { + val filePath = "$tempDir/Code.kt" + FileWriter(filePath).use { + it.write( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), + ) + } + + val lintErrors = mutableListOf() + val actual = + ktLintRuleEngine.format( + code = Code.fromFile(File(filePath)), + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + """ + fun bar() { + // FOO + // BAR + } + """.trimIndent(), ) + } - val originalCode = - """ - fun main() { - println("Hello world!") - println("Hello world!") - println("Hello world!") + @Test + fun `Given defaultAutocorrect is enabled`( + @TempDir + tempDir: Path, + ) { + val filePath = "$tempDir/Code.kt" + FileWriter(filePath).use { + it.write( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), + ) } - """.trimIndent() - val newlineIndexes = - Regex("\n") - .findAll(originalCode) - .map { it.range.first } - .toList() - val actual = - ktLintRuleEngine.format( - code = Code.fromSnippet(originalCode), - autoCorrectOffsetRange = IntRange(newlineIndexes[1], newlineIndexes[2]), + + val lintErrors = mutableListOf() + val actual = + ktLintRuleEngine.format( + code = Code.fromFile(File(filePath)), + defaultAutocorrect = true, + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), ) + assertThat(actual).isEqualTo( + """ + fun bar() { + // FOO + // BAR + } + """.trimIndent(), + ) + } + + @Test + fun `Given defaultAutocorrect is disabled`( + @TempDir + tempDir: Path, + ) { + val filePath = "$tempDir/Code.kt" + FileWriter(filePath).use { + it.write( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), + ) + } + val lintErrors = mutableSetOf() + val actual = + ktLintRuleEngine.format( + code = Code.fromFile(File(filePath)), + defaultAutocorrect = false, + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + // Note that "foo" is not transformed to "FOO" as the defaultAutocorrect for rules without AutocorrectApproveHandler is + // not set + """ + fun bar() { + // foo + // BAR + } + """.trimIndent(), + ) + } + } + + @Nested + inner class `Given a kotlin code snippet that does contain an indentation error` { + @Test + fun `Given defaultAutocorrect is not set`() { + val lintErrors = mutableListOf() + val actual = + ktLintRuleEngine.format( + code = + Code.fromSnippet( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), + ), + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + """ + fun bar() { + // FOO + // BAR + } + """.trimIndent(), + ) + } + + @Test + fun `Given defaultAutocorrect is enabled`() { + val lintErrors = mutableListOf() + val actual = + ktLintRuleEngine.format( + code = + Code.fromSnippet( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), + ), + defaultAutocorrect = true, + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + """ + fun bar() { + // FOO + // BAR + } + """.trimIndent(), + ) + } + + @Test + fun `Given defaultAutocorrect is disabled`() { + val lintErrors = mutableSetOf() + val actual = + ktLintRuleEngine.format( + code = + Code.fromSnippet( + """ + fun bar() { + // foo + // bar + } + """.trimIndent(), + ), + defaultAutocorrect = false, + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + // Note that "foo" is not transformed to "FOO" as the defaultAutocorrect for rules without AutocorrectApproveHandler is + // not set + """ + fun bar() { + // foo + // BAR + } + """.trimIndent(), + ) + } + } + + @Nested + inner class `Given a kotlin script code snippet that does contain an indentation error` { + @Test + fun `Given defaultAutocorrect is not set`() { + val lintErrors = mutableListOf() + val actual = + ktLintRuleEngine.format( + code = + Code.fromSnippet( + """ + plugins { + // foo + // bar + } + """.trimIndent(), + script = true, + ), + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + """ + plugins { + // FOO + // BAR + } + """.trimIndent(), + ) + } + + @Test + fun `Given defaultAutocorrect is enabled`() { + val lintErrors = mutableListOf() + val actual = + ktLintRuleEngine.format( + code = + Code.fromSnippet( + """ + plugins { + // foo + // bar + } + """.trimIndent(), + script = true, + ), + defaultAutocorrect = true, + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + """ + plugins { + // FOO + // BAR + } + """.trimIndent(), + ) + } + + @Test + fun `Given defaultAutocorrect is disabled`() { + val lintErrors = mutableSetOf() + val actual = + ktLintRuleEngine.format( + code = + Code.fromSnippet( + """ + plugins { + // foo + // bar + } + """.trimIndent(), + script = true, + ), + defaultAutocorrect = false, + ) { lintError -> + lintErrors.add(lintError) + ALLOW_AUTOCORRECT + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) + assertThat(actual).isEqualTo( + // Note that "foo" is not transformed to "FOO" as the defaultAutocorrect for rules without AutocorrectApproveHandler is + // not set + """ + plugins { + // foo + // BAR + } + """.trimIndent(), + ) + } + } + + @Test + fun `Given a kotlin code snippet that does contain multiple errors then only format the lint error at specific offset and message`() { + val lintErrors = mutableSetOf() + val actual = + ktLintRuleEngine + .format( + code = + Code.fromSnippet( + """ + // bar + // bar + // bar + """.trimIndent(), + ), + ) { lintError -> + lintErrors.add(lintError) + if (lintError.line == 2 && + lintError.col == 1 && + lintError.ruleId == RULE_WITH_AUTOCORRECT_APPROVE_HANDLER && + lintError.detail == "Bar comment with autocorrect approve handler" + ) { + ALLOW_AUTOCORRECT + } else { + NO_AUTOCORRECT + } + } + + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(1, 1, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(2, 1, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(3, 1, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + ) assertThat(actual).isEqualTo( """ - fun main() { - println("Hello world!") - println("Hello world!") - println("Hello world!") - } + // bar + // BAR + // bar """.trimIndent(), ) } @@ -317,60 +666,92 @@ class KtLintRuleEngineTest { @Test fun `Given that all experimental rules are enabled`() { - val ktLintEngine = + val ktLintRuleEngine = KtLintRuleEngine( ruleProviders = setOf( - RuleProvider { NoVarRule() }, + RuleProvider { IndentationRule() }, + RuleProvider { RuleWithAutocorrectApproveHandler() }, + RuleProvider { RuleWithoutAutocorrectApproveHandler() }, ), - editorConfigDefaults = - EditorConfigDefaults( - EditorConfig - .builder() - .section( - Section - .builder() - .glob(Glob("*.{kt,kts}")) - .properties( - EXPERIMENTAL_RULES_EXECUTION_PROPERTY.toPropertyBuilderWithValue("enabled"), - ), - ).build(), + editorConfigOverride = + EditorConfigOverride.from( + EXPERIMENTAL_RULES_EXECUTION_PROPERTY to RuleExecution.enabled, ), fileSystem = ktlintTestFileSystem.fileSystem, ) - val errors = mutableListOf() - ktLintEngine.lint( + + val lintErrors = mutableListOf() + ktLintRuleEngine.lint( code = Code.fromSnippet( """ - var foo = "foo" + fun bar() { + // foo + // bar + } """.trimIndent(), ), - callback = errors::add, + callback = lintErrors::add, ) - val failedRules = errors.map { it.ruleId } - check(failedRules.contains(NoVarRule.NO_VAR_RULE_ID)) + assertThat(lintErrors).containsExactlyInAnyOrder( + LintError(2, 5, RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, "Foo comment without autocorrect approve handler", true), + LintError(3, 5, RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, "Bar comment with autocorrect approve handler", true), + LintError(4, 1, INDENTATION_RULE_ID, "Unexpected indentation (4) (should be 0)", true), + ) } - private class NoVarRule : + private class RuleWithoutAutocorrectApproveHandler : Rule( - ruleId = NO_VAR_RULE_ID, + ruleId = RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER, about = About(), ), Rule.Experimental { + @Deprecated("Marked for removal in Ktlint 2.0. Please implement interface RuleAutocorrectApproveHandler.") override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { - if (node.elementType == ElementType.VAR_KEYWORD) { - emit(node.startOffset, "Unexpected var, use val instead", false) + if (node.elementType == ElementType.EOL_COMMENT && node.text == "// foo") { + emit(node.startOffset, "Foo comment without autocorrect approve handler", true) + if (autoCorrect) { + node + .safeAs() + ?.rawReplaceWithText("// FOO") + } + } + } + + companion object { + val RULE_WITHOUT_AUTOCORRECT_APPROVE_HANDLER = RuleId("custom:rule-without-autocorrect-approval-handler") + } + } + + private class RuleWithAutocorrectApproveHandler : + Rule( + ruleId = RULE_WITH_AUTOCORRECT_APPROVE_HANDLER, + about = About(), + ), + RuleAutocorrectApproveHandler, + Rule.Experimental { + override fun beforeVisitChildNodes( + node: ASTNode, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + if (node.elementType == ElementType.EOL_COMMENT && node.text == "// bar") { + emit(node.startOffset, "Bar comment with autocorrect approve handler", true) + .ifAutocorrectAllowed { + node + .safeAs() + ?.rawReplaceWithText("// BAR") + } } } companion object { - val NO_VAR_RULE_ID = RuleId("test:no-var-rule") + val RULE_WITH_AUTOCORRECT_APPROVE_HANDLER = RuleId("custom:rule-with-autocorrect-approval-handler") } } } diff --git a/ktlint-api-consumer/src/test/resources/api/no-code-style-error/Main.kt b/ktlint-api-consumer/src/test/resources/api/no-code-style-error/Main.kt deleted file mode 100644 index acf03f8cf9..0000000000 --- a/ktlint-api-consumer/src/test/resources/api/no-code-style-error/Main.kt +++ /dev/null @@ -1,3 +0,0 @@ -fun main() { - println("Hello world!") -} diff --git a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt index aa52d9c208..659031260b 100644 --- a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt +++ b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt @@ -39,6 +39,8 @@ import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride.Companion.plus import com.pinterest.ktlint.rule.engine.api.KtLintParseException import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine import com.pinterest.ktlint.rule.engine.api.KtLintRuleException +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecution @@ -231,7 +233,7 @@ internal class KtlintCommandLine : private lateinit var patterns: List - private val tripped = AtomicBoolean() + private val containsUnfixedLintErrors = AtomicBoolean() private val fileNumber = AtomicInteger() private val errorNumber = AtomicInteger() private val adviseToUseFormat = AtomicBoolean() @@ -319,7 +321,7 @@ internal class KtlintCommandLine : // at least one kotlin file. logger.warn { "No files matched $patterns" } } - if (tripped.get()) { + if (containsUnfixedLintErrors.get()) { exitKtLintProcess(1) } else { exitKtLintProcess(0) @@ -473,29 +475,31 @@ internal class KtlintCommandLine : val ktlintCliErrors = mutableListOf() try { ktLintRuleEngine - .format(code) { lintError, corrected -> - val ktlintCliError = - KtlintCliError( - line = lintError.line, - col = lintError.col, - ruleId = lintError.ruleId.value, - detail = - lintError - .detail - .applyIf(corrected) { "$this (cannot be auto-corrected)" }, - status = - if (corrected) { - FORMAT_IS_AUTOCORRECTED - } else { - LINT_CAN_NOT_BE_AUTOCORRECTED - }, - ) - if (baselineLintErrors.doesNotContain(ktlintCliError)) { - ktlintCliErrors.add(ktlintCliError) - if (!corrected) { - tripped.set(true) - } - } + .format(code) { lintError -> + KtlintCliError( + line = lintError.line, + col = lintError.col, + ruleId = lintError.ruleId.value, + detail = + lintError + .detail + .applyIf(!lintError.canBeAutoCorrected) { "$this (cannot be auto-corrected)" }, + status = + if (lintError.canBeAutoCorrected) { + FORMAT_IS_AUTOCORRECTED + } else { + LINT_CAN_NOT_BE_AUTOCORRECTED + }, + ).takeIf { baselineLintErrors.doesNotContain(it) } + ?.let { ktlintCliError -> + ktlintCliErrors.add(ktlintCliError) + if (lintError.canBeAutoCorrected) { + ALLOW_AUTOCORRECT + } else { + containsUnfixedLintErrors.set(true) + NO_AUTOCORRECT + } + } ?: NO_AUTOCORRECT }.also { formattedFileContent -> when { code.isStdIn -> print(formattedFileContent) @@ -537,7 +541,7 @@ internal class KtlintCommandLine : } } else { ktlintCliErrors.add(e.toKtlintCliError(code)) - tripped.set(true) + containsUnfixedLintErrors.set(true) code.content // making sure `cat file | ktlint --stdin > file` is (relatively) safe } } @@ -567,7 +571,7 @@ internal class KtlintCommandLine : ) if (baselineLintErrors.doesNotContain(ktlintCliError)) { ktlintCliErrors.add(ktlintCliError) - tripped.set(true) + containsUnfixedLintErrors.set(true) } } } catch (e: Exception) { @@ -600,7 +604,7 @@ internal class KtlintCommandLine : } } else { ktlintCliErrors.add(e.toKtlintCliError(code)) - tripped.set(true) + containsUnfixedLintErrors.set(true) } } return ktlintCliErrors.toList() diff --git a/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt b/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt index e1d7524f87..3912c56af3 100644 --- a/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt +++ b/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt @@ -153,7 +153,7 @@ class SimpleCLITest { } @Test - fun `Given some code with an error which can be autocorrected then return from from with the normal exit code`( + fun `Given some code with an error which can be autocorrected then return with the normal exit code`( @TempDir tempDir: Path, ) { diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index 2395ff17f6..f04686499e 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -62,6 +62,18 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun upsertWhitespaceBeforeMe (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Ljava/lang/String;)V } +public final class com/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision : java/lang/Enum { + public static final field ALLOW_AUTOCORRECT Lcom/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision; + public static final field NO_AUTOCORRECT Lcom/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision; + public static fun values ()[Lcom/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision; +} + +public final class com/pinterest/ktlint/rule/engine/core/api/AutocorrectDecisionKt { + public static final fun ifAutocorrectAllowed (Lcom/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; +} + public final class com/pinterest/ktlint/rule/engine/core/api/ElementType { public static final field INSTANCE Lcom/pinterest/ktlint/rule/engine/core/api/ElementType; public final fun getABSTRACT_KEYWORD ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType; @@ -438,6 +450,16 @@ public final class com/pinterest/ktlint/rule/engine/core/api/Rule$VisitorModifie public static final field INSTANCE Lcom/pinterest/ktlint/rule/engine/core/api/Rule$VisitorModifier$RunAsLateAsPossible; } +public abstract interface class com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler { + public abstract fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V + public abstract fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V +} + +public final class com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler$DefaultImpls { + public static fun afterVisitChildNodes (Lcom/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler;Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V + public static fun beforeVisitChildNodes (Lcom/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler;Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V +} + public final class com/pinterest/ktlint/rule/engine/core/api/RuleId { public static final field Companion Lcom/pinterest/ktlint/rule/engine/core/api/RuleId$Companion; public fun (Ljava/lang/String;)V diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision.kt new file mode 100644 index 0000000000..b39ef9a3e3 --- /dev/null +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/AutocorrectDecision.kt @@ -0,0 +1,19 @@ +package com.pinterest.ktlint.rule.engine.core.api + +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT + +public enum class AutocorrectDecision { + /** + * Autocorrect lint violation if supported by the [Rule]. + */ + ALLOW_AUTOCORRECT, + + /** + * Do not autocorrect lint violation even when this is supported by the [Rule]. + */ + NO_AUTOCORRECT, +} + +public inline fun AutocorrectDecision.ifAutocorrectAllowed(function: () -> T): T? = + takeIf { this == ALLOW_AUTOCORRECT } + ?.let { function() } diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/Rule.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/Rule.kt index 57b67e8f99..86b84bf92a 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/Rule.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/Rule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.rule.engine.core.api +import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule.Mode import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty import com.pinterest.ktlint.rule.engine.core.internal.IdNamingPolicy @@ -61,6 +62,11 @@ public class RuleSetId( * When wrapping a rule from the ktlint project and modifying its behavior, please change the [ruleId] and [about] fields, so that it is * clear to users whenever they used the original rule provided by KtLint versus a modified version which is not maintained by the KtLint * project. + * + * Note: + * In Ktlint 2.0 the methods from interface [RuleAutocorrectApproveHandler] interface will be merged into the [Rule] class. Consider to + * implement this interface on your Ktlint 1.x compatible rules in order to make your rules suitable for API Consumers, like the + * ktlint-intellij-plugin, that allow their users to fix violations that can be autocorrected on an interactive 1-by-1 basis. */ public open class Rule( /** @@ -96,10 +102,17 @@ public open class Rule( * This method is called on a node in AST before visiting the child nodes. This is repeated recursively for the * child nodes resulting in a depth first traversal of the AST. * + * When a rule overrides this method, it only supports linting and formatting of an entire file. All violations which are found are + * being emitted. The rule can not support manual autocorrect of a specific single violation in the intellij-ktlint-plugin in case that + * files contains multiple violations caused by the same, or by different rules. + * + * IMPORTANT: This method will *not* be called in case the rule implements the [RuleAutocorrectApproveHandler] interface! + * * @param node AST node * @param autoCorrect indicates whether rule should attempt autocorrection * @param emit a way for rule to notify about a violation (lint error) */ + @Deprecated(message = "Marked for removal in Ktlint 2.0. Please implement interface RuleAutocorrectApproveHandler.") public open fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, @@ -109,8 +122,18 @@ public open class Rule( /** * This method is called on a node in AST after all its child nodes have been visited. + * + * When a rule overrides this method, it only supports linting and formatting of an entire file. All violations which are found are + * being emitted. The rule can not support manual autocorrect of a specific single violation in the intellij-ktlint-plugin in case that + * files contains multiple violations caused by the same, or by different rules. + * + * IMPORTANT: This method will *not* be called in case the rule implements the [RuleAutocorrectApproveHandler] interface! + * + * @param node AST node + * @param autoCorrect indicates whether rule should attempt autocorrection + * @param emit a way for rule to notify about a violation (lint error) */ - @Suppress("unused") + @Deprecated(message = "Marked for removal in Ktlint 2.0. Please implement interface RuleAutocorrectApproveHandler.") public open fun afterVisitChildNodes( node: ASTNode, autoCorrect: Boolean, diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler.kt new file mode 100644 index 0000000000..1a73ca3e6b --- /dev/null +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler.kt @@ -0,0 +1,51 @@ +package com.pinterest.ktlint.rule.engine.core.api + +import org.jetbrains.kotlin.com.intellij.lang.ASTNode + +/** + * In Ktlint 2.0 the methods from this interface will be merged into the [Rule] class. Consider to implement this interface on your Ktlint + * 1.x compatible rules in order to make your rules suitable for API Consumers, like the ktlint-intellij-plugin, that allow their users to + * fix violations that can be autocorrected on an interactive 1-by-1 basis. + * + * Whenever a rule implements this interface, the [beforeVisitChildNodes] and [afterVisitChildNodes] methods of this interface take + * precedence above the methods with same name in the [Rule] class. As of that the rule should not only implement the interface, but also + * change the implementation by replacing the implementation of [Rule.beforeVisitChildNodes] and/or [Rule.afterVisitChildNodes] in the rule + * class with the methods of this class. + */ +public interface RuleAutocorrectApproveHandler { + /** + * This method is called on a node in AST before visiting the child nodes. This is repeated recursively for the + * child nodes resulting in a depth first traversal of the AST. + * + * When a rule overrides this method, the API Consumer can decide per violation whether the violation needs to be autocorrected. For + * this the [emit] function is called, and its result can be used to determine whether the violation is to be corrected. In + * lint mode the [emit] should always return false. + * + * @param node AST node + * @param emit a way for rule to notify about a violation (lint error) and get approval to actually autocorrect the violation + * if that is supported by the rule + */ + public fun beforeVisitChildNodes( + node: ASTNode, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + } + + /** + * This method is called on a node in AST after all its child nodes have been visited. + * + * When a rule overrides this method, the API Consumer can decide per violation whether the violation needs to be autocorrected. For + * this the [emit] function is called, and its result can be used to determine whether the violation is to be corrected. In + * lint mode the [emit] should always return false. + * + * @param node AST node + * @param emit a way for rule to notify about a violation (lint error) and get approval to actually autocorrect the violation + * if that is supported by the rule + */ + @Suppress("unused") + public fun afterVisitChildNodes( + node: ASTNode, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + } +} diff --git a/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt b/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt index 657fa0f6ff..cf0f753f3f 100644 --- a/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt +++ b/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt @@ -989,7 +989,7 @@ class ASTNodeExtensionTest { .text // The newline (and indentation spaces) before the word "comment" inside the block comment is entirely ignored. As there are no - // whitespace nodes containing a newline, this piece of code is considered to be a oneliner starting with the word "val". + // whitespace nodes containing a newline, this piece of code is considered to be an oneliner starting with the word "val". assertThat(actual).contains("val") } @@ -1075,11 +1075,11 @@ class ASTNodeExtensionTest { ) : Rule( ruleId = RuleId("test:dummy-rule"), about = About(), - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { block(node) } diff --git a/ktlint-rule-engine/api/ktlint-rule-engine.api b/ktlint-rule-engine/api/ktlint-rule-engine.api index 85560f94f4..a63e2d76cb 100644 --- a/ktlint-rule-engine/api/ktlint-rule-engine.api +++ b/ktlint-rule-engine/api/ktlint-rule-engine.api @@ -69,9 +69,9 @@ public final class com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine { public synthetic fun (Ljava/util/Set;Lcom/pinterest/ktlint/rule/engine/api/EditorConfigDefaults;Lcom/pinterest/ktlint/rule/engine/api/EditorConfigOverride;ZLjava/nio/file/FileSystem;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun editorConfigFilePaths (Ljava/nio/file/Path;)Ljava/util/List; public final fun format (Lcom/pinterest/ktlint/rule/engine/api/Code;Lkotlin/jvm/functions/Function2;)Ljava/lang/String; - public final fun format (Lcom/pinterest/ktlint/rule/engine/api/Code;Lkotlin/ranges/IntRange;Lkotlin/jvm/functions/Function2;)Ljava/lang/String; + public final fun format (Lcom/pinterest/ktlint/rule/engine/api/Code;ZZLkotlin/jvm/functions/Function1;)Ljava/lang/String; public static synthetic fun format$default (Lcom/pinterest/ktlint/rule/engine/api/KtLintRuleEngine;Lcom/pinterest/ktlint/rule/engine/api/Code;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Ljava/lang/String; - public static synthetic fun format$default (Lcom/pinterest/ktlint/rule/engine/api/KtLintRuleEngine;Lcom/pinterest/ktlint/rule/engine/api/Code;Lkotlin/ranges/IntRange;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Ljava/lang/String; + public static synthetic fun format$default (Lcom/pinterest/ktlint/rule/engine/api/KtLintRuleEngine;Lcom/pinterest/ktlint/rule/engine/api/Code;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String; public final fun generateKotlinEditorConfigSection (Ljava/nio/file/Path;)Ljava/lang/String; public final fun getEditorConfigDefaults ()Lcom/pinterest/ktlint/rule/engine/api/EditorConfigDefaults; public final fun getEditorConfigOverride ()Lcom/pinterest/ktlint/rule/engine/api/EditorConfigOverride; @@ -140,14 +140,16 @@ public final class com/pinterest/ktlint/rule/engine/api/LintError { public fun toString ()Ljava/lang/String; } -public class com/pinterest/ktlint/rule/engine/internal/rules/InternalRule : com/pinterest/ktlint/rule/engine/core/api/Rule { +public class com/pinterest/ktlint/rule/engine/internal/rules/InternalRule : com/pinterest/ktlint/rule/engine/core/api/Rule, com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler { + public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V public fun getUsesEditorConfigProperties ()Ljava/util/Set; public fun getVisitorModifiers ()Ljava/util/Set; } public final class com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule : com/pinterest/ktlint/rule/engine/internal/rules/InternalRule { public fun (Ljava/util/List;)V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRuleKt { diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt index 2d32c5f0e4..d7d76b0474 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt @@ -2,40 +2,33 @@ package com.pinterest.ktlint.rule.engine.api -import com.pinterest.ktlint.logger.api.initKtLintKLogger import com.pinterest.ktlint.rule.engine.api.EditorConfigDefaults.Companion.EMPTY_EDITOR_CONFIG_DEFAULTS import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride.Companion.EMPTY_EDITOR_CONFIG_OVERRIDE +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue -import com.pinterest.ktlint.rule.engine.core.api.editorconfig.END_OF_LINE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.propertyTypes import com.pinterest.ktlint.rule.engine.core.util.safeAs -import com.pinterest.ktlint.rule.engine.internal.AutoCorrectDisabledHandler -import com.pinterest.ktlint.rule.engine.internal.AutoCorrectEnabledHandler -import com.pinterest.ktlint.rule.engine.internal.AutoCorrectHandler -import com.pinterest.ktlint.rule.engine.internal.AutoCorrectOffsetRangeHandler +import com.pinterest.ktlint.rule.engine.internal.AllAutocorrectHandler +import com.pinterest.ktlint.rule.engine.internal.CodeFormatter import com.pinterest.ktlint.rule.engine.internal.EditorConfigFinder import com.pinterest.ktlint.rule.engine.internal.EditorConfigGenerator import com.pinterest.ktlint.rule.engine.internal.EditorConfigLoader import com.pinterest.ktlint.rule.engine.internal.EditorConfigLoaderEc4j -import com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext +import com.pinterest.ktlint.rule.engine.internal.LintErrorAutocorrectHandler +import com.pinterest.ktlint.rule.engine.internal.NoneAutocorrectHandler import com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.Companion.createRuleExecutionContext import com.pinterest.ktlint.rule.engine.internal.ThreadSafeEditorConfigCache.Companion.THREAD_SAFE_EDITOR_CONFIG_CACHE -import com.pinterest.ktlint.rule.engine.internal.VisitorProvider -import io.github.oshai.kotlinlogging.KotlinLogging import org.ec4j.core.Resource -import org.ec4j.core.model.PropertyType.EndOfLineValue.crlf -import org.ec4j.core.model.PropertyType.EndOfLineValue.lf import org.jetbrains.kotlin.com.intellij.lang.FileASTNode import java.nio.charset.StandardCharsets import java.nio.file.FileSystem import java.nio.file.FileSystems import java.nio.file.Path -private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger() - public class KtLintRuleEngine( /** * The set of [RuleProvider]s to be invoked by the [KtLintRuleEngine]. A [RuleProvider] is able to create a new instance of a [Rule] so @@ -82,6 +75,8 @@ public class KtLintRuleEngine( editorConfigOverride, ) + private val codeFormatter = CodeFormatter(this) + /** * Check the [code] for lint errors. If [code] is path as file reference then the '.editorconfig' files on the path to file are taken * into account. For each lint violation found, the [callback] is invoked. @@ -93,186 +88,70 @@ public class KtLintRuleEngine( code: Code, callback: (LintError) -> Unit = { }, ) { - LOGGER.debug { "Starting with linting file '${code.fileNameOrStdin()}'" } - - val ruleExecutionContext = createRuleExecutionContext(this, code) - val errors = mutableListOf() - - VisitorProvider(ruleExecutionContext.ruleProviders) - .visitor() - .invoke { rule -> - ruleExecutionContext.executeRule(rule, AutoCorrectDisabledHandler) { offset, errorMessage, canBeAutoCorrected -> - val (line, col) = ruleExecutionContext.positionInTextLocator(offset) - LintError(line, col, rule.ruleId, errorMessage, canBeAutoCorrected) - .let { lintError -> - errors.add(lintError) - // In trace mode report the violation immediately. The order in which violations are actually found might be - // different from the order in which they are reported. For debugging purposes it can be helpful to know the - // exact order in which violations are being solved. - LOGGER.trace { "Lint violation: ${lintError.logMessage(code)}" } - } - } - } - - errors - .sortedWith { l, r -> if (l.line != r.line) l.line - r.line else l.col - r.col } - .forEach { e -> callback(e) } - - LOGGER.debug { "Finished with linting file '${code.fileNameOrStdin()}'" } + codeFormatter.format( + code = code, + autocorrectHandler = NoneAutocorrectHandler, + callback = { lintError, _ -> callback(lintError) }, + maxFormatRunsPerFile = 1, + ) } /** - * Fix style violations in [code] for lint errors when possible. If [code] is passed as file reference then the '.editorconfig' files on - * the path are taken into account. For each lint violation found, the [callback] is invoked. + * Fix all style violations in [code] for lint errors when possible. If [code] is passed as file reference then the '.editorconfig' + * files on the path are taken into account. For each lint violation found, the [callback] is invoked. + * + * If [code] contains lint errors which have been autocorrected, then the resulting code is formatted again (up until + * [MAX_FORMAT_RUNS_PER_FILE] times) in order to fix lint errors that might result from the previous formatting run. + * + * [callback] is invoked once for each [LintError] found during any runs. As of that the [callback] might be invoked multiple times for + * the same [LintError]. * * @throws KtLintParseException if text is not a valid Kotlin code * @throws KtLintRuleException in case of internal failure caused by a bug in rule implementation */ + @Deprecated(message = "Marked for removal in Ktlint 2.0") public fun format( code: Code, callback: (LintError, Boolean) -> Unit = { _, _ -> }, - ): String = format(code, AutoCorrectEnabledHandler, callback) + ): String = codeFormatter.format(code, AllAutocorrectHandler, callback, MAX_FORMAT_RUNS_PER_FILE) /** - * Fix style violations in [code] for lint errors found in the [autoCorrectOffsetRange] when possible. If [code] is passed as file - * reference then the '.editorconfig' files on the path are taken into account. For each lint violation found, the [callback] is - * invoked. - * - * IMPORTANT: Partial formatting not always works as expected. The offset of the node which is triggering the violation does not - * necessarily to be close to the offset at which the violation is reported. Counter-intuitively the offset of the trigger node must be - * located inside the [autoCorrectOffsetRange] instead of the offset at which the violation is reported. - * - * For example, the given code might contain the when-statement below: - * ``` - * // code with lint violations + * Formats style violations in [code]. Whenever a [LintError] is found the [callback] is invoked. If the [LintError] can be + * autocorrected *and* the rule that found that the violation has implemented the [RuleAutocorrectApproveHandler] interface, the API + * Consumer determines whether that [LintError] is to autocorrected, or not. * - * when(foobar) { - * FOO -> "Single line" - * BAR -> - * """ - * Multi line - * """.trimIndent() - * else -> null - * } + * When autocorrecting a [LintError] it is possible that other violations are introduced. By default, format is run up until + * [MAX_FORMAT_RUNS_PER_FILE] times. It is still possible that violations remain after the last run. This is a trait-off between solving + * as many errors as possible versus bad performance in case an endless loop of violations exists. In case the [callback] is implemented + * to let the user of the API Consumer to decide which [LintError] it to be autocorrected, or not, it might be better to disable this + * behavior by disabling [rerunAfterAutocorrect]. * - * // more code with lint violations - * ``` - * The `blank-line-between-when-conditions` rule requires blank lines to be added between the conditions. If the when-keyword above is - * included in the range which is to be formatted then the blank lines before the conditions are added. If only the when-conditions - * itself are selected, but not the when-keyword, then the blank lines are not added. + * In case the rule has not implemented the [RuleAutocorrectApproveHandler] interface, then the result of the [callback] is ignored as + * the rule is not able to process it. For such rules the [defaultAutocorrect] determines whether autocorrect for this rule is to be + * applied, or not. By default, the autocorrect will be applied (backwards compatability). * - * This unexpected behavior is a side effect of the way the partial formatting is implemented currently. The side effects can be - * prevented by delaying the decision to autocorrect as late as possible and the exact offset of the error is known. This however would - * cause a breaking change, and needs to wait until Ktlint V2.x. + * [callback] is invoked once for each [LintError] found during any runs. As of that the [callback] might be invoked multiple times for + * the same [LintError]. * * @throws KtLintParseException if text is not a valid Kotlin code * @throws KtLintRuleException in case of internal failure caused by a bug in rule implementation */ public fun format( code: Code, - autoCorrectOffsetRange: IntRange, - callback: (LintError, Boolean) -> Unit = { _, _ -> }, - ): String = format(code, AutoCorrectOffsetRangeHandler(autoCorrectOffsetRange), callback) - - private fun format( - code: Code, - autoCorrectHandler: AutoCorrectHandler, - callback: (LintError, Boolean) -> Unit = { _, _ -> }, - ): String { - LOGGER.debug { "Starting with formatting file '${code.fileNameOrStdin()}'" } - - val hasUTF8BOM = code.content.startsWith(UTF8_BOM) - val ruleExecutionContext = createRuleExecutionContext(this, code) - - val visitorProvider = VisitorProvider(ruleExecutionContext.ruleProviders) - var formatRunCount = 0 - var mutated: Boolean - val errors = mutableSetOf>() - do { - mutated = false - formatRunCount++ - visitorProvider - .visitor() - .invoke { rule -> - ruleExecutionContext.executeRule(rule, autoCorrectHandler) { offset, errorMessage, canBeAutoCorrected -> - if (canBeAutoCorrected) { - mutated = true - /* - * Rebuild the suppression locator after each change in the AST as the offsets of the suppression hints might - * have changed. - */ - ruleExecutionContext.rebuildSuppressionLocator() - } - val (line, col) = ruleExecutionContext.positionInTextLocator(offset) - LintError(line, col, rule.ruleId, errorMessage, canBeAutoCorrected) - .let { lintError -> - errors.add( - Pair( - lintError, - // It is assumed that a rule that emits that an error can be autocorrected, also does correct the error. - canBeAutoCorrected, - ), - ) - // In trace mode report the violation immediately. The order in which violations are actually found might be - // different from the order in which they are reported. For debugging purposes it can be helpful to know the - // exact order in which violations are being solved. - LOGGER.trace { "Format violation: ${lintError.logMessage(code)}" } - } - } - } - } while (mutated && formatRunCount < MAX_FORMAT_RUNS_PER_FILE) - if (formatRunCount == MAX_FORMAT_RUNS_PER_FILE && mutated) { - // It is unknown if the last format run introduces new lint violations which can be autocorrected. So run lint once more so that - // the user can be informed about this correctly. - var hasErrorsWhichCanBeAutocorrected = false - visitorProvider - .visitor() - .invoke { rule -> - if (!hasErrorsWhichCanBeAutocorrected) { - ruleExecutionContext.executeRule(rule, AutoCorrectDisabledHandler) { _, _, canBeAutoCorrected -> - if (canBeAutoCorrected) { - hasErrorsWhichCanBeAutocorrected = true - } - } - } - } - if (hasErrorsWhichCanBeAutocorrected) { - LOGGER.warn { - "Format was not able to resolve all violations which (theoretically) can be autocorrected in file " + - "${code.filePathOrStdin()} in $MAX_FORMAT_RUNS_PER_FILE consecutive runs of format." - } - } - } - - errors - .sortedWith { (l), (r) -> if (l.line != r.line) l.line - r.line else l.col - r.col } - .forEach { (e, corrected) -> callback(e, corrected) } - - if (!mutated && formatRunCount == 1) { - return code.content - } - - val formattedCode = - ruleExecutionContext - .rootNode - .text - .replace("\n", ruleExecutionContext.determineLineSeparator(code.content)) - return if (hasUTF8BOM) { - UTF8_BOM + formattedCode - } else { - formattedCode - }.also { - LOGGER.debug { "Finished with formatting file '${code.fileNameOrStdin()}'" } - } - } - - private fun RuleExecutionContext.determineLineSeparator(fileContent: String): String { - val eol = editorConfig[END_OF_LINE_PROPERTY] - return when { - eol == crlf || eol != lf && fileContent.lastIndexOf('\r') != -1 -> "\r\n" - else -> "\n" - } - } + rerunAfterAutocorrect: Boolean = true, + defaultAutocorrect: Boolean = true, + callback: (LintError) -> AutocorrectDecision, + ): String = + codeFormatter.format( + code = code, + autocorrectHandler = LintErrorAutocorrectHandler(defaultAutocorrect, callback), + maxFormatRunsPerFile = + if (rerunAfterAutocorrect) { + MAX_FORMAT_RUNS_PER_FILE + } else { + 1 + }, + ) /** * Generates Kotlin `.editorconfig` file section content based on a path to a file or directory. Given that path, all '.editorconfig' @@ -334,14 +213,6 @@ public class KtLintRuleEngine( public fun transformToAst(code: Code): FileASTNode = createRuleExecutionContext(this, code).rootNode - private fun LintError.logMessage(code: Code) = - "${code.fileNameOrStdin()}:$line:$col: $detail ($ruleId)" + - if (canBeAutoCorrected) { - "" - } else { - " [cannot be autocorrected]" - } - public companion object { internal const val UTF8_BOM = "\uFEFF" diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/AutoCorrectHandler.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/AutoCorrectHandler.kt deleted file mode 100644 index 66e99fa95a..0000000000 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/AutoCorrectHandler.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.pinterest.ktlint.rule.engine.internal - -import org.jetbrains.kotlin.com.intellij.lang.ASTNode - -/** - * Handler which determines whether autocorrect should be enabled or disabled for the given offset. - */ -internal sealed interface AutoCorrectHandler { - fun autoCorrect(node: ASTNode): Boolean -} - -internal data object AutoCorrectDisabledHandler : AutoCorrectHandler { - override fun autoCorrect(node: ASTNode) = false -} - -internal data object AutoCorrectEnabledHandler : AutoCorrectHandler { - override fun autoCorrect(node: ASTNode) = true -} - -internal class AutoCorrectOffsetRangeHandler( - private val autoCorrectOffsetRange: IntRange, -) : AutoCorrectHandler { - override fun autoCorrect(node: ASTNode) = node.startOffset in autoCorrectOffsetRange -} diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/AutocorrectHandler.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/AutocorrectHandler.kt new file mode 100644 index 0000000000..fb68a7bbda --- /dev/null +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/AutocorrectHandler.kt @@ -0,0 +1,42 @@ +package com.pinterest.ktlint.rule.engine.internal + +import com.pinterest.ktlint.rule.engine.api.LintError +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler + +/** + * Handler which determines whether autocorrect should be enabled or disabled for the given offset. + */ +internal sealed interface AutocorrectHandler { + fun autocorrectDecision(lintError: LintError): AutocorrectDecision +} + +/** + * Autocorrect no [LintError]s. This handler is used for backward compatability with rules that have not implemented + * [RuleAutocorrectApproveHandler]. + */ +internal data object NoneAutocorrectHandler : AutocorrectHandler { + override fun autocorrectDecision(lintError: LintError) = NO_AUTOCORRECT +} + +/** + * Autocorrect all [LintError]s. This handler is used for backward compatability with rules that have not implemented + * [RuleAutocorrectApproveHandler]. + */ +internal data object AllAutocorrectHandler : AutocorrectHandler { + override fun autocorrectDecision(lintError: LintError) = ALLOW_AUTOCORRECT +} + +/** + * The [LintErrorAutocorrectHandler] only works for rules that implement [RuleAutocorrectApproveHandler]. For rules that do not implement + * that interface, no autocorrections will be made even though the rule is capable doing so. Reason for this is that the API consumer should + * be able to control whether a specific [LintError] is to be corrected or not. + */ +internal class LintErrorAutocorrectHandler( + val autocorrectRuleWithoutAutocorrectApproveHandler: Boolean, + private val callback: (LintError) -> AutocorrectDecision, +) : AutocorrectHandler { + override fun autocorrectDecision(lintError: LintError) = callback(lintError) +} diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/CodeFormatter.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/CodeFormatter.kt new file mode 100644 index 0000000000..e3e9b82ceb --- /dev/null +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/CodeFormatter.kt @@ -0,0 +1,173 @@ +package com.pinterest.ktlint.rule.engine.internal + +import com.pinterest.ktlint.logger.api.initKtLintKLogger +import com.pinterest.ktlint.rule.engine.api.Code +import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine +import com.pinterest.ktlint.rule.engine.api.LintError +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.END_OF_LINE_PROPERTY +import io.github.oshai.kotlinlogging.KotlinLogging +import org.ec4j.core.model.PropertyType + +private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger() + +internal class CodeFormatter( + val ktLintRuleEngine: KtLintRuleEngine, +) { + fun format( + code: Code, + autocorrectHandler: AutocorrectHandler, + callback: (LintError, Boolean) -> Unit = { _, _ -> }, + maxFormatRunsPerFile: Int, + ): String { + LOGGER.debug { "Starting with processing file '${code.fileNameOrStdin()}'" } + + val (formattedCode, errors) = format(code, autocorrectHandler, maxFormatRunsPerFile) + + errors + .sortedWith(lintErrorLineAndColumnComparator { it.first }) + .forEach { (e, corrected) -> callback(e, corrected) } + + return (code.utf8Bom() + formattedCode).also { + LOGGER.debug { "Finished with processing file '${code.fileNameOrStdin()}'" } + } + } + + private fun Code.utf8Bom() = + if (content.startsWith(KtLintRuleEngine.UTF8_BOM)) { + KtLintRuleEngine.UTF8_BOM + } else { + "" + } + + private fun format( + code: Code, + autocorrectHandler: AutocorrectHandler, + maxFormatRunsPerFile: Int, + ): Pair>> { + with(RuleExecutionContext.createRuleExecutionContext(ktLintRuleEngine, code)) { + val errors = mutableSetOf>() + var formatRunCount = 0 + var mutated: Boolean + do { + mutated = + format(autocorrectHandler, code).let { ruleErrors -> + errors.addAll(ruleErrors) + ruleErrors.any { it.first.canBeAutoCorrected } + } + formatRunCount++ + } while (mutated && formatRunCount < maxFormatRunsPerFile) + if (mutated && formatRunCount == maxFormatRunsPerFile) { + // It is unknown if the last format run introduces new lint violations which can be autocorrected. So run lint once more + // so that the user can be informed about this correctly. + lintAfterFormat().also { + LOGGER.warn { + "Format was not able to resolve all violations which (theoretically) can be autocorrected in file " + + "${code.filePathOrStdin()} in $maxFormatRunsPerFile consecutive runs of format." + } + } + } + val lineSeparator = code.determineLineSeparator(editorConfig[END_OF_LINE_PROPERTY]) + return Pair(formattedCode(lineSeparator), errors) + } + } + + private fun RuleExecutionContext.formattedCode(lineSeparator: String) = rootNode.text.replace("\n", lineSeparator) + + private fun RuleExecutionContext.format( + autocorrectHandler: AutocorrectHandler, + code: Code, + ): Set> { + val errors: MutableSet> = mutableSetOf() + VisitorProvider(ruleProviders) + .rules + .forEach { rule -> + executeRule(rule, autocorrectHandler, code).let { ruleErrors -> errors.addAll(ruleErrors) } + } + return errors + } + + private fun RuleExecutionContext.lintAfterFormat(): Boolean { + var hasErrorsWhichCanBeAutocorrected = false + VisitorProvider(ruleProviders) + .rules + .forEach { rule -> + if (!hasErrorsWhichCanBeAutocorrected) { + executeRule(rule, NoneAutocorrectHandler) { _, _, canBeAutoCorrected -> + if (canBeAutoCorrected) { + hasErrorsWhichCanBeAutocorrected = true + } + // No need to ask for approval in lint mode + NO_AUTOCORRECT + } + } + } + return hasErrorsWhichCanBeAutocorrected + } + + private fun RuleExecutionContext.executeRule( + rule: Rule, + autocorrectHandler: AutocorrectHandler, + code: Code, + ): Set> { + val errors = mutableSetOf>() + executeRule(rule, autocorrectHandler) { offset, errorMessage, canBeAutoCorrected -> + val (line, col) = positionInTextLocator(offset) + val lintError = LintError(line, col, rule.ruleId, errorMessage, canBeAutoCorrected) + // In trace mode report the violation immediately. The order in which violations are actually found might be + // different from the order in which they are reported. For debugging purposes it can be helpful to know the + // exact order in which violations are being solved. + LOGGER.trace { "Format violation: ${lintError.logMessage(code)}" } + + // Always request the autocorrectDecision, even in case it is already known that the LintError can not be autocorrected. In + // this way the API Consumer can still use data from the LintError for other purposes. + autocorrectHandler + .autocorrectDecision(lintError) + .also { autocorrectDecision -> + // Ignore decision of the API Consumer in case the error can not be autocorrected + val autocorrect = autocorrectDecision == ALLOW_AUTOCORRECT && canBeAutoCorrected + if (autocorrect) { + /* + * Rebuild the suppression locator after each change in the AST as the offsets of the suppression hints might + * have changed. + */ + rebuildSuppressionLocator() + } + errors.add( + Pair( + lintError, + // It is assumed that a rule that asks for autocorrect approval, also does correct the error. + autocorrect, + ), + ) + } + } + return errors + } + + private fun Code.determineLineSeparator(eolEditorConfigProperty: PropertyType.EndOfLineValue): String = + when { + eolEditorConfigProperty == PropertyType.EndOfLineValue.crlf || + eolEditorConfigProperty != PropertyType.EndOfLineValue.lf && + doesNotContain('\r') -> + "\r\n".also { LOGGER.debug { "line separator: ${eolEditorConfigProperty.name} --> CRLF" } } + + else -> "\n".also { LOGGER.debug { "line separator: ${eolEditorConfigProperty.name} --> LF" } } + } + + private fun Code.doesNotContain(char: Char) = content.lastIndexOf(char) != -1 + + private fun lintErrorLineAndColumnComparator(transformer: (T) -> LintError) = + compareBy { transformer(it).line } + .then(compareBy { transformer(it).col }) + + private fun LintError.logMessage(code: Code) = + "${code.fileNameOrStdin()}:$line:$col: $detail ($ruleId)" + + if (canBeAutoCorrected) { + "" + } else { + " [cannot be autocorrected]" + } +} diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt index 802b9c03e6..220f4d6ee0 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt @@ -6,7 +6,9 @@ import com.pinterest.ktlint.rule.engine.api.KtLintParseException import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine.Companion.UTF8_BOM import com.pinterest.ktlint.rule.engine.api.KtLintRuleException +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig @@ -46,8 +48,8 @@ internal class RuleExecutionContext private constructor( fun executeRule( rule: Rule, - autoCorrectHandler: AutoCorrectHandler, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + autocorrectHandler: AutocorrectHandler, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { try { rule.startTraversalOfAST() @@ -59,7 +61,7 @@ internal class RuleExecutionContext private constructor( // EditorConfigProperty that is not explicitly defined. editorConfig.filterBy(rule.usesEditorConfigProperties.plus(CODE_STYLE_PROPERTY)), ) - this.executeRuleOnNodeRecursively(rootNode, rule, autoCorrectHandler, emit) + this.executeRuleOnNodeRecursively(rootNode, rule, autocorrectHandler, emitAndApprove) rule.afterLastNode() } catch (e: RuleExecutionException) { throw KtLintRuleException( @@ -80,45 +82,38 @@ internal class RuleExecutionContext private constructor( private fun executeRuleOnNodeRecursively( node: ASTNode, rule: Rule, - autoCorrectHandler: AutoCorrectHandler, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + autocorrectHandler: AutocorrectHandler, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - // TODO: In Ktlint V2 the autocorrect handler should be passed down to the rules, so that the autocorrect handler can check the - // offset at which the violation is found is in the autocorrect range or not. Currently it is checked whether the offset of the - // node that is triggering the violation is in the range. This has following side effects: - // * if the offset of the node which triggers the violation is inside the range, but the offset of the violation itself it outside - // the autocorrect range than a change is made to code outside the selected range - // * if the offset of the node which triggers the violation is outside the range, but the offset of the violation itself it inside - // the autocorrect range than *not* change is made to code which is in the selected range while the user would have expected it - // to be changed. - // Passing down the autocorrectHandler to the rules is a breaking change as the Rule signature needs to be changed. - val autoCorrect = autoCorrectHandler.autoCorrect(node) - /** * The [suppressionLocator] can be changed during each visit of node when running [KtLintRuleEngine.format]. So a new handler is to * be built before visiting the nodes. */ - val suppressHandler = SuppressHandler(suppressionLocator, autoCorrect, emit) + val suppress = suppressionLocator(node.startOffset, rule.ruleId) if (rule.shouldContinueTraversalOfAST()) { try { - executeRuleOnNodeRecursively(node, rule, autoCorrectHandler, suppressHandler) + if (rule is RuleAutocorrectApproveHandler) { + executeRuleWithAutocorrectApproveHandlerOnNodeRecursively(node, rule, autocorrectHandler, suppress, emitAndApprove) + } else { + executeRuleWithoutAutocorrectApproveHandlerOnNodeRecursively(node, rule, autocorrectHandler, suppress, emitAndApprove) + } } catch (e: Exception) { - if (autoCorrect) { - // line/col cannot be reliably mapped as exception might originate from a node not present in the - // original AST + if (autocorrectHandler is NoneAutocorrectHandler) { + val (line, col) = positionInTextLocator(node.startOffset) throw RuleExecutionException( rule, - 0, - 0, + line, + col, // Prevent extreme long stack trace caused by recursive call and only pass root cause e.cause ?: e, ) } else { - val (line, col) = positionInTextLocator(node.startOffset) + // line/col cannot be reliably mapped as exception might originate from a node not present in the + // original AST throw RuleExecutionException( rule, - line, - col, + 0, + 0, // Prevent extreme long stack trace caused by recursive call and only pass root cause e.cause ?: e, ) @@ -127,34 +122,82 @@ internal class RuleExecutionContext private constructor( } } - private fun executeRuleOnNodeRecursively( + @Deprecated(message = "Remove in Ktlint 2.0") + private fun executeRuleWithoutAutocorrectApproveHandlerOnNodeRecursively( node: ASTNode, rule: Rule, - autoCorrectHandler: AutoCorrectHandler, - suppressHandler: SuppressHandler, + autocorrectHandler: AutocorrectHandler, + suppress: Boolean, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - suppressHandler.handle(node, rule.ruleId) { autoCorrect, emit -> - rule.beforeVisitChildNodes(node, autoCorrect, emit) + require(rule !is RuleAutocorrectApproveHandler) + val autoCorrect = + autocorrectHandler is AllAutocorrectHandler || + ( + autocorrectHandler is LintErrorAutocorrectHandler && + autocorrectHandler.autocorrectRuleWithoutAutocorrectApproveHandler + ) + val emitOnly = emitAndApprove.onlyEmit() + if (!suppress) { + rule.beforeVisitChildNodes(node, autoCorrect, emitOnly) } if (rule.shouldContinueTraversalOfAST()) { node .getChildren(null) .forEach { childNode -> - suppressHandler.handle(childNode, rule.ruleId) { _, emit -> - this.executeRuleOnNodeRecursively( - childNode, - rule, - autoCorrectHandler, - emit, - ) - } + this.executeRuleOnNodeRecursively( + childNode, + rule, + autocorrectHandler, + emitAndApprove, + ) } } - suppressHandler.handle(node, rule.ruleId) { autoCorrect, emit -> - rule.afterVisitChildNodes(node, autoCorrect, emit) + if (!suppress) { + rule.afterVisitChildNodes(node, autoCorrect, emitOnly) } } + private fun executeRuleWithAutocorrectApproveHandlerOnNodeRecursively( + node: ASTNode, + rule: Rule, + autocorrectHandler: AutocorrectHandler, + suppress: Boolean, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + require(rule is RuleAutocorrectApproveHandler) + if (!suppress) { + rule.beforeVisitChildNodes(node, emitAndApprove) + } + if (rule.shouldContinueTraversalOfAST()) { + node + .getChildren(null) + .forEach { childNode -> + this.executeRuleOnNodeRecursively( + childNode, + rule, + autocorrectHandler, + emitAndApprove, + ) + } + } + if (!suppress) { + rule.afterVisitChildNodes(node, emitAndApprove) + } + } + + // Simplify the emitAndApprove to an emit only lambda which can be used in the legacy (deprecated) functions + @Deprecated(message = "Remove in Ktlint 2.0") + private fun ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision).onlyEmit() = + { + offset: Int, + errorMessage: String, + canBeAutoCorrected: Boolean, + -> + this(offset, errorMessage, canBeAutoCorrected) + Unit + } + companion object { internal fun createRuleExecutionContext( ktLintRuleEngine: KtLintRuleEngine, diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressHandler.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressHandler.kt deleted file mode 100644 index 80ccbdec49..0000000000 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressHandler.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.pinterest.ktlint.rule.engine.internal - -import com.pinterest.ktlint.rule.engine.core.api.RuleId -import org.jetbrains.kotlin.com.intellij.lang.ASTNode - -internal class SuppressHandler( - private val suppressionLocator: SuppressionLocator, - private val autoCorrect: Boolean, - private val emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, -) { - fun handle( - node: ASTNode, - ruleId: RuleId, - function: (Boolean, (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) -> Unit, - ) { - val suppress = - suppressionLocator( - node.startOffset, - ruleId, - ) - val autoCorrect = this.autoCorrect && !suppress - val emit = - if (suppress) { - SUPPRESS_EMIT - } else { - this.emit - } - function(autoCorrect, emit) - } - - private companion object { - // Swallow violation so that it is not emitted - val SUPPRESS_EMIT: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit = { _, _, _ -> } - } -} diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilder.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilder.kt index 9b3fb9e5ce..572467b64d 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilder.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilder.kt @@ -111,49 +111,31 @@ internal object SuppressionLocatorBuilder { } private fun ASTNode.createSuppressionHintFromComment(formatterTags: FormatterTags): CommentSuppressionHint? = - if (text.startsWith("//")) { - createSuppressionHintFromEolComment(formatterTags) - } else { - createSuppressionHintFromBlockComment(formatterTags) - } - - private fun ASTNode.createSuppressionHintFromEolComment(formatterTags: FormatterTags): CommentSuppressionHint? = text .removePrefix("//") - .trim() - .split(" ") - .takeIf { it.isNotEmpty() } - ?.takeIf { it[0] == formatterTags.formatterTagOff } - ?.let { parts -> - CommentSuppressionHint( - this, - HashSet(parts.tail()), - EOL, - ) - } - - private fun ASTNode.createSuppressionHintFromBlockComment(formatterTags: FormatterTags): CommentSuppressionHint? = - text .removePrefix("/*") .removeSuffix("*/") .trim() .split(" ") .takeIf { it.isNotEmpty() } ?.let { parts -> - if (parts[0] == formatterTags.formatterTagOff) { - CommentSuppressionHint( - this, - HashSet(parts.tail()), - BLOCK_START, - ) - } else if (parts[0] == formatterTags.formatterTagOn) { - CommentSuppressionHint( - this, - HashSet(parts.tail()), - BLOCK_END, - ) - } else { - null + when (parts[0]) { + formatterTags.formatterTagOff -> + CommentSuppressionHint( + this, + HashSet(parts.tail()), + BLOCK_START, + ) + + formatterTags.formatterTagOn -> + CommentSuppressionHint( + this, + HashSet(parts.tail()), + BLOCK_END, + ) + + else -> + null } } diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProvider.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProvider.kt index 37f35b45e3..5540b1c929 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProvider.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProvider.kt @@ -36,15 +36,14 @@ internal class VisitorProvider( RULE_PROVIDER_SORTER }.getSortedRuleProviders(ruleProviders) - internal fun visitor(): ((rule: Rule) -> Unit) -> Unit { - if (ruleProvidersSorted.isEmpty()) { - LOGGER.debug { "Skipping file as no enabled rules are found to be executed" } - return { _ -> } - } - return { visit -> - ruleProvidersSorted.forEach { - visit(it.createNewRuleInstance()) + internal val rules: List + get() { + if (ruleProvidersSorted.isEmpty()) { + LOGGER.debug { "Skipping file as no enabled rules are found to be executed" } + return emptyList() + } + return ruleProvidersSorted.map { + it.createNewRuleInstance() } } - } } diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/InternalRule.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/InternalRule.kt index cfc4f12f1d..5ce5d86fae 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/InternalRule.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/InternalRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.rule.engine.internal.rules import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty @@ -23,4 +24,5 @@ public open class InternalRule internal constructor( visitorModifiers = visitorModifiers, usesEditorConfigProperties = usesEditorConfigProperties, about = INTERNAL_RULE_ABOUT, - ) + ), + RuleAutocorrectApproveHandler diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt index a159a21e70..d0fa821694 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.rule.engine.internal.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -8,6 +9,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.STRING_TEMPLATE import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT import com.pinterest.ktlint.rule.engine.core.api.RuleId +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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.nextLeaf @@ -70,16 +72,15 @@ public class KtlintSuppressionRule( override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { isKtlintRuleSuppressionInAnnotation(it) } - ?.let { visitKtlintSuppressionInAnnotation(node, autoCorrect, emit) } + ?.let { visitKtlintSuppressionInAnnotation(node, emit) } node .ktlintDirectiveOrNull(ruleIdValidator) - ?.visitKtlintDirective(autoCorrect, emit) + ?.visitKtlintDirective(emit) } private fun isKtlintRuleSuppressionInAnnotation(node: ASTNode) = @@ -97,8 +98,7 @@ public class KtlintSuppressionRule( private fun visitKtlintSuppressionInAnnotation( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .psi @@ -113,13 +113,14 @@ public class KtlintSuppressionRule( val offset = literalStringTemplateEntry.startOffset if (prefixedSuppression.isUnknownKtlintSuppression()) { emit(offset, "Ktlint rule with id '$prefixedSuppression' is unknown or not loaded", false) + Unit } else if (prefixedSuppression != literalStringTemplateEntry.text) { emit(offset, "Identifier to suppress ktlint rule must be fully qualified with the rule set id", true) - if (autoCorrect) { - node - .createLiteralStringTemplateEntry(prefixedSuppression) - ?.let { literalStringTemplateEntry.replaceWith(it) } - } + .ifAutocorrectAllowed { + node + .createLiteralStringTemplateEntry(prefixedSuppression) + ?.let { literalStringTemplateEntry.replaceWith(it) } + } } } } @@ -145,38 +146,35 @@ public class KtlintSuppressionRule( ?.node private fun KtLintDirective.visitKtlintDirective( - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (ktlintDirectiveType) { KTLINT_DISABLE -> { if (node.elementType == EOL_COMMENT && node.prevLeaf().isWhiteSpaceWithNewline()) { - removeDanglingEolCommentWithKtlintDisableDirective(autoCorrect, emit) + removeDanglingEolCommentWithKtlintDisableDirective(emit) } else { - visitKtlintDisableDirective(autoCorrect, emit) + visitKtlintDisableDirective(emit) } } KTLINT_ENABLE -> { - removeKtlintEnableDirective(autoCorrect, emit) + removeKtlintEnableDirective(emit) } } } private fun KtLintDirective.removeDanglingEolCommentWithKtlintDisableDirective( - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit(offset, "Directive 'ktlint-disable' in EOL comment is ignored as it is not preceded by a code element", true) - if (autoCorrect) { - node.removePrecedingWhitespace() - node.remove() - } + .ifAutocorrectAllowed { + node.removePrecedingWhitespace() + node.remove() + } } private fun KtLintDirective.visitKtlintDisableDirective( - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == BLOCK_COMMENT && hasNoMatchingKtlintEnableDirective(ruleIdValidator)) { emit( @@ -187,7 +185,7 @@ public class KtlintSuppressionRule( ) return } - emit(offset, "Directive 'ktlint-disable' is deprecated. Replace with @Suppress annotation", true) + val autocorrectDecision = emit(offset, "Directive 'ktlint-disable' is deprecated. Replace with @Suppress annotation", true) suppressionIdChanges .filterIsInstance() .forEach { ktlintDirectiveChange -> @@ -199,7 +197,7 @@ public class KtlintSuppressionRule( false, ) } - if (autoCorrect) { + autocorrectDecision.ifAutocorrectAllowed { val suppressionIds = suppressionIdChanges .filterIsInstance() @@ -225,14 +223,13 @@ public class KtlintSuppressionRule( } private fun KtLintDirective.removeKtlintEnableDirective( - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit(offset, "Directive 'ktlint-enable' is obsolete after migrating to suppress annotations", true) - if (autoCorrect) { - node.removePrecedingWhitespace() - node.remove() - } + .ifAutocorrectAllowed { + node.removePrecedingWhitespace() + node.remove() + } } private fun String.isUnknownKtlintSuppression(): Boolean = @@ -298,7 +295,6 @@ private class KtLintDirective( private fun ASTNode.surroundsMultipleListElements(): Boolean { require(ktlintDirectiveType == KTLINT_DISABLE && elementType == BLOCK_COMMENT) return if (treeParent.elementType in listTypeTokenSet) { - val firstElementAfterEnableDirective = nextSibling { it.elementType in listElementTypeTokenSet } findMatchingKtlintEnableDirective(ruleIdValidator) ?.siblings(false) ?.takeWhile { it != this } @@ -389,6 +385,7 @@ private fun String.toKtlintDirectiveTypeOrNull() = private fun String.toSuppressionIdChanges(ruleIdValidator: (String) -> Boolean) = trim() .split(" ") + .asSequence() .map { it.trim() } .filter { it.isNotBlank() } .map { it.qualifiedRuleIdString() } diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/DisabledRulesTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/DisabledRulesTest.kt index f5c4d0014b..3825356c07 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/DisabledRulesTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/DisabledRulesTest.kt @@ -1,6 +1,8 @@ package com.pinterest.ktlint.rule.engine.api +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecution @@ -61,11 +63,11 @@ class DisabledRulesTest { ) : Rule( ruleId = ruleId, about = About(), - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit(node.startOffset, SOME_NO_VAR_RULE_VIOLATION, false) } diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt index 1fbc943bf1..9ab4a9dae7 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt @@ -12,6 +12,7 @@ import com.pinterest.ktlint.rule.engine.api.RuleExecutionCall.RuleMethod.BEFORE_ import com.pinterest.ktlint.rule.engine.api.RuleExecutionCall.RuleMethod.BEFORE_FIRST import com.pinterest.ktlint.rule.engine.api.RuleExecutionCall.VisitNodeType.CHILD import com.pinterest.ktlint.rule.engine.api.RuleExecutionCall.VisitNodeType.ROOT +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER @@ -20,11 +21,15 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.PACKAGE_DIRECTIVE import com.pinterest.ktlint.rule.engine.core.api.ElementType.REGULAR_STRING_PART import com.pinterest.ktlint.rule.engine.core.api.Rule import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAsLateAsPossible +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleProvider +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.END_OF_LINE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isRoot import org.assertj.core.api.Assertions.assertThat +import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType @@ -119,7 +124,7 @@ class KtLintTest { } }, ), - ).format(Code.fromSnippet("fun main() {}")) + ).format(Code.fromSnippet("fun main() {}")) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(numberOfRootNodesVisited).isEqualTo(1) } @@ -135,14 +140,14 @@ class KtLintTest { val foo = "$STRING_VALUE_NOT_TO_BE_CORRECTED" val bar = "$STRING_VALUE_AFTER_AUTOCORRECT" """.trimIndent() - val callbacks = mutableListOf() + val callbacks = mutableSetOf() val actualFormattedCode = KtLintRuleEngine( ruleProviders = setOf( RuleProvider { AutoCorrectErrorRule() }, ), - ).format(Code.fromSnippet(code)) { e, corrected -> + ).format(Code.fromSnippet(code)) { e -> callbacks.add( CallbackResult( line = e.line, @@ -150,9 +155,14 @@ class KtLintTest { ruleId = e.ruleId, detail = e.detail, canBeAutoCorrected = e.canBeAutoCorrected, - corrected = corrected, + corrected = e.canBeAutoCorrected, ), ) + if (e.canBeAutoCorrected) { + AutocorrectDecision.ALLOW_AUTOCORRECT + } else { + AutocorrectDecision.NO_AUTOCORRECT + } } assertThat(actualFormattedCode).isEqualTo(formattedCode) assertThat(callbacks).containsExactly( @@ -282,7 +292,11 @@ class KtLintTest { @Test fun testFormatUnicodeBom() { - val code = getResourceAsText("spec/format-unicode-bom.kt.spec") + val code = + getResourceAsText("spec/format-unicode-bom.kt.spec") + // Standardize code to use LF as line separator regardless of OS + .replace("\r\n", "\n") + .replace("\r", "\n") val actual = KtLintRuleEngine( @@ -290,7 +304,13 @@ class KtLintTest { setOf( RuleProvider { DummyRule() }, ), - ).format(Code.fromSnippet(code)) + editorConfigOverride = + EditorConfigOverride.from( + // The code sample use LF as line separator, so ensure that formatted code uses that as well, as otherwise the test + // breaks on Windows OS + END_OF_LINE_PROPERTY to PropertyType.EndOfLineValue.lf, + ), + ).format(Code.fromSnippet(code)) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(actual).isEqualTo(code) } @@ -311,7 +331,7 @@ class KtLintTest { ) }, ), - ).format(Code.fromSnippet("class Foo")) + ).format(Code.fromSnippet("class Foo")) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(ruleExecutionCalls).containsExactly( RuleExecutionCall(SimpleTestRule.RULE_ID_STOP_TRAVERSAL, BEFORE_FIRST), @@ -346,7 +366,7 @@ class KtLintTest { ) }, ), - ).format(Code.fromSnippet(code)) + ).format(Code.fromSnippet(code)) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(ruleExecutionCalls) .filteredOn { it.elementType == null || it.classIdentifier != null } @@ -387,7 +407,7 @@ class KtLintTest { ) }, ), - ).format(Code.fromSnippet(code)) + ).format(Code.fromSnippet(code)) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(ruleExecutionCalls) .filteredOn { it.elementType == null || it.classIdentifier != null } @@ -417,7 +437,7 @@ class KtLintTest { ) }, ), - ).format(Code.fromSnippet("class Foo")) + ).format(Code.fromSnippet("class Foo")) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(ruleExecutionCalls).containsExactly( RuleExecutionCall(SimpleTestRule.RULE_ID_STOP_TRAVERSAL, BEFORE_FIRST), @@ -437,7 +457,7 @@ class KtLintTest { setOf( RuleProvider { WithStateRule() }, ), - ).format(EMPTY_CODE_SNIPPET) + ).format(EMPTY_CODE_SNIPPET) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } } @Test @@ -454,7 +474,7 @@ class KtLintTest { setOf( RuleProvider { AutoCorrectErrorRule() }, ), - ).format(Code.fromSnippet(code)) + ).format(Code.fromSnippet(code)) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(actualFormattedCode).isEqualTo(code) } @@ -475,11 +495,11 @@ private open class DummyRule( ) : Rule( ruleId = RuleId("test:dummy"), about = About(), - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { block(node) } @@ -492,19 +512,19 @@ private class AutoCorrectErrorRule : Rule( ruleId = AUTOCORRECT_ERROR_RULE_ID, about = About(), - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == REGULAR_STRING_PART) { when (node.text) { STRING_VALUE_TO_BE_AUTOCORRECTED -> { emit(node.startOffset, ERROR_MESSAGE_CAN_BE_AUTOCORRECTED, true) - if (autoCorrect) { - (node as LeafElement).rawReplaceWithText(STRING_VALUE_AFTER_AUTOCORRECT) - } + .ifAutocorrectAllowed { + (node as LeafElement).rawReplaceWithText(STRING_VALUE_AFTER_AUTOCORRECT) + } } STRING_VALUE_NOT_TO_BE_CORRECTED -> @@ -541,7 +561,8 @@ private class SimpleTestRule( ruleId = ruleId, about = About(), visitorModifiers, - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeFirstNode(editorConfig: EditorConfig) { ruleExecutionCalls.add(RuleExecutionCall(ruleId, BEFORE_FIRST)) if (stopTraversalInBeforeFirstNode) { @@ -551,8 +572,7 @@ private class SimpleTestRule( override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { ruleExecutionCalls.add(node.toRuleExecutionCall(ruleId, BEFORE_CHILDREN)) if (stopTraversalInBeforeVisitChildNodes(node)) { @@ -562,8 +582,7 @@ private class SimpleTestRule( override fun afterVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { ruleExecutionCalls.add(node.toRuleExecutionCall(ruleId, AFTER_CHILDREN)) if (stopTraversalInAfterVisitChildNodes(node)) { @@ -609,7 +628,7 @@ private data class RuleExecutionCall( val elementType: IElementType? = null, val classIdentifier: String? = null, ) { - enum class RuleMethod { BEFORE_FIRST, BEFORE_CHILDREN, VISIT, AFTER_CHILDREN, AFTER_LAST } + enum class RuleMethod { BEFORE_FIRST, BEFORE_CHILDREN, AFTER_CHILDREN, AFTER_LAST } enum class VisitNodeType { ROOT, CHILD } } @@ -643,7 +662,8 @@ private class WithStateRule : Rule( ruleId = RuleId("test:with-state"), about = About(), - ) { + ), + RuleAutocorrectApproveHandler { private var hasNotBeenVisitedYet = true override fun beforeFirstNode(editorConfig: EditorConfig) { @@ -655,8 +675,7 @@ private class WithStateRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit(node.startOffset, "Fake violation which can be autocorrected", true) } diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleProviderSorterTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleProviderSorterTest.kt index acf18b5d66..c73aad4689 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleProviderSorterTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleProviderSorterTest.kt @@ -1,8 +1,10 @@ package com.pinterest.ktlint.rule.engine.internal +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.Rule import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule.Mode.ONLY_WHEN_RUN_AFTER_RULE_IS_LOADED_AND_ENABLED 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.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.RuleSetId @@ -459,13 +461,13 @@ class RuleProviderSorterTest { ruleId = ruleId, about = About(), visitorModifiers, - ) { + ), + RuleAutocorrectApproveHandler { constructor(ruleId: RuleId, visitorModifier: VisitorModifier) : this(ruleId, setOf(visitorModifier)) override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Unit = throw UnsupportedOperationException( "Rule should never be really invoked because that is not the aim of this unit test.", diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilderTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilderTest.kt index e55951e3bf..de4dc18ea9 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilderTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocatorBuilderTest.kt @@ -6,8 +6,10 @@ import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride.Companion.EMPTY import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride.Companion.plus import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine import com.pinterest.ktlint.rule.engine.api.LintError +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.RuleSetId @@ -364,7 +366,7 @@ class SuppressionLocatorBuilderTest { .plus( STANDARD_NO_FOO_IDENTIFIER_RULE_ID.createRuleExecutionEditorConfigProperty() to RuleExecution.enabled, ), - ).format(Code.fromSnippet(code)) { _, _ -> } + ).format(Code.fromSnippet(code)) { _ -> AutocorrectDecision.ALLOW_AUTOCORRECT } assertThat(actual).isEqualTo(formattedCode) } @@ -374,11 +376,11 @@ class SuppressionLocatorBuilderTest { ) : Rule( ruleId = id, about = About(), - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == ElementType.IDENTIFIER && node.text.startsWith("foo")) { emit(node.startOffset, "Line should not contain a foo identifier", false) diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProviderTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProviderTest.kt index 2aa6abf490..2af4c667b4 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProviderTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/VisitorProviderTest.kt @@ -12,12 +12,11 @@ class VisitorProviderTest { VisitorProvider( ruleProviders = emptySet(), recreateRuleSorter = true, - ).visitor() - .invoke { _ -> - assertThat(false) - .withFailMessage("The visitor provider should not have called this lambda in case it has no rule providers") - .isTrue - } + ).rules.forEach { _ -> + assertThat(false) + .withFailMessage("The visitor provider should not have called this lambda in case it has no rule providers") + .isTrue + } } } } diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/InternalRuleProvidersFilterTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/InternalRuleProvidersFilterTest.kt index 2a6ce8388c..a740cebfcd 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/InternalRuleProvidersFilterTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/InternalRuleProvidersFilterTest.kt @@ -1,7 +1,9 @@ package com.pinterest.ktlint.rule.engine.internal.rulefilter import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.RuleSetId @@ -46,11 +48,11 @@ class InternalRuleProvidersFilterTest { ruleId = ruleId, about = About(), visitorModifiers, - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Unit = throw UnsupportedOperationException( "Rule should never be really invoked because that is not the aim of this unit test.", diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RunAfterRuleFilterTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RunAfterRuleFilterTest.kt index 60b9fc7687..78b5b9ba4e 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RunAfterRuleFilterTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RunAfterRuleFilterTest.kt @@ -1,8 +1,10 @@ package com.pinterest.ktlint.rule.engine.internal.rulefilter +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.Rule import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule.Mode.ONLY_WHEN_RUN_AFTER_RULE_IS_LOADED_AND_ENABLED 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.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.RuleSetId @@ -374,13 +376,13 @@ class RunAfterRuleFilterTest { ruleId = ruleId, about = About(), visitorModifiers, - ) { + ), + RuleAutocorrectApproveHandler { constructor(ruleId: RuleId, visitorModifier: VisitorModifier) : this(ruleId, setOf(visitorModifier)) override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Unit = throw UnsupportedOperationException( "Rule should never be really invoked because that is not the aim of this unit test.", diff --git a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api index a3455bf324..9952db820e 100644 --- a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api +++ b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api @@ -1,4 +1,6 @@ -public class com/pinterest/ktlint/ruleset/standard/StandardRule : com/pinterest/ktlint/rule/engine/core/api/Rule { +public class com/pinterest/ktlint/ruleset/standard/StandardRule : com/pinterest/ktlint/rule/engine/core/api/Rule, com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler { + public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V public fun getUsesEditorConfigProperties ()Ljava/util/Set; public fun getVisitorModifiers ()Ljava/util/Set; } @@ -11,7 +13,7 @@ public final class com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider public final class com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleKt { @@ -21,7 +23,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleKt public final class com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public static final field ERROR_MESSAGE Ljava/lang/String; public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRuleKt { @@ -32,7 +34,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrapp public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule$Companion { @@ -46,7 +48,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrapp public final class com/pinterest/ktlint/ruleset/standard/rules/BackingPropertyNamingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/BackingPropertyNamingRuleKt { @@ -56,7 +58,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/BackingPropertyNa public final class com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionWrappingRuleKt { @@ -65,18 +67,18 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/BinaryExpressionW public final class com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRuleKt { public static final fun getBLANK_LINE_BEFORE_DECLARATION_RULE_ID ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleId; } -public final class com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { +public final class com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental, com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler { public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions$Companion { @@ -89,7 +91,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenW public final class com/pinterest/ktlint/ruleset/standard/rules/BlockCommentInitialStarAlignmentRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/BlockCommentInitialStarAlignmentRuleKt { @@ -100,7 +102,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContin public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule$Companion { @@ -114,7 +116,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContin public final class com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRuleKt { @@ -123,7 +125,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule public final class com/pinterest/ktlint/ruleset/standard/rules/ClassNamingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ClassNamingRuleKt { @@ -134,7 +136,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRul public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule$Companion { @@ -147,7 +149,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRul public final class com/pinterest/ktlint/ruleset/standard/rules/CommentSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/CommentSpacingRuleKt { @@ -156,7 +158,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/CommentSpacingRul public final class com/pinterest/ktlint/ruleset/standard/rules/CommentWrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/CommentWrappingRuleKt { @@ -166,7 +168,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/CommentWrappingRu public final class com/pinterest/ktlint/ruleset/standard/rules/ConditionWrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ConditionWrappingRuleKt { @@ -176,7 +178,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ConditionWrapping public final class com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRuleKt { @@ -185,7 +187,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWr public final class com/pinterest/ktlint/ruleset/standard/rules/DiscouragedCommentLocationRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/DiscouragedCommentLocationRuleKt { @@ -194,7 +196,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/DiscouragedCommen public final class com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRuleKt { @@ -204,7 +206,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCase public final class com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRuleKt { @@ -213,7 +215,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRuleK public final class com/pinterest/ktlint/ruleset/standard/rules/FilenameRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FilenameRuleKt { @@ -223,7 +225,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FilenameRuleKt { public final class com/pinterest/ktlint/ruleset/standard/rules/FinalNewlineRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FinalNewlineRuleKt { @@ -232,7 +234,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FinalNewlineRuleK public final class com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRuleKt { @@ -242,7 +244,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacing public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionExpressionBodyRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionExpressionBodyRuleKt { @@ -252,7 +254,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionExpressio public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionLiteralRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionLiteralRuleKt { @@ -263,7 +265,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRul public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule$Companion { @@ -277,7 +279,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRul public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionReturnTypeSpacingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionReturnTypeSpacingRuleKt { @@ -288,7 +290,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionSignature public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule$Companion { @@ -311,7 +313,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionSignature public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionStartOfBodySpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionStartOfBodySpacingRuleKt { @@ -320,7 +322,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionStartOfBo public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeModifierSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeModifierSpacingRuleKt { @@ -329,7 +331,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeModif public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeReferenceSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeReferenceSpacingRuleKt { @@ -339,7 +341,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeRefer public final class com/pinterest/ktlint/ruleset/standard/rules/IfElseBracingRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/IfElseBracingRuleKt { @@ -349,7 +351,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/IfElseBracingRule public final class com/pinterest/ktlint/ruleset/standard/rules/IfElseWrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/IfElseWrappingRuleKt { @@ -360,7 +362,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRul public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule$Companion { @@ -371,14 +373,14 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRul public static final fun getIMPORT_ORDERING_RULE_ID ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleId; } -public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRule : com/pinterest/ktlint/ruleset/standard/StandardRule { +public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler { public static final field KDOC_CONTINUATION_INDENT Ljava/lang/String; public static final field TYPE_CONSTRAINT_CONTINUATION_INDENT Ljava/lang/String; public fun ()V public fun afterLastNode ()V - public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleKt { @@ -387,7 +389,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleKt public final class com/pinterest/ktlint/ruleset/standard/rules/KdocRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/KdocRuleKt { @@ -396,7 +398,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/KdocRuleKt { public final class com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRuleKt { @@ -407,7 +409,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule$Companion { @@ -420,7 +422,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule public final class com/pinterest/ktlint/ruleset/standard/rules/MixedConditionOperatorsRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/MixedConditionOperatorsRuleKt { @@ -429,7 +431,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/MixedConditionOpe public final class com/pinterest/ktlint/ruleset/standard/rules/ModifierListSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ModifierListSpacingRuleKt { @@ -438,7 +440,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ModifierListSpaci public final class com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRuleKt { @@ -448,7 +450,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule public final class com/pinterest/ktlint/ruleset/standard/rules/MultiLineIfElseRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/MultiLineIfElseRuleKt { @@ -458,7 +460,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/MultiLineIfElseRu public final class com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRuleKt { @@ -468,7 +470,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressi public final class com/pinterest/ktlint/ruleset/standard/rules/MultilineLoopRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/MultilineLoopRuleKt { @@ -477,7 +479,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/MultilineLoopRule public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBeforeRbraceRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBeforeRbraceRuleKt { @@ -486,7 +488,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBefore public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineInListRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineInListRuleKt { @@ -495,7 +497,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineInList public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRuleKt { @@ -504,7 +506,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInCha public final class com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveBlankLinesRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveBlankLinesRuleKt { @@ -513,7 +515,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveBlan public final class com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRuleKt { @@ -522,7 +524,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveComm public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRuleKt { @@ -531,7 +533,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyR public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFileRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFileRuleKt { @@ -541,7 +543,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFileRuleKt public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInClassBodyRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInClassBodyRuleKt { @@ -550,7 +552,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineI public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInMethodBlockRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInMethodBlockRuleKt { @@ -559,7 +561,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineI public final class com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterElseRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterElseRuleKt { @@ -568,7 +570,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterE public final class com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakBeforeAssignmentRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakBeforeAssignmentRuleKt { @@ -577,7 +579,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakBefore public final class com/pinterest/ktlint/ruleset/standard/rules/NoMultipleSpacesRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoMultipleSpacesRuleKt { @@ -586,7 +588,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoMultipleSpacesR public final class com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRuleKt { @@ -595,7 +597,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRuleK public final class com/pinterest/ktlint/ruleset/standard/rules/NoSingleLineBlockCommentRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoSingleLineBlockCommentRuleKt { @@ -604,7 +606,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoSingleLineBlock public final class com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRuleKt { @@ -613,7 +615,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesR public final class com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRuleKt { @@ -622,8 +624,8 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRuleK public final class com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRuleKt { @@ -634,7 +636,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImports public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule$Companion { @@ -647,7 +649,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImports public final class com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpacingRuleKt { @@ -656,7 +658,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpaci public final class com/pinterest/ktlint/ruleset/standard/rules/PackageNameRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/PackageNameRuleKt { @@ -666,7 +668,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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpacingRuleKt { @@ -676,7 +678,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterListSpac public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRuleKt { @@ -686,7 +688,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrap public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterWrappingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterWrappingRuleKt { @@ -696,7 +698,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ParameterWrapping public final class com/pinterest/ktlint/ruleset/standard/rules/PropertyNamingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public static final field SERIAL_VERSION_UID_PROPERTY_NAME Ljava/lang/String; public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/PropertyNamingRuleKt { @@ -706,7 +708,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/PropertyNamingRul public final class com/pinterest/ktlint/ruleset/standard/rules/PropertyWrappingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/PropertyWrappingRuleKt { @@ -715,7 +717,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/PropertyWrappingR public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleKt { @@ -724,7 +726,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngl public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRuleKt { @@ -733,7 +735,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColo public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCommaRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCommaRuleKt { @@ -743,7 +745,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundComm public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCurlyRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCurlyRuleKt { @@ -752,7 +754,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCurl public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDotRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDotRuleKt { @@ -761,7 +763,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDotR public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRuleKt { @@ -770,7 +772,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoub public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundKeywordRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundKeywordRuleKt { @@ -779,7 +781,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundKeyw public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRuleKt { @@ -788,7 +790,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOper public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRuleKt { @@ -797,7 +799,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundPare public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundRangeOperatorRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundRangeOperatorRuleKt { @@ -806,7 +808,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundRang public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundSquareBracketsRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundSquareBracketsRuleKt { @@ -815,7 +817,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundSqua public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundUnaryOperatorRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundUnaryOperatorRuleKt { @@ -824,7 +826,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundUnar public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRuleKt { @@ -833,7 +835,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDec public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRuleKt { @@ -842,7 +844,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDec public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRuleKt { @@ -852,7 +854,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFun public final class com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRuleKt { @@ -863,7 +865,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/StringTemplateInd public static final field RAW_STRING_LITERAL_QUOTES Ljava/lang/String; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/StringTemplateIndentRuleKt { @@ -872,7 +874,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/StringTemplateInd public final class com/pinterest/ktlint/ruleset/standard/rules/StringTemplateRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/StringTemplateRuleKt { @@ -883,7 +885,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCa public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule$Companion { @@ -898,7 +900,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDe public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule$Companion; 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule$Companion { @@ -912,7 +914,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDe public final class com/pinterest/ktlint/ruleset/standard/rules/TryCatchFinallySpacingRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle { 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/TryCatchFinallySpacingRuleKt { @@ -921,7 +923,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TryCatchFinallySp public final class com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentCommentRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentCommentRuleKt { @@ -931,7 +933,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentComme public final class com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRuleKt { @@ -940,7 +942,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListS public final class com/pinterest/ktlint/ruleset/standard/rules/TypeParameterCommentRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/TypeParameterCommentRuleKt { @@ -950,7 +952,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TypeParameterComm public final class com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule : 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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRuleKt { @@ -959,7 +961,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/TypeParameterList public final class com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParenthesesBeforeTrailingLambdaRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParenthesesBeforeTrailingLambdaRuleKt { @@ -968,7 +970,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParent public final class com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentCommentRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentCommentRuleKt { @@ -977,7 +979,7 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentComm public final class com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCommentRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCommentRuleKt { @@ -986,9 +988,9 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCom public final class com/pinterest/ktlint/ruleset/standard/rules/WrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V - public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V + public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)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 + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V } public final class com/pinterest/ktlint/ruleset/standard/rules/WrappingRuleKt { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRule.kt index 19b68659ba..77ad24ca56 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.RuleSetId import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty @@ -25,4 +26,5 @@ public open class StandardRule internal constructor( visitorModifiers = visitorModifiers, usesEditorConfigProperties = usesEditorConfigProperties, about = STANDARD_RULE_ABOUT, - ) + ), + RuleAutocorrectApproveHandler diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt index 2bdabde834..2ce641b009 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -29,6 +30,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -110,39 +112,37 @@ public class AnnotationRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { FILE_ANNOTATION_LIST -> { - visitAnnotationList(node, emit, autoCorrect) - visitFileAnnotationList(node, emit, autoCorrect) + visitAnnotationList(node, emit) + visitFileAnnotationList(node, emit) } ANNOTATED_EXPRESSION, MODIFIER_LIST -> { - visitAnnotationList(node, emit, autoCorrect) + visitAnnotationList(node, emit) } ANNOTATION -> { // Annotation array // @[...] - visitAnnotation(node, emit, autoCorrect) + visitAnnotation(node, emit) } ANNOTATION_ENTRY -> { - visitAnnotationEntry(node, emit, autoCorrect) + visitAnnotationEntry(node, emit) } TYPE_ARGUMENT_LIST -> { - visitTypeArgumentList(node, emit, autoCorrect) + visitTypeArgumentList(node, emit) } } } private fun visitAnnotationList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType in ANNOTATION_CONTAINER) @@ -182,14 +182,14 @@ public class AnnotationRule : // wrapped if (!prevLeaf.textContains('\n')) { emit(prevLeaf.startOffset, "Expected newline before annotation", true) - if (autoCorrect) { - prevLeaf.upsertWhitespaceBeforeMe( - prevLeaf - .text - .substringBeforeLast('\n', "") - .plus(expectedIndent), - ) - } + .ifAutocorrectAllowed { + prevLeaf.upsertWhitespaceBeforeMe( + prevLeaf + .text + .substringBeforeLast('\n', "") + .plus(expectedIndent), + ) + } } } } @@ -204,9 +204,9 @@ public class AnnotationRule : // Let the indentation rule determine the exact indentation and only report and fix when the line needs to be wrapped if (!prevLeaf.textContains('\n')) { emit(prevLeaf.startOffset, "Expected newline after last annotation", true) - if (autoCorrect) { - prevLeaf.upsertWhitespaceAfterMe(expectedIndent) - } + .ifAutocorrectAllowed { + prevLeaf.upsertWhitespaceAfterMe(expectedIndent) + } } } @@ -219,9 +219,9 @@ public class AnnotationRule : // Let the indentation rule determine the exact indentation and only report and fix when the line needs to be wrapped if (!leaf.textContains('\n')) { emit(leaf.startOffset, "Expected newline", true) - if (autoCorrect) { - leaf.upsertWhitespaceBeforeMe(node.indent()) - } + .ifAutocorrectAllowed { + leaf.upsertWhitespaceBeforeMe(node.indent()) + } } } } @@ -269,8 +269,7 @@ public class AnnotationRule : private fun visitTypeArgumentList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .children() @@ -280,13 +279,12 @@ public class AnnotationRule : .mapNotNull { it.findChildByType(MODIFIER_LIST) } .filter { it.elementType == MODIFIER_LIST } .any { it.shouldWrapAnnotations() } - .ifTrue { wrapTypeArgumentList(node, emit, autoCorrect) } + .ifTrue { wrapTypeArgumentList(node, emit) } } private fun wrapTypeArgumentList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .children() @@ -295,9 +293,9 @@ public class AnnotationRule : val prevLeaf = typeProjection.prevLeaf().takeIf { it.isWhiteSpace() } if (prevLeaf == null || prevLeaf.isWhiteSpaceWithoutNewline()) { emit(typeProjection.startOffset - 1, "Expected newline", true) - if (autoCorrect) { - typeProjection.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) - } + .ifAutocorrectAllowed { + typeProjection.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) + } } } @@ -307,17 +305,16 @@ public class AnnotationRule : val prevLeaf = gt.prevLeaf().takeIf { it.isWhiteSpace() } if (prevLeaf == null || prevLeaf.isWhiteSpaceWithoutNewline()) { emit(gt.startOffset, "Expected newline", true) - if (autoCorrect) { - gt.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) - } + .ifAutocorrectAllowed { + gt.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) + } } } } private fun visitAnnotationEntry( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == ANNOTATION_ENTRY) if (node.isPrecededByOtherAnnotationEntryOnTheSameLine() && node.isPrecededByAnnotationOnAnotherLine()) { @@ -329,8 +326,7 @@ public class AnnotationRule : node.startOffset, "All annotations should either be on a single line or all annotations should be on a separate line", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { node .firstChildLeafOrSelf() .upsertWhitespaceBeforeMe(getNewlineWithIndent(node.treeParent)) @@ -409,8 +405,7 @@ public class AnnotationRule : private fun visitFileAnnotationList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .lastChildLeafOrSelf() @@ -423,8 +418,7 @@ public class AnnotationRule : codeLeaf.startOffset, "File annotations should be separated from file contents with a blank line", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { codeLeaf.upsertWhitespaceBeforeMe("\n\n") } } @@ -433,26 +427,21 @@ public class AnnotationRule : private fun visitAnnotation( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == ANNOTATION) if ((node.isFollowedByOtherAnnotationEntry() && node.isOnSameLineAsNextAnnotationEntry()) || (node.isPrecededByOtherAnnotationEntry() && node.isOnSameLineAsPreviousAnnotationEntry()) ) { - emit( - node.startOffset, - "@[...] style annotations should be on a separate line from other annotations.", - true, - ) - if (autoCorrect) { - if (node.isFollowedByOtherAnnotationEntry()) { - node.upsertWhitespaceAfterMe(getNewlineWithIndent(node.treeParent)) - } else if (node.isPrecededByOtherAnnotationEntry()) { - node.upsertWhitespaceBeforeMe(getNewlineWithIndent(node.treeParent)) + emit(node.startOffset, "@[...] style annotations should be on a separate line from other annotations.", true) + .ifAutocorrectAllowed { + if (node.isFollowedByOtherAnnotationEntry()) { + node.upsertWhitespaceAfterMe(getNewlineWithIndent(node.treeParent)) + } else if (node.isPrecededByOtherAnnotationEntry()) { + node.upsertWhitespaceBeforeMe(getNewlineWithIndent(node.treeParent)) + } } - } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt index 5e66a2ebe8..634bdc422d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -37,8 +39,7 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType != ElementType.MODIFIER_LIST && node.elementType != ElementType.FILE_ANNOTATION_LIST) { return @@ -90,30 +91,30 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { if (node.elementType != ElementType.FILE_ANNOTATION_LIST && next.isPartOfComment()) { val psi = node.psi emit(psi.endOffset, ERROR_MESSAGE, true) - if (autoCorrect) { - // Special-case autocorrection when the annotation is separated from the annotated construct - // by a comment: we need to swap the order of the comment and the annotation - // Remove the annotation and the following whitespace - val eolComment = node.nextSibling { it.isCommentOnSameLineAsPrevLeaf() } - if (eolComment != null) { - eolComment.prevSibling { it.isWhiteSpace() }?.let { it.treeParent.removeChild(it) } - eolComment.nextSibling { it.isWhiteSpace() }?.let { it.treeParent.removeChild(it) } - eolComment.treeParent?.removeChild(eolComment) - } else { - node.nextSibling { it.isWhiteSpace() }?.let { it.treeParent?.removeChild(it) } - } - node.treeParent.removeChild(node) + .ifAutocorrectAllowed { + // Special-case autocorrection when the annotation is separated from the annotated construct + // by a comment: we need to swap the order of the comment and the annotation + // Remove the annotation and the following whitespace + val eolComment = node.nextSibling { it.isCommentOnSameLineAsPrevLeaf() } + if (eolComment != null) { + eolComment.prevSibling { it.isWhiteSpace() }?.let { it.treeParent.removeChild(it) } + eolComment.nextSibling { it.isWhiteSpace() }?.let { it.treeParent.removeChild(it) } + eolComment.treeParent?.removeChild(eolComment) + } else { + node.nextSibling { it.isWhiteSpace() }?.let { it.treeParent?.removeChild(it) } + } + node.treeParent.removeChild(node) - // Insert the annotation prior to the annotated construct - val beforeAnchor = next.nextCodeSibling() - val treeParent = next.treeParent - treeParent.addChild(node, beforeAnchor) - if (eolComment != null) { - treeParent.addChild(PsiWhiteSpaceImpl(" "), beforeAnchor) - treeParent.addChild(eolComment, beforeAnchor) + // Insert the annotation prior to the annotated construct + val beforeAnchor = next.nextCodeSibling() + val treeParent = next.treeParent + treeParent.addChild(node, beforeAnchor) + if (eolComment != null) { + treeParent.addChild(PsiWhiteSpaceImpl(" "), beforeAnchor) + treeParent.addChild(eolComment, beforeAnchor) + } + treeParent.addChild(PsiWhiteSpaceImpl("\n"), beforeAnchor) } - treeParent.addChild(PsiWhiteSpaceImpl("\n"), beforeAnchor) - } } } if (whiteSpaces.isNotEmpty() && node.elementType != ElementType.FILE_ANNOTATION_LIST) { @@ -121,10 +122,10 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { if (whiteSpaces.any { psi -> psi.textToCharArray().count { it == '\n' } > 1 }) { val psi = node.psi emit(psi.endOffset, ERROR_MESSAGE, true) - if (autoCorrect) { - removeIntraLineBreaks(node, annotations.last()) - removeExtraLineBreaks(node) - } + .ifAutocorrectAllowed { + removeIntraLineBreaks(node, annotations.last()) + removeExtraLineBreaks(node) + } } } } @@ -177,7 +178,7 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { last: KtAnnotationEntry, ) { val txt = node.text - // Pull the next before raw replace or it will blow up + // Pull the next before raw replace, or it will blow up val lNext = node.nextLeaf() if (node is PsiWhiteSpaceImpl) { if (txt.toCharArray().count { it == '\n' } > 1) { 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 7bb95cb356..ad47b8c1a0 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 @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLLECTION_LITERAL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION @@ -24,6 +25,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProper 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -96,8 +98,7 @@ public class ArgumentListWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (editorConfigIndent.disabled) { return @@ -107,7 +108,7 @@ public class ArgumentListWrappingRule : if (needToWrapArgumentList(node)) { node .children() - .forEach { child -> wrapArgumentInList(child, emit, autoCorrect) } + .forEach { child -> wrapArgumentInList(child, emit) } } } } @@ -186,17 +187,16 @@ public class ArgumentListWrappingRule : private fun wrapArgumentInList( child: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (child.elementType) { LPAR -> { val prevLeaf = child.prevLeaf() if (prevLeaf is PsiWhiteSpace && prevLeaf.textContains('\n')) { emit(child.startOffset, errorMessage(child), true) - if (autoCorrect) { - prevLeaf.delete() - } + .ifAutocorrectAllowed { + prevLeaf.delete() + } } } @@ -217,20 +217,20 @@ public class ArgumentListWrappingRule : } else { // The current child needs to be wrapped to a newline. emit(child.startOffset, errorMessage(child), true) - if (autoCorrect) { - // The indentation is purely based on the previous leaf only. Note that in - // autoCorrect mode the indent rule, if enabled, runs after this rule and - // determines the final indentation. But if the indent rule is disabled then the - // indent of this rule is kept. - (prevLeaf as LeafPsiElement).rawReplaceWithText(intendedIndent) - } + .ifAutocorrectAllowed { + // The indentation is purely based on the previous leaf only. Note that in + // autoCorrect mode the indent rule, if enabled, runs after this rule and + // determines the final indentation. But if the indent rule is disabled then the + // indent of this rule is kept. + (prevLeaf as LeafPsiElement).rawReplaceWithText(intendedIndent) + } } } else { // Insert a new whitespace element in order to wrap the current child to a new line. emit(child.startOffset, errorMessage(child), true) - if (autoCorrect) { - child.treeParent.addChild(PsiWhiteSpaceImpl(intendedIndent), child) - } + .ifAutocorrectAllowed { + child.treeParent.addChild(PsiWhiteSpaceImpl(intendedIndent), child) + } } // Indentation of child nodes need to be fixed by the IndentationRule. } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BackingPropertyNamingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BackingPropertyNamingRule.kt index 3b8e727a49..709f104dae 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BackingPropertyNamingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BackingPropertyNamingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER import com.pinterest.ktlint.rule.engine.core.api.ElementType.INTERNAL_KEYWORD @@ -38,8 +39,7 @@ public class BackingPropertyNamingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { node.elementType == PROPERTY } @@ -48,7 +48,7 @@ public class BackingPropertyNamingRule : private fun visitProperty( property: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { property .findChildByType(IDENTIFIER) @@ -60,7 +60,7 @@ public class BackingPropertyNamingRule : private fun visitBackingProperty( identifier: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { identifier .text 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 897eec3dce..523bc933cf 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 @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 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 @@ -26,6 +27,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -75,18 +77,16 @@ public class BinaryExpressionWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { - BINARY_EXPRESSION -> visitBinaryExpression(node, emit, autoCorrect) + BINARY_EXPRESSION -> visitBinaryExpression(node, emit) } } private fun visitBinaryExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == BINARY_EXPRESSION) @@ -104,8 +104,7 @@ public class BinaryExpressionWrappingRule : expression.startOffset, "Line is exceeding max line length. Break line between assignment and expression", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { expression.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(expression)) } } @@ -133,8 +132,7 @@ public class BinaryExpressionWrappingRule : expression.startOffset, "Line is exceeding max line length. Break line before expression", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { expression.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(expression)) } } @@ -145,13 +143,13 @@ public class BinaryExpressionWrappingRule : .firstOrNull { !it.isCodeLeaf() } ?.takeIf { it.elementType == CALL_EXPRESSION } ?.takeIf { it.causesMaxLineLengthToBeExceeded() } - ?.let { callExpression -> visitCallExpression(callExpression, emit, autoCorrect) } + ?.let { callExpression -> visitCallExpression(callExpression, emit) } // The remainder (operation reference plus right hand side) might still cause the max line length to be exceeded node .takeIf { node.lastChildNode.causesMaxLineLengthToBeExceeded() || node.isPartOfConditionExceedingMaxLineLength() } ?.findChildByType(OPERATION_REFERENCE) - ?.let { operationReference -> visitOperationReference(operationReference, emit, autoCorrect) } + ?.let { operationReference -> visitOperationReference(operationReference, emit) } } private fun ASTNode.isPartOfConditionExceedingMaxLineLength() = @@ -169,8 +167,7 @@ public class BinaryExpressionWrappingRule : private fun visitCallExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == CALL_EXPRESSION } @@ -186,17 +183,17 @@ public class BinaryExpressionWrappingRule : .findChildByType(LBRACE) ?.let { lbrace -> emit(lbrace.startOffset + 1, "Newline expected after '{'", true) - if (autoCorrect) { - lbrace.upsertWhitespaceAfterMe(indentConfig.childIndentOf(lbrace.treeParent)) - } + .ifAutocorrectAllowed { + 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)) - } + .ifAutocorrectAllowed { + rbrace.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node.treeParent)) + } } } } @@ -204,8 +201,7 @@ public class BinaryExpressionWrappingRule : private fun visitOperationReference( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == OPERATION_REFERENCE } @@ -227,9 +223,9 @@ public class BinaryExpressionWrappingRule : ?.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)) - } + .ifAutocorrectAllowed { + operationReference.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(operationReference)) + } } } else { operationReference @@ -239,8 +235,7 @@ public class BinaryExpressionWrappingRule : nextSibling.startOffset, "Line is exceeding max line length. Break line after '${operationReference.text}' in binary expression", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { nextSibling.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(operationReference)) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt index b43a56ee07..932c980720 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY @@ -20,6 +21,7 @@ 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -44,8 +46,7 @@ public class BlankLineBeforeDeclarationRule : Rule.OfficialCodeStyle { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { CLASS, @@ -55,14 +56,13 @@ public class BlankLineBeforeDeclarationRule : PROPERTY, PROPERTY_ACCESSOR, -> - visitDeclaration(node, autoCorrect, emit) + visitDeclaration(node, emit) } } private fun visitDeclaration( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isFirstCodeSiblingInClassBody()) { // No blank line between class signature and first declaration in class body: @@ -148,9 +148,9 @@ public class BlankLineBeforeDeclarationRule : prevLeaf != null && (!prevLeaf.isWhiteSpace() || !prevLeaf.text.startsWith("\n\n")) }?.let { insertBeforeNode -> emit(insertBeforeNode.startOffset, "Expected a blank line for this declaration", true) - if (autoCorrect) { - insertBeforeNode.upsertWhitespaceBeforeMe("\n".plus(node.indent())) - } + .ifAutocorrectAllowed { + insertBeforeNode.upsertWhitespaceBeforeMe("\n".plus(node.indent())) + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions.kt index 34a79aba34..37767d1ad5 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBetweenWhenConditions.kt @@ -1,15 +1,18 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler 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.EditorConfigProperty +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -36,6 +39,7 @@ public class BlankLineBetweenWhenConditions : id = "blank-line-between-when-conditions", usesEditorConfigProperties = setOf(LINE_BREAK_AFTER_WHEN_CONDITION_PROPERTY), ), + RuleAutocorrectApproveHandler, Rule.Experimental { private var lineBreakAfterWhenCondition = LINE_BREAK_AFTER_WHEN_CONDITION_PROPERTY.defaultValue @@ -45,31 +49,28 @@ public class BlankLineBetweenWhenConditions : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == ElementType.WHEN) { - visitWhenStatement(node, autoCorrect, emit) + visitWhenStatement(node, emit) } } private fun visitWhenStatement( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val hasMultilineWhenCondition = node.hasAnyMultilineWhenCondition() if (hasMultilineWhenCondition && lineBreakAfterWhenCondition) { - addBlankLinesBetweenWhenConditions(node, autoCorrect, emit) + addBlankLinesBetweenWhenConditions(node, emitAndApprove) } else { - removeBlankLinesBetweenWhenConditions(node, autoCorrect, emit) + removeBlankLinesBetweenWhenConditions(node, emitAndApprove) } } private fun addBlankLinesBetweenWhenConditions( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .children() @@ -81,13 +82,12 @@ public class BlankLineBetweenWhenConditions : .findWhitespaceAfterPreviousCodeSibling() ?.takeUnless { it.containsBlankLine() } ?.let { whitespaceBeforeWhenEntry -> - emit( + emitAndApprove( whitespaceBeforeWhenEntry.startOffset + 1, "Add a blank line between all when-condition in case at least one multiline when-condition is found in the " + "statement", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { whitespaceBeforeWhenEntry.upsertWhitespaceBeforeMe("\n${whenEntry.indent()}") } } @@ -109,8 +109,7 @@ public class BlankLineBetweenWhenConditions : private fun removeBlankLinesBetweenWhenConditions( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .children() @@ -122,12 +121,11 @@ public class BlankLineBetweenWhenConditions : .findWhitespaceAfterPreviousCodeSibling() ?.takeIf { it.containsBlankLine() } ?.let { whitespaceBeforeWhenEntry -> - emit( + emitAndApprove( whitespaceBeforeWhenEntry.startOffset + 1, "Unexpected blank lines between when-condition if all when-conditions are single lines", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { whitespaceBeforeWhenEntry.upsertWhitespaceBeforeMe("\n${whenEntry.indent(includeNewline = false)}") } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlockCommentInitialStarAlignmentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlockCommentInitialStarAlignmentRule.kt index 9d9242b431..22717b50fb 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlockCommentInitialStarAlignmentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlockCommentInitialStarAlignmentRule.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -30,14 +32,14 @@ public class BlockCommentInitialStarAlignmentRule : ) { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == BLOCK_COMMENT) { val expectedIndentForLineWithInitialStar = node.indent(false) + " *" val lines = node.text.split("\n") var offset = node.startOffset val modifiedLines = mutableListOf() + var autocorrect = false lines.forEach { line -> val modifiedLine = CONTINUATION_COMMENT_REGEX @@ -46,6 +48,7 @@ public class BlockCommentInitialStarAlignmentRule : val (prefix, content) = matchResult.destructured if (prefix != expectedIndentForLineWithInitialStar) { emit(offset + prefix.length, "Initial star should align with start of block comment", true) + .ifAutocorrectAllowed { autocorrect = true } expectedIndentForLineWithInitialStar + content } else { line @@ -55,7 +58,7 @@ public class BlockCommentInitialStarAlignmentRule : modifiedLines.add(modifiedLine) offset += line.length + 1 } - if (autoCorrect) { + if (autocorrect) { val newText = modifiedLines.joinToString(separator = "\n") if (node.text != newText) { (node as LeafElement).rawReplaceWithText(newText) 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 5fc7413b3d..4cfa164f6d 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 @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARRAY_ACCESS_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION @@ -38,6 +39,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -104,8 +106,7 @@ public class ChainMethodContinuationRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType in chainOperatorTokenSet } @@ -124,17 +125,16 @@ public class ChainMethodContinuationRule : ?.takeUnless { it.rootASTNode.treeParent.elementType == PACKAGE_DIRECTIVE } ?.takeUnless { it.rootASTNode.treeParent.elementType == LONG_STRING_TEMPLATE_ENTRY } ?.let { chainedExpression -> - fixWhitespaceBeforeChainOperators(chainedExpression, emit, autoCorrect) + fixWhitespaceBeforeChainOperators(chainedExpression, emit) disallowCommentBetweenDotAndCallExpression(chainedExpression, emit) - fixWhiteSpaceAfterChainOperators(chainedExpression, emit, autoCorrect) + fixWhiteSpaceAfterChainOperators(chainedExpression, emit) } } } private fun fixWhitespaceBeforeChainOperators( chainedExpression: ChainedExpression, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val wrapBeforeEachChainOperator = chainedExpression.wrapBeforeChainOperator() val exceedsMaxLineLength = chainedExpression.exceedsMaxLineLength() @@ -145,11 +145,11 @@ public class ChainMethodContinuationRule : .forEach { chainOperator -> when { chainOperator.shouldBeOnSameLineAsClosingElementOfPreviousExpressionInMethodChain() -> { - removeWhiteSpaceBeforeChainOperator(chainOperator, emit, autoCorrect) + removeWhiteSpaceBeforeChainOperator(chainOperator, emit) } wrapBeforeEachChainOperator || exceedsMaxLineLength || chainOperator.isPrecededByComment() -> { - insertWhiteSpaceBeforeChainOperator(chainOperator, emit, autoCorrect) + insertWhiteSpaceBeforeChainOperator(chainOperator, emit) } } } @@ -261,8 +261,7 @@ public class ChainMethodContinuationRule : private fun insertWhiteSpaceBeforeChainOperator( chainOperator: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { chainOperator .prevLeaf() @@ -275,9 +274,10 @@ public class ChainMethodContinuationRule : // fooBar // .bar { ... }.foo() emit(chainOperator.startOffset, "Expected newline before '${chainOperator.text}'", true) - if (autoCorrect) { - chainOperator.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(chainOperator.treeParent)) - } + .ifAutocorrectAllowed { + chainOperator.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(chainOperator.treeParent)) + } + Unit } whiteSpaceOrComment == null || whiteSpaceOrComment.isWhiteSpaceWithoutNewline() -> { @@ -286,9 +286,10 @@ public class ChainMethodContinuationRule : // fooBar // .bar { ... }.foo() emit(chainOperator.startOffset, "Expected newline before '${chainOperator.text}'", true) - if (autoCorrect) { - chainOperator.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(chainOperator.treeParent)) - } + .ifAutocorrectAllowed { + chainOperator.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(chainOperator.treeParent)) + } + Unit } } } @@ -317,8 +318,7 @@ public class ChainMethodContinuationRule : private fun removeWhiteSpaceBeforeChainOperator( chainOperator: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { chainOperator .prevLeaf() @@ -336,16 +336,16 @@ public class ChainMethodContinuationRule : // .trimIndent() if (whiteSpaceOrComment.isWhiteSpaceWithNewline()) { emit(chainOperator.startOffset, "Unexpected newline before '${chainOperator.text}'", true) - if (autoCorrect) { - whiteSpaceOrComment?.treeParent?.removeChild(whiteSpaceOrComment) - } + .ifAutocorrectAllowed { + whiteSpaceOrComment?.treeParent?.removeChild(whiteSpaceOrComment) + } } } } private fun disallowCommentBetweenDotAndCallExpression( chainedExpression: ChainedExpression, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { chainedExpression .chainOperators @@ -359,8 +359,7 @@ public class ChainMethodContinuationRule : private fun fixWhiteSpaceAfterChainOperators( chainedExpression: ChainedExpression, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { chainedExpression .chainOperators @@ -370,9 +369,9 @@ public class ChainMethodContinuationRule : .takeIf { it.isWhiteSpaceWithNewline() } ?.let { whiteSpace -> emit(whiteSpace.startOffset - 1, "Unexpected newline after '${chainOperator.text}'", true) - if (autoCorrect) { - whiteSpace.treeParent.removeChild(whiteSpace) - } + .ifAutocorrectAllowed { + whiteSpace.treeParent.removeChild(whiteSpace) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt index 3183473bad..9b7555c2b3 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANDAND import com.pinterest.ktlint.rule.engine.core.api.ElementType.COMMA import com.pinterest.ktlint.rule.engine.core.api.ElementType.DIV @@ -24,6 +25,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 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.ifAutocorrectAllowed 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.isWhiteSpaceWithoutNewline @@ -56,7 +58,6 @@ public class ChainWrappingRule : private val sameLineTokens = TokenSet.create(MUL, DIV, PERC, ANDAND, OROR) private val prefixTokens = TokenSet.create(PLUS, MINUS) private val nextLineTokens = TokenSet.create(DOT, SAFE_ACCESS, ELVIS) - private val noSpaceAroundTokens = TokenSet.create(DOT, SAFE_ACCESS) private var indentConfig = DEFAULT_INDENT_CONFIG override fun beforeFirstNode(editorConfig: EditorConfig) { @@ -69,8 +70,7 @@ public class ChainWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { /* org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement (DOT) | "." @@ -85,21 +85,21 @@ public class ChainWrappingRule : val nextLeaf = node.nextCodeLeaf()?.prevLeaf() if (nextLeaf.isWhiteSpaceWithNewline() && !node.isElvisOperatorAndComment()) { emit(node.startOffset, "Line must not end with \"${node.text}\"", true) - if (autoCorrect) { - // rewriting - // to - // - // (or) - // to - // - if (node.elementType == ELVIS) { - node.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) - node.upsertWhitespaceAfterMe(" ") - } else { - node.treeParent.removeChild(node) - (nextLeaf as LeafElement).rawInsertAfterMe(node as LeafElement) + .ifAutocorrectAllowed { + // rewriting + // to + // + // (or) + // to + // + if (node.elementType == ELVIS) { + node.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) + node.upsertWhitespaceAfterMe(" ") + } else { + node.treeParent.removeChild(node) + (nextLeaf as LeafElement).rawInsertAfterMe(node as LeafElement) + } } - } } } else if (sameLineTokens.contains(elementType) || prefixTokens.contains(elementType)) { if (node.isPartOfComment()) { @@ -123,46 +123,46 @@ public class ChainWrappingRule : if (prevLeaf != null && prevLeaf.isWhiteSpaceWithNewline()) { emit(node.startOffset, "Line must not begin with \"${node.text}\"", true) - if (autoCorrect) { - // rewriting - // to - // - // (or) - // to - // - val nextLeaf = node.nextLeaf() - val whiteSpaceToBeDeleted = - when { - nextLeaf.isWhiteSpaceWithNewline() -> { - // Node is preceded and followed by whitespace. Prefer to remove the whitespace before the node as this will - // change the indent of the next line - prevLeaf - } + .ifAutocorrectAllowed { + // rewriting + // to + // + // (or) + // to + // + val nextLeaf = node.nextLeaf() + val whiteSpaceToBeDeleted = + when { + nextLeaf.isWhiteSpaceWithNewline() -> { + // Node is preceded and followed by whitespace. Prefer to remove the whitespace before the node as this will + // change the indent of the next line + prevLeaf + } - nextLeaf.isWhiteSpaceWithoutNewline() -> nextLeaf + nextLeaf.isWhiteSpaceWithoutNewline() -> nextLeaf - else -> null - } + else -> null + } - if (node.treeParent.elementType == OPERATION_REFERENCE) { - val operationReference = node.treeParent - val insertBeforeSibling = - operationReference - .prevCodeSibling() - ?.nextSibling() - operationReference.treeParent.removeChild(operationReference) - insertBeforeSibling?.treeParent?.addChild(operationReference, insertBeforeSibling) - node.treeParent.upsertWhitespaceBeforeMe(" ") - } else { - val insertionPoint = prevLeaf.prevCodeLeaf() as LeafPsiElement - (node as LeafPsiElement).treeParent.removeChild(node) - insertionPoint.rawInsertAfterMe(node) - (insertionPoint as ASTNode).upsertWhitespaceAfterMe(" ") + if (node.treeParent.elementType == OPERATION_REFERENCE) { + val operationReference = node.treeParent + val insertBeforeSibling = + operationReference + .prevCodeSibling() + ?.nextSibling() + operationReference.treeParent.removeChild(operationReference) + insertBeforeSibling?.treeParent?.addChild(operationReference, insertBeforeSibling) + node.treeParent.upsertWhitespaceBeforeMe(" ") + } else { + val insertionPoint = prevLeaf.prevCodeLeaf() as LeafPsiElement + (node as LeafPsiElement).treeParent.removeChild(node) + insertionPoint.rawInsertAfterMe(node) + (insertionPoint as ASTNode).upsertWhitespaceAfterMe(" ") + } + whiteSpaceToBeDeleted + ?.treeParent + ?.removeChild(whiteSpaceToBeDeleted) } - whiteSpaceToBeDeleted - ?.treeParent - ?.removeChild(whiteSpaceToBeDeleted) - } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassNamingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassNamingRule.kt index 78f4b9a518..80329711ee 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassNamingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassNamingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER @@ -26,8 +27,7 @@ public class ClassNamingRule : StandardRule("class-naming") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (!allowBacktickedClassName && node.elementType == IMPORT_DIRECTIVE) { node 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 9dc041f4b8..fbef559758 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 @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS @@ -36,6 +37,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPE import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY_OFF import com.pinterest.ktlint.rule.engine.core.api.hasModifier +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -103,11 +105,10 @@ public class ClassSignatureRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == CLASS) { - visitClass(node, emit, autoCorrect) + visitClass(node, emit) } } @@ -160,8 +161,7 @@ public class ClassSignatureRule : private fun visitClass( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == CLASS) @@ -169,17 +169,16 @@ public class ClassSignatureRule : node.hasTooManyParameters() || node.containsMultilineParameter() || (codeStyle == ktlint_official && node.containsAnnotatedParameter()) || - (isMaxLineLengthSet() && classSignatureExcludingSuperTypesExceedsMaxLineLength(node, emit, autoCorrect)) || + (isMaxLineLengthSet() && classSignatureExcludingSuperTypesExceedsMaxLineLength(node, emit)) || (!isMaxLineLengthSet() && node.classSignatureExcludingSuperTypesIsMultiline()) - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = wrapPrimaryConstructorParameters, dryRun = false) - fixWhitespacesInSuperTypeList(node, emit, autoCorrect, wrappedPrimaryConstructor = wrapPrimaryConstructorParameters) - fixClassBody(node, emit, autoCorrect) + fixWhiteSpacesInValueParameterList(node, emit, multiline = wrapPrimaryConstructorParameters, dryRun = false) + fixWhitespacesInSuperTypeList(node, emit, wrappedPrimaryConstructor = wrapPrimaryConstructorParameters) + fixClassBody(node, emit) } private fun classSignatureExcludingSuperTypesExceedsMaxLineLength( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Boolean { val actualClassSignatureLength = node.getClassSignatureLength(excludeSuperTypes = true) // Calculate the length of the class signature in case it, excluding the super types, would be rewritten as single @@ -188,7 +187,7 @@ public class ClassSignatureRule : val length = actualClassSignatureLength + // Calculate the white space correction in case the signature would be rewritten to a single line - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = false, dryRun = true) + fixWhiteSpacesInValueParameterList(node, emit, multiline = false, dryRun = true) return length > maxLineLength } @@ -226,8 +225,7 @@ public class ClassSignatureRule : private fun fixWhiteSpacesInValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -242,11 +240,11 @@ public class ClassSignatureRule : whiteSpaceCorrection += if (hasNoValueParameters) { - fixWhiteSpacesInEmptyValueParameterList(node, emit, autoCorrect, dryRun) + fixWhiteSpacesInEmptyValueParameterList(node, emit, dryRun) } else { - fixWhiteSpacesBeforeFirstParameterInValueParameterList(node, emit, autoCorrect, multiline, dryRun) + - fixWhiteSpacesBetweenParametersInValueParameterList(node, emit, autoCorrect, multiline, dryRun) + - fixWhiteSpaceBeforeClosingParenthesis(node, emit, autoCorrect, multiline, dryRun) + fixWhiteSpacesBeforeFirstParameterInValueParameterList(node, emit, multiline, dryRun) + + fixWhiteSpacesBetweenParametersInValueParameterList(node, emit, multiline, dryRun) + + fixWhiteSpaceBeforeClosingParenthesis(node, emit, multiline, dryRun) } return whiteSpaceCorrection @@ -254,8 +252,7 @@ public class ClassSignatureRule : private fun fixWhiteSpacesInEmptyValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, dryRun: Boolean, ): Int { var whiteSpaceCorrection = 0 @@ -273,9 +270,9 @@ public class ClassSignatureRule : }?.let { parameterList -> if (!dryRun) { emit(parameterList.startOffset, "No parenthesis expected", true) - } - if (autoCorrect && !dryRun) { - parameterList.treeParent.removeChild(parameterList) + .ifAutocorrectAllowed { + parameterList.treeParent.removeChild(parameterList) + } } else { whiteSpaceCorrection -= parameterList.textLength } @@ -288,8 +285,7 @@ public class ClassSignatureRule : private fun fixWhiteSpacesBeforeFirstParameterInValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -311,13 +307,13 @@ public class ClassSignatureRule : if (whiteSpaceBeforeIdentifier == null || !whiteSpaceBeforeIdentifier.textContains('\n') ) { - if (!dryRun) { - emit(firstParameterInList.startOffset, "Newline expected after opening parenthesis", true) - } // Let indent rule determine the exact indent val expectedParameterIndent = indentConfig.childIndentOf(node) - if (autoCorrect && !dryRun) { - valueParameterList.firstChildNode.upsertWhitespaceAfterMe(expectedParameterIndent) + if (!dryRun) { + emit(firstParameterInList.startOffset, "Newline expected after opening parenthesis", true) + .ifAutocorrectAllowed { + valueParameterList.firstChildNode.upsertWhitespaceAfterMe(expectedParameterIndent) + } } else { whiteSpaceCorrection += expectedParameterIndent.length - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -329,10 +325,9 @@ public class ClassSignatureRule : firstParameter!!.startOffset, "No whitespace expected between opening parenthesis and first parameter name", true, - ) - } - if (autoCorrect && !dryRun) { - whiteSpaceBeforeIdentifier.treeParent.removeChild(whiteSpaceBeforeIdentifier) + ).ifAutocorrectAllowed { + whiteSpaceBeforeIdentifier.treeParent.removeChild(whiteSpaceBeforeIdentifier) + } } else { whiteSpaceCorrection -= whiteSpaceBeforeIdentifier.textLength } @@ -345,8 +340,7 @@ public class ClassSignatureRule : private fun fixWhiteSpacesBetweenParametersInValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -373,13 +367,13 @@ public class ClassSignatureRule : if (whiteSpaceBeforeIdentifier == null || !whiteSpaceBeforeIdentifier.textContains('\n') ) { - if (!dryRun) { - emit(valueParameter.startOffset, "Parameter should start on a newline", true) - } // Let IndentationRule determine the exact indent val expectedParameterIndent = indentConfig.childIndentOf(node) - if (autoCorrect && !dryRun) { - firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(expectedParameterIndent) + if (!dryRun) { + emit(valueParameter.startOffset, "Parameter should start on a newline", true) + .ifAutocorrectAllowed { + firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(expectedParameterIndent) + } } else { whiteSpaceCorrection += expectedParameterIndent.length - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -388,9 +382,9 @@ public class ClassSignatureRule : if (whiteSpaceBeforeIdentifier == null || whiteSpaceBeforeIdentifier.text != " ") { if (!dryRun) { emit(firstChildNodeInValueParameter!!.startOffset, "Single whitespace expected before parameter", true) - } - if (autoCorrect && !dryRun) { - firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(" ") + .ifAutocorrectAllowed { + firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(" ") + } } else { whiteSpaceCorrection += 1 - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -404,8 +398,7 @@ public class ClassSignatureRule : private fun fixWhiteSpaceBeforeClosingParenthesis( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -423,13 +416,13 @@ public class ClassSignatureRule : if (whiteSpaceBeforeClosingParenthesis == null || !whiteSpaceBeforeClosingParenthesis.textContains('\n') ) { - if (!dryRun) { - emit(closingParenthesisPrimaryConstructor!!.startOffset, "Newline expected before closing parenthesis", true) - } // Let IndentationRule determine the exact indent val expectedParameterIndent = node.indent() - if (autoCorrect && !dryRun) { - closingParenthesisPrimaryConstructor!!.upsertWhitespaceBeforeMe(expectedParameterIndent) + if (!dryRun) { + emit(closingParenthesisPrimaryConstructor!!.startOffset, "Newline expected before closing parenthesis", true) + .ifAutocorrectAllowed { + closingParenthesisPrimaryConstructor.upsertWhitespaceBeforeMe(expectedParameterIndent) + } } else { whiteSpaceCorrection += expectedParameterIndent.length - (whiteSpaceBeforeClosingParenthesis?.textLength ?: 0) } @@ -443,10 +436,9 @@ public class ClassSignatureRule : whiteSpaceBeforeClosingParenthesis.startOffset, "No whitespace expected between last parameter and closing parenthesis", true, - ) - } - if (autoCorrect && !dryRun) { - whiteSpaceBeforeClosingParenthesis.treeParent.removeChild(whiteSpaceBeforeClosingParenthesis) + ).ifAutocorrectAllowed { + whiteSpaceBeforeClosingParenthesis.treeParent.removeChild(whiteSpaceBeforeClosingParenthesis) + } } else { whiteSpaceCorrection -= whiteSpaceBeforeClosingParenthesis.textLength } @@ -458,8 +450,7 @@ public class ClassSignatureRule : private fun fixWhitespacesInSuperTypeList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, wrappedPrimaryConstructor: Boolean, ): Int { var whiteSpaceCorrection = 0 @@ -471,22 +462,22 @@ public class ClassSignatureRule : .firstOrNull { it.elementType == SUPER_TYPE_CALL_ENTRY } ?.let { superTypeCallEntry -> emit(superTypeCallEntry.startOffset, "Super type call must be first super type", true) - if (autoCorrect) { - val superTypeList = node.findChildByType(SUPER_TYPE_LIST) ?: return 0 - val originalFirstSuperType = superTypes.first() - val commaBeforeSuperTypeCall = requireNotNull(superTypeCallEntry.prevSibling { it.elementType == COMMA }) - - // Remove the whitespace before the super type call and do not insert a new whitespace as it will be fixed later - superTypeCallEntry - .prevSibling() - ?.takeIf { it.elementType == WHITE_SPACE } - ?.let { whitespaceBeforeSuperTypeCallEntry -> - superTypeList.removeChild(whitespaceBeforeSuperTypeCallEntry) - } + .ifAutocorrectAllowed { + val superTypeList = node.findChildByType(SUPER_TYPE_LIST) ?: return 0 + val originalFirstSuperType = superTypes.first() + val commaBeforeSuperTypeCall = requireNotNull(superTypeCallEntry.prevSibling { it.elementType == COMMA }) + + // Remove the whitespace before the super type call and do not insert a new whitespace as it will be fixed later + superTypeCallEntry + .prevSibling() + ?.takeIf { it.elementType == WHITE_SPACE } + ?.let { whitespaceBeforeSuperTypeCallEntry -> + superTypeList.removeChild(whitespaceBeforeSuperTypeCallEntry) + } - superTypeList.addChild(superTypeCallEntry, superTypes.first()) - superTypeList.addChild(commaBeforeSuperTypeCall, originalFirstSuperType) - } + superTypeList.addChild(superTypeCallEntry, superTypes.first()) + superTypeList.addChild(commaBeforeSuperTypeCall, originalFirstSuperType) + } } } @@ -510,9 +501,9 @@ public class ClassSignatureRule : val expectedWhitespace = " " if (whiteSpaceBeforeSuperType.text != expectedWhitespace) { emit(firstSuperType.startOffset, "Expected single space before the super type", true) - if (autoCorrect) { - firstSuperType.upsertWhitespaceBeforeMe(expectedWhitespace) - } + .ifAutocorrectAllowed { + firstSuperType.upsertWhitespaceBeforeMe(expectedWhitespace) + } } } } @@ -526,16 +517,16 @@ public class ClassSignatureRule : ?.takeIf { it.elementType == WHITE_SPACE } .let { whiteSpaceBeforeIdentifier -> if (node.hasMultilineSuperTypeList() || - classSignaturesIncludingFirstSuperTypeExceedsMaxLineLength(node, emit, autoCorrect) + classSignaturesIncludingFirstSuperTypeExceedsMaxLineLength(node, emit) ) { if (whiteSpaceBeforeIdentifier == null || !whiteSpaceBeforeIdentifier.textContains('\n') ) { emit(superTypeFirstChildNode.startOffset, "Super type should start on a newline", true) - if (autoCorrect) { - // Let IndentationRule determine the exact indent - superTypeFirstChildNode.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) - } + .ifAutocorrectAllowed { + // Let IndentationRule determine the exact indent + superTypeFirstChildNode.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) + } } } else { val expectedWhitespace = " " @@ -543,9 +534,9 @@ public class ClassSignatureRule : whiteSpaceBeforeIdentifier.text != expectedWhitespace ) { emit(superTypeFirstChildNode.startOffset, "Expected single space before the super type", true) - if (autoCorrect) { - superTypeFirstChildNode.upsertWhitespaceBeforeMe(expectedWhitespace) - } + .ifAutocorrectAllowed { + superTypeFirstChildNode.upsertWhitespaceBeforeMe(expectedWhitespace) + } } } } @@ -565,19 +556,19 @@ public class ClassSignatureRule : (whiteSpaceBeforeIdentifier == null || whiteSpaceBeforeIdentifier.text != expectedWhitespace) ) { emit(firstChildNodeInSuperType.startOffset, "Expected single space before the first super type", true) - if (autoCorrect) { - firstChildNodeInSuperType.upsertWhitespaceBeforeMe(expectedWhitespace) - } + .ifAutocorrectAllowed { + firstChildNodeInSuperType.upsertWhitespaceBeforeMe(expectedWhitespace) + } } } else { if (whiteSpaceBeforeIdentifier == null || !whiteSpaceBeforeIdentifier.textContains('\n') ) { emit(firstChildNodeInSuperType.startOffset, "Super type should start on a newline", true) - if (autoCorrect) { - // Let IndentationRule determine the exact indent - firstChildNodeInSuperType.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) - } + .ifAutocorrectAllowed { + // Let IndentationRule determine the exact indent + firstChildNodeInSuperType.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) + } } } } @@ -593,9 +584,9 @@ public class ClassSignatureRule : .findChildByType(WHITE_SPACE) ?.let { whitespace -> emit(whitespace.startOffset, "No whitespace expected", true) - if (autoCorrect) { - whitespace.treeParent.removeChild(whitespace) - } + .ifAutocorrectAllowed { + whitespace.treeParent.removeChild(whitespace) + } } } @@ -604,8 +595,7 @@ public class ClassSignatureRule : private fun classSignaturesIncludingFirstSuperTypeExceedsMaxLineLength( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Boolean { val actualClassSignatureLength = node.getClassSignatureLength(excludeSuperTypes = false) // Calculate the length of the class signature in case it, including the super types, would be rewritten as single @@ -614,7 +604,7 @@ public class ClassSignatureRule : val length = actualClassSignatureLength + // Calculate the white space correction in case the signature would be rewritten to a single line - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = false, dryRun = true) + fixWhiteSpacesInValueParameterList(node, emit, multiline = false, dryRun = true) return length > maxLineLength } @@ -627,19 +617,18 @@ public class ClassSignatureRule : private fun fixClassBody( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(CLASS_BODY) ?.let { classBody -> if (classBody.prevLeaf()?.text != " ") { emit(classBody.startOffset, "Expected a single space before class body", true) - if (autoCorrect) { - classBody - .prevLeaf(true) - ?.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + classBody + .prevLeaf(true) + ?.upsertWhitespaceAfterMe(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentSpacingRule.kt index 04368b85d1..d0aa25c9ef 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentSpacingRule.kt @@ -1,8 +1,10 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -14,16 +16,15 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class CommentSpacingRule : StandardRule("comment-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == EOL_COMMENT) { val prevLeaf = node.prevLeaf() if (prevLeaf !is PsiWhiteSpace && prevLeaf is LeafPsiElement) { emit(node.startOffset, "Missing space before //", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + } } val text = node.text if (text.length != 2 && @@ -34,9 +35,9 @@ public class CommentSpacingRule : StandardRule("comment-spacing") { !text.startsWith("//language=") ) { emit(node.startOffset, "Missing space after //", true) - if (autoCorrect) { - (node as LeafPsiElement).rawReplaceWithText("// " + text.removePrefix("//")) - } + .ifAutocorrectAllowed { + (node as LeafPsiElement).rawReplaceWithText("// " + text.removePrefix("//")) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentWrappingRule.kt index 8f56f111ea..59e1763e77 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/CommentWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE @@ -11,6 +12,7 @@ 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.firstChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.hasNewLineInClosedRange +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -38,8 +40,7 @@ public class CommentWrappingRule : ) { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == BLOCK_COMMENT) { val beforeBlockComment = @@ -99,8 +100,7 @@ public class CommentWrappingRule : node.startOffset, "A single line block comment after a code element on the same line must be replaced with an EOL comment", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { node.upsertWhitespaceBeforeMe(" ") } } @@ -114,8 +114,7 @@ public class CommentWrappingRule : nextLeaf.startOffset, "A block comment may not be followed by any other element on that same line", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { nextLeaf.upsertWhitespaceBeforeMe(node.indent()) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ConditionWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ConditionWrappingRule.kt index f0db48210f..8a68041cdc 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ConditionWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ConditionWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANDAND import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPERATION_REFERENCE @@ -14,13 +15,13 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.leavesInClosedRange import com.pinterest.ktlint.rule.engine.core.api.nextSibling -import com.pinterest.ktlint.rule.engine.core.api.parent import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -53,14 +54,13 @@ public class ConditionWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.isLogicalBinaryExpression() } ?.takeIf { it.isMultiline() } ?.let { - visitLogicalExpression(it, emit, autoCorrect) + visitLogicalExpression(it, emit) } } @@ -72,8 +72,7 @@ public class ConditionWrappingRule : private fun visitLogicalExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(OPERATION_REFERENCE) @@ -89,9 +88,9 @@ public class ConditionWrappingRule : ?: 0, ) emit(startOffset, "Newline expected before operand in multiline condition", true) - if (autoCorrect) { - operationReference.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(node)) - } + .ifAutocorrectAllowed { + operationReference.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(node)) + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt index b08338807f..75d81c1425 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONTEXT_RECEIVER import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONTEXT_RECEIVER_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.GT @@ -18,6 +19,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -61,22 +63,20 @@ public class ContextReceiverWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when { node.elementType == CONTEXT_RECEIVER_LIST -> - visitContextReceiverList(node, autoCorrect, emit) + visitContextReceiverList(node, emit) node.elementType == TYPE_ARGUMENT_LIST && node.isPartOf(CONTEXT_RECEIVER) -> - visitContextReceiverTypeArgumentList(node, autoCorrect, emit) + visitContextReceiverTypeArgumentList(node, emit) } } private fun visitContextReceiverList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // Context receiver must be followed by new line or comment node @@ -85,11 +85,11 @@ public class ContextReceiverWrappingRule : ?.takeIf { !it.isWhiteSpaceWithNewline() } ?.let { nodeAfterContextReceiver -> emit(nodeAfterContextReceiver.startOffset, "Expected a newline after the context receiver", true) - if (autoCorrect) { - nodeAfterContextReceiver - .firstChildLeafOrSelf() - .upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(node)) - } + .ifAutocorrectAllowed { + nodeAfterContextReceiver + .firstChildLeafOrSelf() + .upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(node)) + } } // Check line length assuming that the context receiver is indented correctly. Wrapping rule must however run before indenting. @@ -104,8 +104,7 @@ public class ContextReceiverWrappingRule : it.startOffset, "Newline expected before context receiver as max line length is violated", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { it .prevLeaf(includeEmpty = true) ?.upsertWhitespaceAfterMe(indentConfig.childIndentOf(node)) @@ -118,8 +117,7 @@ public class ContextReceiverWrappingRule : rpar.startOffset, "Newline expected before closing parenthesis as max line length is violated", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { rpar.upsertWhitespaceBeforeMe(node.indent()) } } @@ -128,8 +126,7 @@ public class ContextReceiverWrappingRule : private fun visitContextReceiverTypeArgumentList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val contextReceiver = node.treeParent.text // Check line length assuming that the context receiver is indented correctly. Wrapping rule must however run @@ -145,8 +142,7 @@ public class ContextReceiverWrappingRule : it.startOffset, "Newline expected before context receiver type projection as max line length is violated", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { it.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) } } @@ -157,10 +153,9 @@ public class ContextReceiverWrappingRule : gt.startOffset, "Newline expected before closing angle bracket as max line length is violated", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { // Ideally, the closing angle bracket should be de-indented to make it consistent with - // de-intentation of closing ")", "}" and "]". This however would be inconsistent with how the + // de-indentation of closing ")", "}" and "]". This however would be inconsistent with how the // type argument lists are formatted by IntelliJ IDEA default formatter. gt.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/DiscouragedCommentLocationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/DiscouragedCommentLocationRule.kt index bd9b948976..c693da3324 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/DiscouragedCommentLocationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/DiscouragedCommentLocationRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 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 @@ -26,10 +27,9 @@ public class DiscouragedCommentLocationRule : StandardRule("discouraged-comment- @Suppress("RedundantOverride") override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - super.beforeVisitChildNodes(node, autoCorrect, emit) + super.beforeVisitChildNodes(node, emit) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt index bddce64322..7bc0012b9f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 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 @@ -22,8 +23,7 @@ public class EnumEntryNameCaseRule : StandardRule("enum-entry-name-case") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node !is CompositeElement) { return diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt index 8b197d48ec..63d9ba4080 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY @@ -16,6 +17,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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.isWhiteSpaceWithoutNewline @@ -55,37 +57,34 @@ public class EnumWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { node.elementType == CLASS } ?.takeIf { (node.psi as KtClass).isEnum() } ?.findChildByType(CLASS_BODY) ?.let { classBody -> - visitEnumClass(classBody, autoCorrect, emit) + visitEnumClass(classBody, emit) } } private fun visitEnumClass( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == CLASS_BODY) - val commentBeforeFirstEnumEntry = wrapCommentBeforeFirstEnumEntry(node, emit, autoCorrect) + val commentBeforeFirstEnumEntry = wrapCommentBeforeFirstEnumEntry(node, emit) if (commentBeforeFirstEnumEntry || node.isMultiline() || node.hasAnnotatedEnumEntry() || node.hasCommentedEnumEntry()) { - wrapEnumEntries(node, autoCorrect, emit) - wrapClosingBrace(node, emit, autoCorrect) + wrapEnumEntries(node, emit) + wrapClosingBrace(node, emit) } - addBlankLineBetweenEnumEntriesAndOtherDeclarations(node, emit, autoCorrect) + addBlankLineBetweenEnumEntriesAndOtherDeclarations(node, emit) } private fun wrapCommentBeforeFirstEnumEntry( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Boolean { val firstEnumEntry = node.findChildByType(ENUM_ENTRY)?.firstChildLeafOrSelf() if (firstEnumEntry != null) { @@ -98,9 +97,9 @@ public class EnumWrappingRule : val expectedIndent = indentConfig.childIndentOf(node) if (commentBeforeFirstEnumEntry.prevLeaf()?.text != expectedIndent) { emit(node.startOffset, "Expected a (single) newline before comment", true) - if (autoCorrect) { - commentBeforeFirstEnumEntry.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) - } + .ifAutocorrectAllowed { + commentBeforeFirstEnumEntry.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) + } return true } } @@ -127,37 +126,34 @@ public class EnumWrappingRule : private fun wrapEnumEntries( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .children() .filter { it.elementType == ENUM_ENTRY } .forEach { enumEntry -> - wrapEnumEntry(enumEntry, autoCorrect, emit) + wrapEnumEntry(enumEntry, emit) } } private fun wrapEnumEntry( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .prevLeaf { !it.isPartOfComment() && !it.isWhiteSpaceWithoutNewline() } ?.takeUnless { it.isWhiteSpaceWithNewline() } ?.let { prevLeaf -> emit(node.startOffset, "Enum entry should start on a separate line", true) - if (autoCorrect) { - prevLeaf.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(node)) - } + .ifAutocorrectAllowed { + prevLeaf.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(node)) + } } } private fun wrapClosingBrace( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(RBRACE) @@ -166,17 +162,16 @@ public class EnumWrappingRule : val expectedIndent = indentConfig.parentIndentOf(node) if (prevLeaf?.text != expectedIndent) { emit(rbrace.startOffset, "Expected newline before '}'", true) - if (autoCorrect) { - rbrace.upsertWhitespaceBeforeMe(expectedIndent) - } + .ifAutocorrectAllowed { + rbrace.upsertWhitespaceBeforeMe(expectedIndent) + } } } } private fun addBlankLineBetweenEnumEntriesAndOtherDeclarations( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .children() @@ -187,9 +182,9 @@ public class EnumWrappingRule : val expectedIndent = "\n".plus(indentConfig.siblingIndentOf(node)) if (nextSibling.text != expectedIndent) { emit(nextSibling.startOffset + 1, "Expected blank line between enum entries and other declaration(s)", true) - if (autoCorrect) { - nextSibling.upsertWhitespaceBeforeMe(expectedIndent) - } + .ifAutocorrectAllowed { + nextSibling.upsertWhitespaceBeforeMe(expectedIndent) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FilenameRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FilenameRule.kt index c92b0f103d..bc17e8886b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FilenameRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FilenameRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER @@ -46,8 +47,7 @@ import org.jetbrains.kotlin.psi.KtFile public class FilenameRule : StandardRule("filename") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isRoot()) { node as FileASTNode? ?: error("node is not ${FileASTNode::class} but ${node::class}") @@ -125,7 +125,7 @@ public class FilenameRule : StandardRule("filename") { private fun String.shouldMatchClassName( className: String, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (this != className) { emit( @@ -141,7 +141,7 @@ public class FilenameRule : StandardRule("filename") { private fun String.shouldMatchFileName( filename: String, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (this != filename) { emit( @@ -152,7 +152,9 @@ public class FilenameRule : StandardRule("filename") { } } - private fun String.shouldMatchPascalCase(emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) { + private fun String.shouldMatchPascalCase( + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { if (!this.matches(PASCAL_CASE_REGEX)) { emit(0, "File name '$this.kt' should conform PascalCase", false) } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FinalNewlineRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FinalNewlineRule.kt index f68717d1c2..f0881263da 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FinalNewlineRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FinalNewlineRule.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 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.STABLE import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INSERT_FINAL_NEWLINE_PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isRoot import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -25,8 +27,7 @@ public class FinalNewlineRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isRoot()) { if (node.textLength == 0) { @@ -37,16 +38,16 @@ public class FinalNewlineRule : if (insertFinalNewline) { if (lastNode !is PsiWhiteSpace || !lastNode.textContains('\n')) { emit(node.textLength - 1, "File must end with a newline (\\n)", true) - if (autoCorrect) { - node.addChild(PsiWhiteSpaceImpl("\n"), null) - } + .ifAutocorrectAllowed { + node.addChild(PsiWhiteSpaceImpl("\n"), null) + } } } else { if (lastNode is PsiWhiteSpace && lastNode.textContains('\n')) { emit(lastNode.startOffset, "Redundant newline (\\n) at the end of file", true) - if (autoCorrect) { - lastNode.node.treeParent.removeChild(lastNode.node) - } + .ifAutocorrectAllowed { + lastNode.node.treeParent.removeChild(lastNode.node) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt index 57b280d40b..b37377246b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN_KEYWORD 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -19,8 +21,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class FunKeywordSpacingRule : StandardRule("fun-keyword-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == FUN_KEYWORD } @@ -31,8 +32,7 @@ public class FunKeywordSpacingRule : StandardRule("fun-keyword-spacing") { whiteSpaceAfterFunKeyword.startOffset, "Single space expected after the fun keyword", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (whiteSpaceAfterFunKeyword as LeafPsiElement).rawReplaceWithText(" ") } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionExpressionBodyRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionExpressionBodyRule.kt index 4397149dc9..176ba5f1bb 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionExpressionBodyRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionExpressionBodyRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ @@ -22,6 +23,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.leavesInClosedRange @@ -94,18 +96,16 @@ public class FunctionExpressionBodyRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == BLOCK && it.treeParent.elementType == FUN } - ?.let { visitFunctionBody(node, autoCorrect, emit) } + ?.let { visitFunctionBody(node, emit) } } private fun visitFunctionBody( block: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(block.elementType == BLOCK) block @@ -116,43 +116,43 @@ public class FunctionExpressionBodyRule : ?.nextSibling { !it.isWhiteSpace() } ?.let { codeSibling -> emit(block.startOffset, "Function body should be replaced with body expression", true) - if (autoCorrect) { - with(block.treeParent) { - // Insert the code sibling before the block - addChild(LeafPsiElement(EQ, "="), block) - addChild(PsiWhiteSpaceImpl(" "), block) - addChild(codeSibling, block) - // Remove old (and now empty block) - removeChild(block) + .ifAutocorrectAllowed { + with(block.treeParent) { + // Insert the code sibling before the block + addChild(LeafPsiElement(EQ, "="), block) + addChild(PsiWhiteSpaceImpl(" "), block) + addChild(codeSibling, block) + // Remove old (and now empty block) + removeChild(block) + } } - } } block .takeIf { it.containingOnly(THROW) } ?.findChildByType(THROW) ?.let { throwNode -> emit(block.startOffset, "Function body should be replaced with body expression", true) - if (autoCorrect) { - with(block.treeParent) { - // Remove whitespace before block - block - .prevSibling() - .takeIf { it.isWhiteSpace() } - ?.let { removeChild(it) } - if (findChildByType(TYPE_REFERENCE) == null) { - // Insert Unit as return type as otherwise a compilation error results - addChild(LeafPsiElement(COLON, ":"), block) + .ifAutocorrectAllowed { + with(block.treeParent) { + // Remove whitespace before block + block + .prevSibling() + .takeIf { it.isWhiteSpace() } + ?.let { removeChild(it) } + if (findChildByType(TYPE_REFERENCE) == null) { + // Insert Unit as return type as otherwise a compilation error results + addChild(LeafPsiElement(COLON, ":"), block) + addChild(PsiWhiteSpaceImpl(" "), block) + addChild(createUnitTypeReference(), block) + } + addChild(PsiWhiteSpaceImpl(" "), block) + addChild(LeafPsiElement(EQ, "="), block) addChild(PsiWhiteSpaceImpl(" "), block) - addChild(createUnitTypeReference(), block) + addChild(throwNode, block) + // Remove old (and now empty block) + removeChild(block) } - addChild(PsiWhiteSpaceImpl(" "), block) - addChild(LeafPsiElement(EQ, "="), block) - addChild(PsiWhiteSpaceImpl(" "), block) - addChild(throwNode, block) - // Remove old (and now empty block) - removeChild(block) } - } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionLiteralRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionLiteralRule.kt index d66b4b3e02..97d8f08a24 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionLiteralRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionLiteralRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_LITERAL @@ -23,6 +24,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -105,26 +107,24 @@ public class FunctionLiteralRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == FUNCTION_LITERAL) { node .findChildByType(VALUE_PARAMETER_LIST) - ?.let { visitValueParameterList(it, autoCorrect, emit) } + ?.let { visitValueParameterList(it, emit) } node .findChildByType(ARROW) - ?.let { visitArrow(it, autoCorrect, emit) } + ?.let { visitArrow(it, emit) } node .findChildByType(BLOCK) - ?.let { visitBlock(it, autoCorrect, emit) } + ?.let { visitBlock(it, emit) } } } private fun visitValueParameterList( parameterList: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val valueParameters = parameterList @@ -132,9 +132,9 @@ public class FunctionLiteralRule : .filter { it.elementType == VALUE_PARAMETER } if (valueParameters.count() > 1 || parameterList.wrapFirstParameterToNewline()) { if (parameterList.textContains('\n') || parameterList.doesNotFitOnSameLineAsStartOfFunctionLiteral()) { - rewriteToMultilineParameterList(parameterList, autoCorrect, emit) + rewriteToMultilineParameterList(parameterList, emit) } else { - rewriteToSingleLineFunctionLiteral(parameterList, emit, autoCorrect) + rewriteToSingleLineFunctionLiteral(parameterList, emit) } } else { if (parameterList.textContains('\n')) { @@ -157,7 +157,7 @@ public class FunctionLiteralRule : // -> // bar() // } - rewriteToSingleLineFunctionLiteral(parameterList, emit, autoCorrect) + rewriteToSingleLineFunctionLiteral(parameterList, emit) } } } @@ -230,28 +230,26 @@ public class FunctionLiteralRule : private fun rewriteToMultilineParameterList( parameterList: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(parameterList.elementType == VALUE_PARAMETER_LIST) parameterList .children() .filter { it.elementType == VALUE_PARAMETER } - .forEach { wrapValueParameter(it, autoCorrect, emit) } + .forEach { wrapValueParameter(it, emit) } parameterList .treeParent .findChildByType(ARROW) - ?.let { arrow -> wrapArrow(arrow, autoCorrect, emit) } + ?.let { arrow -> wrapArrow(arrow, emit) } parameterList .treeParent .findChildByType(RBRACE) - ?.let { rbrace -> wrapBeforeRbrace(rbrace, autoCorrect, emit) } + ?.let { rbrace -> wrapBeforeRbrace(rbrace, emit) } } private fun wrapValueParameter( valueParameter: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(valueParameter.elementType == VALUE_PARAMETER) valueParameter @@ -262,26 +260,24 @@ public class FunctionLiteralRule : !whitespaceBeforeValueParameter.textContains('\n') ) { emit(valueParameter.startOffset, "Newline expected before parameter", true) - if (autoCorrect) { - valueParameter.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(valueParameter.parent(FUNCTION_LITERAL)!!)) - } + .ifAutocorrectAllowed { + valueParameter.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(valueParameter.parent(FUNCTION_LITERAL)!!)) + } } } } private fun wrapArrow( arrow: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - wrapBeforeArrow(arrow, emit, autoCorrect) - wrapAfterArrow(arrow, emit, autoCorrect) + wrapBeforeArrow(arrow, emit) + wrapAfterArrow(arrow, emit) } private fun wrapBeforeArrow( arrow: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(arrow.elementType == ARROW) arrow @@ -292,17 +288,16 @@ public class FunctionLiteralRule : !whitespaceBeforeArrow.textContains('\n') ) { emit(arrow.startOffset, "Newline expected before arrow", true) - if (autoCorrect) { - arrow.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(arrow.treeParent)) - } + .ifAutocorrectAllowed { + arrow.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(arrow.treeParent)) + } } } } private fun wrapAfterArrow( arrow: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(arrow.elementType == ARROW) arrow @@ -313,17 +308,16 @@ public class FunctionLiteralRule : !whitespaceAfterArrow.textContains('\n') ) { emit(arrow.startOffset + arrow.textLength - 1, "Newline expected after arrow", true) - if (autoCorrect) { - arrow.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(arrow)) - } + .ifAutocorrectAllowed { + arrow.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(arrow)) + } } } } private fun wrapBeforeRbrace( rbrace: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(rbrace.elementType == RBRACE) rbrace @@ -334,17 +328,16 @@ public class FunctionLiteralRule : !whitespaceBeforeRbrace.textContains('\n') ) { emit(rbrace.startOffset, "Newline expected before closing brace", true) - if (autoCorrect) { - rbrace.upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(rbrace)) - } + .ifAutocorrectAllowed { + rbrace.upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(rbrace)) + } } } } private fun rewriteToSingleLineFunctionLiteral( parameterList: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(parameterList.elementType == VALUE_PARAMETER_LIST) parameterList @@ -352,25 +345,24 @@ public class FunctionLiteralRule : ?.takeIf { it.isWhiteSpaceWithNewline() } ?.let { whitespaceBeforeParameterList -> emit(parameterList.startOffset, "No newline expected before parameter", true) - if (autoCorrect) { - whitespaceBeforeParameterList.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + whitespaceBeforeParameterList.upsertWhitespaceBeforeMe(" ") + } } parameterList .nextSibling { it.isWhiteSpace() } ?.takeIf { it.isWhiteSpaceWithNewline() } ?.let { whitespaceAfterParameterList -> emit(parameterList.startOffset + parameterList.textLength, "No newline expected after parameter", true) - if (autoCorrect) { - whitespaceAfterParameterList.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + whitespaceAfterParameterList.upsertWhitespaceAfterMe(" ") + } } } private fun visitArrow( arrow: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(arrow.elementType == ARROW) arrow @@ -378,13 +370,13 @@ public class FunctionLiteralRule : ?.takeIf { it.findChildByType(VALUE_PARAMETER) == null && arrow.isFollowedByNonEmptyBlock() } ?.let { emit(arrow.startOffset, "Arrow is redundant when parameter list is empty", true) - if (autoCorrect) { - arrow - .nextSibling() - .takeIf { it.isWhiteSpace() } - ?.let { it.treeParent.removeChild(it) } - arrow.treeParent.removeChild(arrow) - } + .ifAutocorrectAllowed { + arrow + .nextSibling() + .takeIf { it.isWhiteSpace() } + ?.let { it.treeParent.removeChild(it) } + arrow.treeParent.removeChild(arrow) + } } } @@ -395,8 +387,7 @@ public class FunctionLiteralRule : private fun visitBlock( block: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(block.elementType == BLOCK) if (block.textContains('\n') || block.exceedsMaxLineLength()) { @@ -404,8 +395,8 @@ public class FunctionLiteralRule : .prevCodeSibling() ?.let { prevCodeSibling -> when (prevCodeSibling.elementType) { - ARROW -> wrapAfterArrow(prevCodeSibling, emit, autoCorrect) - LBRACE -> wrapAfterLbrace(prevCodeSibling, emit, autoCorrect) + ARROW -> wrapAfterArrow(prevCodeSibling, emit) + LBRACE -> wrapAfterLbrace(prevCodeSibling, emit) else -> LOGGER.debug { "Unexpected type of element ${prevCodeSibling.elementType}" } } } @@ -413,14 +404,13 @@ public class FunctionLiteralRule : block .nextCodeSibling() ?.takeIf { it.elementType == RBRACE } - ?.let { rbrace -> wrapBeforeRbrace(rbrace, autoCorrect, emit) } + ?.let { rbrace -> wrapBeforeRbrace(rbrace, emit) } } } private fun wrapAfterLbrace( lbrace: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(lbrace.elementType == LBRACE) lbrace @@ -431,9 +421,9 @@ public class FunctionLiteralRule : !whitespaceAfterLbrace.textContains('\n') ) { emit(lbrace.startOffset, "Newline expected after opening brace", true) - if (autoCorrect) { - lbrace.upsertWhitespaceAfterMe(indentConfig.childIndentOf(lbrace)) - } + .ifAutocorrectAllowed { + lbrace.upsertWhitespaceAfterMe(indentConfig.childIndentOf(lbrace)) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt index 19dbf6fe87..ea15c9ce25 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -50,8 +51,7 @@ public class FunctionNamingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (!isTestClass && node.elementType == IMPORT_DIRECTIVE) { (node.psi as KtImportDirective) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionReturnTypeSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionReturnTypeSpacingRule.kt index ef91c6a5c0..9ce9a83313 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionReturnTypeSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionReturnTypeSpacingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE @@ -9,6 +10,7 @@ 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.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -32,23 +34,21 @@ public class FunctionReturnTypeSpacingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node.firstChildNode node .takeIf { node.elementType == FUN } ?.let { node.findChildByType(COLON) } ?.let { colonNode -> - removeWhiteSpaceBetweenClosingParenthesisAndColon(colonNode, emit, autoCorrect) - fixWhiteSpaceBetweenColonAndReturnType(colonNode, emit, autoCorrect) + removeWhiteSpaceBetweenClosingParenthesisAndColon(colonNode, emit) + fixWhiteSpaceBetweenColonAndReturnType(colonNode, emit) } } private fun removeWhiteSpaceBetweenClosingParenthesisAndColon( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == COLON) node @@ -56,16 +56,15 @@ public class FunctionReturnTypeSpacingRule : ?.takeIf { it.elementType == WHITE_SPACE } ?.let { whitespaceBeforeColonNode -> emit(whitespaceBeforeColonNode.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - whitespaceBeforeColonNode.treeParent?.removeChild(whitespaceBeforeColonNode) - } + .ifAutocorrectAllowed { + whitespaceBeforeColonNode.treeParent?.removeChild(whitespaceBeforeColonNode) + } } } private fun fixWhiteSpaceBetweenColonAndReturnType( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == COLON) node @@ -89,9 +88,9 @@ public class FunctionReturnTypeSpacingRule : whiteSpaceAfterColon.lengthUntilNewline(true) // Length of the line after but excluding the whitespace if (newLineLength <= maxLineLength) { emit(node.startOffset, "Single space expected between colon and return type", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt index b8f09641c1..79961fc8b8 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION @@ -33,6 +34,7 @@ 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.editorconfig.MAX_LINE_LENGTH_PROPERTY_OFF +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -106,8 +108,7 @@ public class FunctionSignatureRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == FUN) { node @@ -122,7 +123,7 @@ public class FunctionSignatureRule : return } - visitFunctionSignature(node, emit, autoCorrect) + visitFunctionSignature(node, emit) } } @@ -172,8 +173,7 @@ public class FunctionSignatureRule : private fun visitFunctionSignature( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == FUN) @@ -182,7 +182,7 @@ public class FunctionSignatureRule : node.containsMultilineParameter() || (codeStyle == ktlint_official && node.containsAnnotatedParameter()) if (isMaxLineLengthSet()) { - val singleLineFunctionSignatureLength = calculateFunctionSignatureLengthAsSingleLineSignature(node, emit, autoCorrect) + val singleLineFunctionSignatureLength = calculateFunctionSignatureLengthAsSingleLineSignature(node, emit) // Function signatures not having parameters, should not be reformatted automatically. It would result in function signatures // like below, which are not acceptable: // fun aVeryLongFunctionName( @@ -194,21 +194,21 @@ public class FunctionSignatureRule : // Leave it up to the max-line-length rule to detect those violations so that the developer can handle it manually. val rewriteFunctionSignatureWithParameters = node.countParameters() > 0 && singleLineFunctionSignatureLength > maxLineLength if (forceMultilineSignature || rewriteFunctionSignatureWithParameters) { - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = true, dryRun = false) + fixWhiteSpacesInValueParameterList(node, emit, multiline = true, dryRun = false) if (node.findChildByType(EQ) == null) { - fixWhitespaceBeforeFunctionBodyBlock(node, emit, autoCorrect, dryRun = false) + fixWhitespaceBeforeFunctionBodyBlock(node, emit, dryRun = false) } else { // Due to rewriting the function signature, the remaining length on the last line of the multiline signature needs to be // recalculated val lengthOfLastLine = recalculateRemainingLengthForFirstLineOfBodyExpression(node) - fixFunctionBodyExpression(node, emit, autoCorrect, maxLineLength - lengthOfLastLine) + fixFunctionBodyExpression(node, emit, maxLineLength - lengthOfLastLine) } } else { - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = false, dryRun = false) + fixWhiteSpacesInValueParameterList(node, emit, multiline = false, dryRun = false) if (node.findChildByType(EQ) == null) { - fixWhitespaceBeforeFunctionBodyBlock(node, emit, autoCorrect, dryRun = false) + fixWhitespaceBeforeFunctionBodyBlock(node, emit, dryRun = false) } else { - fixFunctionBodyExpression(node, emit, autoCorrect, maxLineLength - singleLineFunctionSignatureLength) + fixFunctionBodyExpression(node, emit, maxLineLength - singleLineFunctionSignatureLength) } } } else { @@ -220,9 +220,9 @@ public class FunctionSignatureRule : .functionSignatureNodes() .none { it.textContains('\n') } if (!forceMultilineSignature && rewriteToSingleLineFunctionSignature) { - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = false, dryRun = false) + fixWhiteSpacesInValueParameterList(node, emit, multiline = false, dryRun = false) } else { - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = true, dryRun = false) + fixWhiteSpacesInValueParameterList(node, emit, multiline = true, dryRun = false) } } } @@ -266,8 +266,7 @@ public class FunctionSignatureRule : private fun calculateFunctionSignatureLengthAsSingleLineSignature( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Int { val actualFunctionSignatureLength = node.getFunctionSignatureLength() @@ -275,9 +274,9 @@ public class FunctionSignatureRule : // maximum line length). The white space correction will be calculated via a dry run of the actual fix. return actualFunctionSignatureLength + // Calculate the white space correction in case the signature would be rewritten to a single line - fixWhiteSpacesInValueParameterList(node, emit, autoCorrect, multiline = false, dryRun = true) + + fixWhiteSpacesInValueParameterList(node, emit, multiline = false, dryRun = true) + if (node.findChildByType(EQ) == null) { - fixWhitespaceBeforeFunctionBodyBlock(node, emit, autoCorrect, dryRun = true) + fixWhitespaceBeforeFunctionBodyBlock(node, emit, dryRun = true) } else { 0 } @@ -292,8 +291,7 @@ public class FunctionSignatureRule : private fun fixWhiteSpacesInValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -308,11 +306,11 @@ public class FunctionSignatureRule : whiteSpaceCorrection += if (firstParameterInList == null) { // handle empty parameter list - fixWhiteSpacesInEmptyValueParameterList(node, emit, autoCorrect, dryRun) + fixWhiteSpacesInEmptyValueParameterList(node, emit, dryRun) } else { - fixWhiteSpacesBeforeFirstParameterInValueParameterList(node, emit, autoCorrect, multiline, dryRun) + - fixWhiteSpacesBetweenParametersInValueParameterList(node, emit, autoCorrect, multiline, dryRun) + - fixWhiteSpaceBeforeClosingParenthesis(node, emit, autoCorrect, multiline, dryRun) + fixWhiteSpacesBeforeFirstParameterInValueParameterList(node, emit, multiline, dryRun) + + fixWhiteSpacesBetweenParametersInValueParameterList(node, emit, multiline, dryRun) + + fixWhiteSpaceBeforeClosingParenthesis(node, emit, multiline, dryRun) } return whiteSpaceCorrection @@ -320,8 +318,7 @@ public class FunctionSignatureRule : private fun fixWhiteSpacesInEmptyValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, dryRun: Boolean, ): Int { var whiteSpaceCorrection = 0 @@ -342,10 +339,9 @@ public class FunctionSignatureRule : whiteSpace.startOffset, "No whitespace expected in empty parameter list", true, - ) - } - if (autoCorrect && !dryRun) { - whiteSpace.treeParent.removeChild(whiteSpace) + ).ifAutocorrectAllowed { + whiteSpace.treeParent.removeChild(whiteSpace) + } } else { whiteSpaceCorrection -= whiteSpace.textLength } @@ -356,8 +352,7 @@ public class FunctionSignatureRule : private fun fixWhiteSpacesBeforeFirstParameterInValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -384,10 +379,9 @@ public class FunctionSignatureRule : firstParameterInList.startOffset, "Newline expected after opening parenthesis", true, - ) - } - if (autoCorrect && !dryRun) { - valueParameterList.firstChildNode.upsertWhitespaceAfterMe(expectedParameterIndent) + ).ifAutocorrectAllowed { + valueParameterList.firstChildNode.upsertWhitespaceAfterMe(expectedParameterIndent) + } } else { whiteSpaceCorrection += expectedParameterIndent.length - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -399,10 +393,9 @@ public class FunctionSignatureRule : firstParameter!!.startOffset, "No whitespace expected between opening parenthesis and first parameter name", true, - ) - } - if (autoCorrect && !dryRun) { - whiteSpaceBeforeIdentifier.treeParent.removeChild(whiteSpaceBeforeIdentifier) + ).ifAutocorrectAllowed { + whiteSpaceBeforeIdentifier.treeParent.removeChild(whiteSpaceBeforeIdentifier) + } } else { whiteSpaceCorrection -= whiteSpaceBeforeIdentifier.textLength } @@ -415,8 +408,7 @@ public class FunctionSignatureRule : private fun fixWhiteSpacesBetweenParametersInValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -448,10 +440,9 @@ public class FunctionSignatureRule : valueParameter.startOffset, "Parameter should start on a newline", true, - ) - } - if (autoCorrect && !dryRun) { - firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(expectedParameterIndent) + ).ifAutocorrectAllowed { + firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(expectedParameterIndent) + } } else { whiteSpaceCorrection += expectedParameterIndent.length - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -463,10 +454,9 @@ public class FunctionSignatureRule : firstChildNodeInValueParameter!!.startOffset, "Single whitespace expected before parameter", true, - ) - } - if (autoCorrect && !dryRun) { - firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(" ") + ).ifAutocorrectAllowed { + firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(" ") + } } else { whiteSpaceCorrection += 1 - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -480,8 +470,7 @@ public class FunctionSignatureRule : private fun fixWhiteSpaceBeforeClosingParenthesis( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multiline: Boolean, dryRun: Boolean, ): Int { @@ -504,10 +493,9 @@ public class FunctionSignatureRule : closingParenthesis!!.startOffset, "Newline expected before closing parenthesis", true, - ) - } - if (autoCorrect && !dryRun) { - closingParenthesis!!.upsertWhitespaceBeforeMe(newlineAndIndent) + ).ifAutocorrectAllowed { + closingParenthesis.upsertWhitespaceBeforeMe(newlineAndIndent) + } } else { whiteSpaceCorrection += newlineAndIndent.length - (whiteSpaceBeforeClosingParenthesis?.textLength ?: 0) } @@ -521,10 +509,9 @@ public class FunctionSignatureRule : whiteSpaceBeforeClosingParenthesis.startOffset, "No whitespace expected between last parameter and closing parenthesis", true, - ) - } - if (autoCorrect && !dryRun) { - whiteSpaceBeforeClosingParenthesis.treeParent.removeChild(whiteSpaceBeforeClosingParenthesis) + ).ifAutocorrectAllowed { + whiteSpaceBeforeClosingParenthesis.treeParent.removeChild(whiteSpaceBeforeClosingParenthesis) + } } else { whiteSpaceCorrection -= whiteSpaceBeforeClosingParenthesis.textLength } @@ -536,8 +523,7 @@ public class FunctionSignatureRule : private fun fixFunctionBodyExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, maxLengthRemainingForFirstLineOfBodyExpression: Int, ) { val lastNodeOfFunctionSignatureWithBodyExpression = @@ -581,8 +567,7 @@ public class FunctionSignatureRule : whiteSpaceBeforeFunctionBodyExpression!!.startOffset, "First line of body expression fits on same line as function signature", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (whiteSpaceBeforeFunctionBodyExpression as LeafPsiElement).rawReplaceWithText(" ") } } @@ -599,8 +584,7 @@ public class FunctionSignatureRule : functionBodyExpressionNodes.first().startOffset, "Single whitespace expected before expression body", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { functionBodyExpressionNodes .first() .upsertWhitespaceBeforeMe(" ") @@ -614,8 +598,7 @@ public class FunctionSignatureRule : functionBodyExpressionNodes.first().startOffset, "Newline expected before expression body", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { functionBodyExpressionNodes .first() .upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) @@ -649,8 +632,7 @@ public class FunctionSignatureRule : private fun fixWhitespaceBeforeFunctionBodyBlock( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, dryRun: Boolean, ): Int { var whiteSpaceCorrection = 0 @@ -666,9 +648,9 @@ public class FunctionSignatureRule : if (whiteSpaceBeforeBlock == null || whiteSpaceBeforeBlock.text != " ") { if (!dryRun) { emit(block.startOffset, "Expected a single space before body block", true) - } - if (autoCorrect && !dryRun) { - block.upsertWhitespaceBeforeMe(" ") + .ifAutocorrectAllowed { + block.upsertWhitespaceBeforeMe(" ") + } } else { whiteSpaceCorrection += 1 - (whiteSpaceBeforeBlock?.textLength ?: 0) } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionStartOfBodySpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionStartOfBodySpacingRule.kt index e703cacb7c..df14d51330 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionStartOfBodySpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionStartOfBodySpacingRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe @@ -21,33 +23,30 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class FunctionStartOfBodySpacingRule : StandardRule("function-start-of-body-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == FUN) { node .findChildByType(ElementType.EQ) - ?.let { visitFunctionFollowedByBodyExpression(node, emit, autoCorrect) } + ?.let { visitFunctionFollowedByBodyExpression(node, emit) } node .findChildByType(ElementType.BLOCK) - ?.let { visitFunctionFollowedByBodyBlock(node, emit, autoCorrect) } + ?.let { visitFunctionFollowedByBodyBlock(node, emit) } } } private fun visitFunctionFollowedByBodyExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - fixWhiteSpaceBeforeAssignmentOfBodyExpression(node, emit, autoCorrect) - fixWhiteSpaceBetweenAssignmentAndBodyExpression(node, emit, autoCorrect) + fixWhiteSpaceBeforeAssignmentOfBodyExpression(node, emit) + fixWhiteSpaceBetweenAssignmentAndBodyExpression(node, emit) } private fun fixWhiteSpaceBeforeAssignmentOfBodyExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(ElementType.EQ) @@ -61,15 +60,15 @@ public class FunctionStartOfBodySpacingRule : StandardRule("function-start-of-bo assignmentExpression.startOffset, "Expected a single white space before assignment of expression body", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { assignmentExpression.upsertWhitespaceBeforeMe(" ") } + Unit } else if (whiteSpaceBeforeAssignment.text != " ") { emit(whiteSpaceBeforeAssignment.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - assignmentExpression.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + assignmentExpression.upsertWhitespaceBeforeMe(" ") + } } } } @@ -77,8 +76,7 @@ public class FunctionStartOfBodySpacingRule : StandardRule("function-start-of-bo private fun fixWhiteSpaceBetweenAssignmentAndBodyExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(ElementType.EQ) @@ -92,8 +90,7 @@ public class FunctionStartOfBodySpacingRule : StandardRule("function-start-of-bo assignmentExpression.startOffset, "Expected a single white space between assignment and expression body on same line", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { assignmentExpression.upsertWhitespaceAfterMe(" ") } } @@ -103,8 +100,7 @@ public class FunctionStartOfBodySpacingRule : StandardRule("function-start-of-bo private fun visitFunctionFollowedByBodyBlock( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(ElementType.BLOCK) @@ -115,12 +111,12 @@ public class FunctionStartOfBodySpacingRule : StandardRule("function-start-of-bo .let { whiteSpaceBeforeExpressionBlock -> if (whiteSpaceBeforeExpressionBlock?.text != " ") { emit(block.startOffset, "Expected a single white space before start of function body", true) - if (autoCorrect) { - block - .firstChildNode - .prevLeaf(true) - ?.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + block + .firstChildNode + .prevLeaf(true) + ?.upsertWhitespaceAfterMe(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeModifierSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeModifierSpacingRule.kt index c14f798fc1..a67760e135 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeModifierSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeModifierSpacingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_TYPE import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE @@ -7,6 +8,7 @@ import com.pinterest.ktlint.rule.engine.core.api.Rule 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling import com.pinterest.ktlint.rule.engine.core.api.prevSibling import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe @@ -22,8 +24,7 @@ public class FunctionTypeModifierSpacingRule : Rule.Experimental { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == MODIFIER_LIST } @@ -32,9 +33,9 @@ public class FunctionTypeModifierSpacingRule : ?.takeUnless { it.isPrecededBySingleSpace() } ?.let { functionTypeNode -> emit(functionTypeNode.startOffset, "Expected a single space between the modifier list and the function type", true) - if (autoCorrect) { - functionTypeNode.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + functionTypeNode.upsertWhitespaceBeforeMe(" ") + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeReferenceSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeReferenceSpacingRule.kt index 5f83c99f3a..2792dd0450 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeReferenceSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionTypeReferenceSpacingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.NULLABLE_TYPE import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE @@ -8,6 +9,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -17,8 +19,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class FunctionTypeReferenceSpacingRule : StandardRule("function-type-reference-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == FUN) { node @@ -28,11 +29,11 @@ public class FunctionTypeReferenceSpacingRule : StandardRule("function-type-refe .firstChildNode .takeIf { it.elementType == NULLABLE_TYPE } ?.let { nullableTypeElement -> - visitNodesUntilStartOfValueParameterList(nullableTypeElement.firstChildNode, emit, autoCorrect) + visitNodesUntilStartOfValueParameterList(nullableTypeElement.firstChildNode, emit) } if (typeReference.elementType != NULLABLE_TYPE) { - visitNodesUntilStartOfValueParameterList(typeReference, emit, autoCorrect) + visitNodesUntilStartOfValueParameterList(typeReference, emit) } } } @@ -52,27 +53,25 @@ public class FunctionTypeReferenceSpacingRule : StandardRule("function-type-refe private fun visitNodesUntilStartOfValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { var currentNode: ASTNode? = node while (currentNode != null && currentNode.elementType != VALUE_PARAMETER_LIST) { val nextNode = currentNode.nextSibling() - removeIfNonEmptyWhiteSpace(currentNode, emit, autoCorrect) + removeIfNonEmptyWhiteSpace(currentNode, emit) currentNode = nextNode } } private fun removeIfNonEmptyWhiteSpace( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == WHITE_SPACE && node.text.isNotEmpty()) { emit(node.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - node.treeParent.removeChild(node) - } + .ifAutocorrectAllowed { + node.treeParent.removeChild(node) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseBracingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseBracingRule.kt index d5e8ac318d..22ed6c7a98 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseBracingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseBracingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE_KEYWORD @@ -17,6 +18,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -58,18 +60,16 @@ public class IfElseBracingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == IF) { - visitIfStatement(node, autoCorrect, emit) + visitIfStatement(node, emit) } } private fun visitIfStatement( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val thenNode = requireNotNull(node.findChildByType(THEN)) { @@ -87,11 +87,11 @@ public class IfElseBracingRule : val elseBracing = elseNode.hasBracing() if (parentIfBracing || thenBracing || elseBracing) { if (!thenBracing) { - visitBranchWithoutBraces(thenNode, autoCorrect, emit) + visitBranchWithoutBraces(thenNode, emit) } if (!elseBracing) { if (elseNode.firstChildNode?.elementType != IF) { - visitBranchWithoutBraces(elseNode, autoCorrect, emit) + visitBranchWithoutBraces(elseNode, emit) } else { // Postpone changing the else-if until that node is being processed } @@ -120,15 +120,13 @@ public class IfElseBracingRule : private fun visitBranchWithoutBraces( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Boolean { emit( node.firstChildNode?.startOffset ?: node.startOffset, "All branches of the if statement should be wrapped between braces if at least one branch is wrapped between braces", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { autocorrect(node) } return true diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseWrappingRule.kt index ddeb59210b..477dbf6344 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IfElseWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE @@ -20,6 +21,7 @@ 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -63,37 +65,34 @@ public class IfElseWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when { - node.elementType == IF -> visitIf(node, autoCorrect, emit) + node.elementType == IF -> visitIf(node, emit) node.isPartOfComment() && node.treeParent.elementType == IF -> visitComment(node, emit) } } private fun visitIf( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val outerIf = node.outerIf() val multilineIf = outerIf.textContains('\n') val nestedIf = outerIf.isNestedIf() with(node) { findChildByType(THEN) - ?.let { visitElement(it, autoCorrect, emit, multilineIf, nestedIf) } + ?.let { visitElement(it, emit, multilineIf, nestedIf) } findChildByType(ELSE_KEYWORD) - ?.let { visitElement(it, autoCorrect, emit, multilineIf, nestedIf) } + ?.let { visitElement(it, emit, multilineIf, nestedIf) } findChildByType(ELSE) - ?.let { visitElement(it, autoCorrect, emit, multilineIf, nestedIf) } + ?.let { visitElement(it, emit, multilineIf, nestedIf) } } } private fun visitElement( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multilineIf: Boolean, nestedIf: Boolean, ) { @@ -101,13 +100,13 @@ public class IfElseWrappingRule : visitBranchSingleLineIf(node, emit) } if (multilineIf || nestedIf) { - visitBranch(node, autoCorrect, emit, multilineIf) + visitBranch(node, emit, multilineIf) } } private fun visitBranchSingleLineIf( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(BLOCK) @@ -124,8 +123,7 @@ public class IfElseWrappingRule : private fun visitBranch( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, multilineIf: Boolean, ) { if (multilineIf) { @@ -158,9 +156,9 @@ public class IfElseWrappingRule : ?.let { // Expected a newline with indent. Leave it up to the IndentationRule to determine exact indent emit(startOffset, "Expected a newline", true) - if (autoCorrect) { - upsertWhitespaceBeforeMe(expectedIndent) - } + .ifAutocorrectAllowed { + upsertWhitespaceBeforeMe(expectedIndent) + } } } } @@ -199,7 +197,7 @@ public class IfElseWrappingRule : private fun visitComment( comment: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(comment.isPartOfComment()) if (comment.betweenCodeSiblings(ElementType.RPAR, THEN) || diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt index 6b7be1659e..667ce3f08f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt @@ -1,12 +1,14 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType 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.STABLE import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.ruleset.standard.StandardRule import com.pinterest.ktlint.ruleset.standard.rules.ImportOrderingRule.Companion.ASCII_PATTERN @@ -56,8 +58,7 @@ public class ImportOrderingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == ElementType.IMPORT_LIST) { val children = node.getChildren(null) @@ -109,14 +110,15 @@ public class ImportOrderingRule : } else { val autoCorrectWhitespace = hasTooMuchWhitespace(children) && !isCustomLayout() val autoCorrectSortOrder = !importsAreEqual(imports, sortedImportsWithSpaces) + var autocorrect = autoCorrectDuplicateImports if (autoCorrectSortOrder || autoCorrectWhitespace) { emit( node.startOffset, ERROR_MESSAGES.getOrDefault(importsLayout, CUSTOM_ERROR_MESSAGE), true, - ) + ).ifAutocorrectAllowed { autocorrect = true } } - if (autoCorrect && (autoCorrectDuplicateImports || autoCorrectSortOrder || autoCorrectWhitespace)) { + if (autocorrect) { node.removeRange(node.firstChildNode, node.lastChildNode.treeNext) sortedImportsWithSpaces.reduce { current, next -> node.addChild(current, null) @@ -134,7 +136,7 @@ public class ImportOrderingRule : private fun getUniqueImportsAndBlankLines( children: Array, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ): Pair> { var autoCorrectDuplicateImports = false val imports = mutableListOf() @@ -150,7 +152,7 @@ public class ImportOrderingRule : imports += current } else { emit(current.startOffset, "Duplicate '${current.text}' found", true) - autoCorrectDuplicateImports = true + .ifAutocorrectAllowed { autoCorrectDuplicateImports = true } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt index 7c75e77c7f..57d77f30f3 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -85,6 +86,7 @@ import com.pinterest.ktlint.rule.engine.core.api.IndentConfig import com.pinterest.ktlint.rule.engine.core.api.IndentConfig.IndentStyle.SPACE import com.pinterest.ktlint.rule.engine.core.api.IndentConfig.IndentStyle.TAB 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.RuleAutocorrectApproveHandler 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.STABLE @@ -97,6 +99,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -157,7 +160,8 @@ public class IndentationRule : INDENT_SIZE_PROPERTY, INDENT_STYLE_PROPERTY, ), - ) { + ), + RuleAutocorrectApproveHandler { private var codeStyle = CODE_STYLE_PROPERTY.defaultValue private var indentConfig = IndentConfig.DEFAULT_INDENT_CONFIG @@ -181,8 +185,7 @@ public class IndentationRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isRoot()) { // File should not start with a whitespace @@ -191,9 +194,7 @@ public class IndentationRule : ?.takeIf { it.isWhiteSpaceWithoutNewline() } ?.let { whitespaceWithoutNewline -> emit(node.startOffset, "Unexpected indentation", true) - if (autoCorrect) { - whitespaceWithoutNewline.treeParent.removeChild(whitespaceWithoutNewline) - } + .ifAutocorrectAllowed { whitespaceWithoutNewline.treeParent.removeChild(whitespaceWithoutNewline) } } indentContextStack.addLast(startNoIndentZone(node)) } @@ -207,7 +208,7 @@ public class IndentationRule : lastIndentContext.copy(activated = true), ) } - visitNewLineIndentation(node, autoCorrect, emit) + visitNewLineIndentation(node, emit) } node.elementType == CONTEXT_RECEIVER_LIST || @@ -355,7 +356,7 @@ public class IndentationRule : node.elementType == LITERAL_STRING_TEMPLATE_ENTRY && node.nextCodeSibling()?.elementType == CLOSING_QUOTE -> - visitWhiteSpaceBeforeClosingQuote(node, autoCorrect, emit) + visitWhiteSpaceBeforeClosingQuote(node, emit) node.elementType == WHEN -> visitWhen(node) @@ -1036,8 +1037,7 @@ public class IndentationRule : override fun afterVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { while (indentContextStack.peekLast()?.toASTNode == node) { LOGGER.trace { @@ -1070,28 +1070,29 @@ public class IndentationRule : private fun visitNewLineIndentation( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.ignoreIndent()) { return } - val normalizedNodeIndent = node.normalizedIndent(emit) + val normalizedNodeIndent = node.normalizedIndent(emitAndApprove) val expectedIndentation = node.expectedIndent() val text = node.text val nodeIndent = text.substringAfterLast("\n") if (nodeIndent != normalizedNodeIndent || normalizedNodeIndent != expectedIndentation) { - if (normalizedNodeIndent != expectedIndentation) { - emit( - node.startOffset + text.length - nodeIndent.length, - "Unexpected indentation (${normalizedNodeIndent.length}) (should be ${expectedIndentation.length})", - true, - ) - } else { - // Indentation was at correct level but contained invalid indent characters. This violation has already - // been emitted. - } + val autoCorrect = + if (normalizedNodeIndent != expectedIndentation) { + emitAndApprove( + node.startOffset + text.length - nodeIndent.length, + "Unexpected indentation (${normalizedNodeIndent.length}) (should be ${expectedIndentation.length})", + true, + ) == AutocorrectDecision.ALLOW_AUTOCORRECT + } else { + // Indentation was at correct level but contained invalid indent characters. This violation has already + // been emitted. + true + } LOGGER.trace { "Line $line: " + (if (!autoCorrect) "would have " else "") + "changed indentation to ${expectedIndentation.length} " + "(from ${normalizedNodeIndent.length}) for ${node.elementType}: ${node.textWithEscapedTabAndNewline()}" @@ -1153,17 +1154,21 @@ public class IndentationRule : return lastIndexContext.nodeIndent + adjustedChildIndent } - private fun ASTNode.normalizedIndent(emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit): String { + private fun ASTNode.normalizedIndent( + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ): String { val nodeIndent = text.substringAfterLast("\n") return when (indentConfig.indentStyle) { SPACE -> { if ('\t' in nodeIndent) { - emit( + emitAndApprove( startOffset + text.length - nodeIndent.length, "Unexpected tab character(s)", true, - ) - indentConfig.toNormalizedIndent(nodeIndent) + ).ifAutocorrectAllowed { + // Ignore approval and fix invalid indent character always + indentConfig.toNormalizedIndent(nodeIndent) + } ?: nodeIndent } else { nodeIndent } @@ -1173,14 +1178,15 @@ public class IndentationRule : val acceptableTrailingSpaces = acceptableTrailingSpaces() val nodeIndentWithoutAcceptableTrailingSpaces = nodeIndent.removeSuffix(acceptableTrailingSpaces) if (' ' in nodeIndentWithoutAcceptableTrailingSpaces) { - emit( + emitAndApprove( startOffset + text.length - nodeIndent.length, "Unexpected space character(s)", true, - ) - indentConfig - .toNormalizedIndent(nodeIndentWithoutAcceptableTrailingSpaces) - .plus(acceptableTrailingSpaces) + ).ifAutocorrectAllowed { + indentConfig + .toNormalizedIndent(nodeIndentWithoutAcceptableTrailingSpaces) + .plus(acceptableTrailingSpaces) + } ?: nodeIndent } else { nodeIndent } @@ -1190,13 +1196,12 @@ public class IndentationRule : private fun visitWhiteSpaceBeforeClosingQuote( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (!this::stringTemplateIndenter.isInitialized) { stringTemplateIndenter = StringTemplateIndenter(codeStyle, indentConfig) } - stringTemplateIndenter.visitClosingQuotes(currentIndent(), node.treeParent, autoCorrect, emit) + stringTemplateIndenter.visitClosingQuotes(currentIndent(), node.treeParent, emitAndApprove) } private fun ASTNode?.isElvisOperator() = @@ -1334,8 +1339,7 @@ private class StringTemplateIndenter( fun visitClosingQuotes( expectedIndent: String, node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == STRING_TEMPLATE) node @@ -1347,7 +1351,7 @@ private class StringTemplateIndenter( // It can not be determined with certainty how mixed indentation characters should be interpreted. // The trimIndent function handles tabs and spaces equally (one tabs equals one space) while the user // might expect that the tab size in the indentation is more than one space. - emit( + emitAndApprove( node.startOffset, "Indentation of multiline string should not contain both tab(s) and space(s)", false, @@ -1394,18 +1398,18 @@ private class StringTemplateIndenter( // It is a deliberate choice not to fix the indents inside the string literal except the line which only // contains the closing quotes. See 'string-template-indent` rule for fixing the content of the string // template itself - emit(it.startOffset, "Unexpected indent of multiline string closing quotes", true) - if (autoCorrect) { - if (it.firstChildNode == null) { - (it as LeafPsiElement).rawInsertBeforeMe( - LeafPsiElement(REGULAR_STRING_PART, correctedExpectedIndent), - ) - } else { - (it.firstChildNode as LeafPsiElement).rawReplaceWithText( - correctedExpectedIndent + actualContent, - ) + emitAndApprove(it.startOffset, "Unexpected indent of multiline string closing quotes", true) + .ifAutocorrectAllowed { + if (it.firstChildNode == null) { + (it as LeafPsiElement).rawInsertBeforeMe( + LeafPsiElement(REGULAR_STRING_PART, correctedExpectedIndent), + ) + } else { + (it.firstChildNode as LeafPsiElement).rawReplaceWithText( + correctedExpectedIndent + actualContent, + ) + } } - } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt index 1c52ff8445..be0a58db15 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.ENUM_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE @@ -36,8 +37,7 @@ public class KdocRule : Rule.Experimental { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == KDOC } @@ -50,6 +50,7 @@ public class KdocRule : false, ) } + Unit } else { if (it.treeParent.elementType == FILE) { emit(node.startOffset, "A dangling toplevel KDoc is not allowed", false) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt index 3aa6005e63..2198a57068 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_END import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_START @@ -10,6 +11,7 @@ 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.editorconfig.INDENT_SIZE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -33,8 +35,7 @@ public class KdocWrappingRule : ) { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == KDOC) { node @@ -57,9 +58,9 @@ public class KdocWrappingRule : ?.takeIf { isNonIndentLeafOnSameLine(it) } ?.let { nextLeaf -> emit(nextLeaf.startOffset, "A KDoc comment may not be followed by any other element on that same line", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(node.indent()) - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(node.indent()) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt index fdcbf3a776..c3c3ad5595 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.COMMA import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER import com.pinterest.ktlint.rule.engine.core.api.ElementType.STRING_TEMPLATE @@ -69,8 +70,7 @@ public class MaxLineLengthRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isWhiteSpace()) { return diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MixedConditionOperatorsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MixedConditionOperatorsRule.kt index c3318a5fab..69b8c28e52 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MixedConditionOperatorsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MixedConditionOperatorsRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANDAND import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPERATION_REFERENCE @@ -23,8 +24,7 @@ public class MixedConditionOperatorsRule : Rule.Experimental { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.isLogicalBinaryExpression() } @@ -42,7 +42,7 @@ public class MixedConditionOperatorsRule : private fun visitLogicalExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .parent { it.elementType == BINARY_EXPRESSION && it.treeParent.elementType != BINARY_EXPRESSION } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierListSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierListSpacingRule.kt index 84492bcd70..a3f1b6ab95 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierListSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierListSpacingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST @@ -9,6 +10,7 @@ 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.nextSibling @@ -25,22 +27,20 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class ModifierListSpacingRule : StandardRule("modifier-list-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == MODIFIER_LIST) { node .children() - .forEach { visitModifierChild(it, autoCorrect, emit) } + .forEach { visitModifierChild(it, emit) } // The whitespace of the last entry of the modifier list is actually placed outside the modifier list - visitModifierChild(node, autoCorrect, emit) + visitModifierChild(node, emit) } } private fun visitModifierChild( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == WHITE_SPACE) { return @@ -60,22 +60,23 @@ public class ModifierListSpacingRule : StandardRule("modifier-list-spacing") { ) { if (whitespace.text.contains("\n\n")) { emit(whitespace.startOffset, "Single newline expected after annotation", true) - if (autoCorrect) { - (whitespace as LeafPsiElement).rawReplaceWithText( - "\n".plus(whitespace.text.substringAfterLast("\n")), - ) - } + .ifAutocorrectAllowed { + (whitespace as LeafPsiElement).rawReplaceWithText( + "\n".plus(whitespace.text.substringAfterLast("\n")), + ) + } } else if (!whitespace.text.contains('\n') && whitespace.text != " ") { emit(whitespace.startOffset, "Single whitespace or newline expected after annotation", true) - if (autoCorrect) { - (whitespace as LeafPsiElement).rawReplaceWithText(" ") - } + .ifAutocorrectAllowed { + (whitespace as LeafPsiElement).rawReplaceWithText(" ") + } } + Unit } else { emit(whitespace.startOffset, "Single whitespace expected after modifier", true) - if (autoCorrect) { - (whitespace as LeafPsiElement).rawReplaceWithText(" ") - } + .ifAutocorrectAllowed { + (whitespace as LeafPsiElement).rawReplaceWithText(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt index dfdafa27b6..fdb2907e54 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ABSTRACT_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.ACTUAL_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -29,26 +30,26 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VARARG_KEYWORD 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtDeclarationModifierList -import java.util.Arrays @SinceKtlint("0.7", STABLE) public class ModifierOrderRule : StandardRule("modifier-order") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.psi is KtDeclarationModifierList) { val modifierArr = node.getChildren(tokenSet) val sorted = modifierArr.copyOf().apply { sortWith(compareBy { ORDERED_MODIFIERS.indexOf(it.elementType) }) } - if (!Arrays.equals(modifierArr, sorted)) { + if (!modifierArr.contentEquals(sorted)) { // Since annotations can be fairly lengthy and/or span multiple lines we are // squashing them into a single placeholder text to guarantee a single line output + var autocorrect = false squashAnnotations(sorted) .joinToString(" ") .let { squashedAnnotations -> @@ -56,9 +57,9 @@ public class ModifierOrderRule : StandardRule("modifier-order") { node.startOffset, "Incorrect modifier order (should be \"$squashedAnnotations\")", true, - ) + ).ifAutocorrectAllowed { autocorrect = true } } - if (autoCorrect) { + if (autocorrect) { modifierArr.forEachIndexed { i, n -> node.replaceChild(n, sorted[i].clone() as ASTNode) } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultiLineIfElseRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultiLineIfElseRule.kt index c5a1417dcc..c50b5ab687 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultiLineIfElseRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultiLineIfElseRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION @@ -17,6 +18,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -55,8 +57,7 @@ public class MultiLineIfElseRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType != THEN && node.elementType != ELSE) { return @@ -117,9 +118,9 @@ public class MultiLineIfElseRule : } emit(node.firstChildNode.startOffset, "Missing { ... }", true) - if (autoCorrect) { - autocorrect(node) - } + .ifAutocorrectAllowed { + autocorrect(node) + } } private fun autocorrect(node: ASTNode) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt index 4bd8d07398..79756519e6 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARRAY_ACCESS_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW @@ -37,6 +38,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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.isWhiteSpaceWithoutNewline @@ -77,19 +79,18 @@ public class MultilineExpressionWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType in CHAINABLE_EXPRESSION && !node.isPartOfSpreadOperatorExpression() && (node.treeParent.elementType !in CHAINABLE_EXPRESSION || node.isRightHandSideOfBinaryExpression()) ) { - visitExpression(node, emit, autoCorrect) + visitExpression(node, emit) } if (node.elementType == BINARY_EXPRESSION && node.treeParent.elementType != BINARY_EXPRESSION ) { - visitExpression(node, emit, autoCorrect) + visitExpression(node, emit) } } @@ -99,8 +100,7 @@ public class MultilineExpressionWrappingRule : private fun visitExpression( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.containsWhitespaceWithNewline() && node.needToWrapMultilineExpression()) { node @@ -108,43 +108,43 @@ public class MultilineExpressionWrappingRule : .let { prevLeaf -> if (prevLeaf != null && !prevLeaf.textContains('\n')) { emit(node.startOffset, "A multiline expression should start on a new line", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) - val leafOnSameLineAfterMultilineExpression = - node - .lastChildLeafOrSelf() - .nextLeaf { !it.isWhiteSpaceWithoutNewline() && !it.isPartOfComment() } - ?.takeIf { !it.isWhiteSpaceWithNewline() } - when { - leafOnSameLineAfterMultilineExpression == null -> Unit - - leafOnSameLineAfterMultilineExpression.treeParent.elementType == OPERATION_REFERENCE -> { - // When binary expressions are wrapped, each binary expression for itself is checked whether it is a - // multiline expression. So there is no need to check whether wrapping after the operation reference is - // needed - Unit - } - - leafOnSameLineAfterMultilineExpression.elementType == COMMA && - ( - leafOnSameLineAfterMultilineExpression.treeParent.elementType == VALUE_ARGUMENT_LIST || - leafOnSameLineAfterMultilineExpression.treeParent.elementType == VALUE_PARAMETER_LIST - ) -> { - // Keep comma on same line as multiline expression: - // foo( - // fooBar - // .filter { it.bar }, - // ) - leafOnSameLineAfterMultilineExpression - .nextLeaf() - ?.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) - } - - else -> { - leafOnSameLineAfterMultilineExpression.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) + val leafOnSameLineAfterMultilineExpression = + node + .lastChildLeafOrSelf() + .nextLeaf { !it.isWhiteSpaceWithoutNewline() && !it.isPartOfComment() } + ?.takeIf { !it.isWhiteSpaceWithNewline() } + when { + leafOnSameLineAfterMultilineExpression == null -> Unit + + leafOnSameLineAfterMultilineExpression.treeParent.elementType == OPERATION_REFERENCE -> { + // When binary expressions are wrapped, each binary expression for itself is checked whether it is a + // multiline expression. So there is no need to check whether wrapping after the operation reference is + // needed + Unit + } + + leafOnSameLineAfterMultilineExpression.elementType == COMMA && + ( + leafOnSameLineAfterMultilineExpression.treeParent.elementType == VALUE_ARGUMENT_LIST || + leafOnSameLineAfterMultilineExpression.treeParent.elementType == VALUE_PARAMETER_LIST + ) -> { + // Keep comma on same line as multiline expression: + // foo( + // fooBar + // .filter { it.bar }, + // ) + leafOnSameLineAfterMultilineExpression + .nextLeaf() + ?.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) + } + + else -> { + leafOnSameLineAfterMultilineExpression.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) + } } } - } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineLoopRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineLoopRule.kt index 2826f77cd6..a99684531f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineLoopRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineLoopRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.DO_KEYWORD @@ -14,6 +15,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -53,8 +55,7 @@ public class MultilineLoopRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == BODY } @@ -70,10 +71,9 @@ public class MultilineLoopRule : !it.treeParent.textContains('\n') } ?: return emit(node.firstChildNode.startOffset, "Missing { ... }", true) - - if (autoCorrect) { - autocorrect(node) - } + .ifAutocorrectAllowed { + autocorrect(node) + } } private fun autocorrect(node: ASTNode) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBeforeRbraceRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBeforeRbraceRule.kt index 15405905fe..f5c0dc58b5 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBeforeRbraceRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineBeforeRbraceRule.kt @@ -1,9 +1,11 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -14,8 +16,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class NoBlankLineBeforeRbraceRule : StandardRule("no-blank-line-before-rbrace") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node is PsiWhiteSpace && node.textContains('\n') && @@ -27,8 +28,7 @@ public class NoBlankLineBeforeRbraceRule : StandardRule("no-blank-line-before-rb node.startOffset + split[0].length + split[1].length + 1, "Unexpected blank line(s) before \"}\"", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (node as LeafPsiElement).rawReplaceWithText("${split.first()}\n${split.last()}") } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineInListRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineInListRule.kt index 9adfc0aa3b..75a020e810 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineInListRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLineInListRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.SUPER_TYPE_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_ARGUMENT_LIST @@ -13,6 +14,7 @@ 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.prevSibling import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -27,8 +29,7 @@ public class NoBlankLineInListRule : Rule.OfficialCodeStyle { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType != WHITE_SPACE) { return @@ -39,7 +40,7 @@ public class NoBlankLineInListRule : .elementType .takeIf { it in LIST_TYPES } ?.let { treeParentElementType -> - visitWhiteSpace(node, emit, autoCorrect, treeParentElementType) + visitWhiteSpace(node, emit, treeParentElementType) } // Note: depending on the implementation of the list type in the Kotlin language, the whitespace before the first and after the last @@ -55,7 +56,6 @@ public class NoBlankLineInListRule : visitWhiteSpace( node = node, emit = emit, - autoCorrect = autoCorrect, partOfElementType = treeParentElementType, replaceWithSingeSpace = treeParentElementType == TYPE_CONSTRAINT_LIST, ) @@ -74,7 +74,6 @@ public class NoBlankLineInListRule : visitWhiteSpace( node = node, emit = emit, - autoCorrect = autoCorrect, partOfElementType = treeParentElementType, replaceWithSingeSpace = node.nextSibling()?.elementType == CLASS_BODY, ) @@ -83,8 +82,7 @@ public class NoBlankLineInListRule : private fun visitWhiteSpace( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, partOfElementType: IElementType, replaceWithSingeSpace: Boolean = false, ) { @@ -97,8 +95,7 @@ public class NoBlankLineInListRule : node.startOffset + 1, "Unexpected blank line(s) in ${partOfElementType.elementTypeDescription()}", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { if (replaceWithSingeSpace) { (node as LeafPsiElement).rawReplaceWithText(" ") } else { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRule.kt index 3b8cc863b7..67b1ae2419 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoBlankLinesInChainedMethodCallsRule.kt @@ -1,8 +1,10 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION 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.ifAutocorrectAllowed import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace @@ -12,16 +14,14 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class NoBlankLinesInChainedMethodCallsRule : StandardRule("no-blank-lines-in-chained-method-calls") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val isBlankLine = node is PsiWhiteSpace && node.getText().contains("\n\n") if (isBlankLine && node.treeParent.elementType == DOT_QUALIFIED_EXPRESSION) { emit(node.startOffset + 1, "Needless blank line(s)", true) - - if (autoCorrect) { - (node as LeafPsiElement).rawReplaceWithText("\n" + node.getText().split("\n\n")[1]) - } + .ifAutocorrectAllowed { + (node as LeafPsiElement).rawReplaceWithText("\n" + node.getText().split("\n\n")[1]) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveBlankLinesRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveBlankLinesRule.kt index 1b31726008..5de5953a76 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveBlankLinesRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveBlankLinesRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER import com.pinterest.ktlint.rule.engine.core.api.ElementType.PRIMARY_CONSTRUCTOR 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -17,8 +19,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class NoConsecutiveBlankLinesRule : StandardRule("no-consecutive-blank-lines") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node is PsiWhiteSpace && node.prevSibling != null @@ -44,18 +45,18 @@ public class NoConsecutiveBlankLinesRule : StandardRule("no-consecutive-blank-li 2 } emit(offset, "Needless blank line(s)", true) - if (autoCorrect) { - val newText = - buildString { - append(split.first()) - append("\n") - if (!eof && !betweenClassAndPrimaryConstructor) { + .ifAutocorrectAllowed { + val newText = + buildString { + append(split.first()) append("\n") + if (!eof && !betweenClassAndPrimaryConstructor) { + append("\n") + } + append(split.last()) } - append(split.last()) - } - (node as LeafPsiElement).rawReplaceWithText(newText) - } + (node as LeafPsiElement).rawReplaceWithText(newText) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt index 503d04a567..e3816dde27 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_END @@ -29,8 +30,7 @@ public class NoConsecutiveCommentsRule : Rule.OfficialCodeStyle { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.isStartOfComment() } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt index fe43aca326..3fbabcd793 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE @@ -8,6 +9,7 @@ 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.STABLE import com.pinterest.ktlint.rule.engine.core.api.children +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -18,8 +20,7 @@ import org.jetbrains.kotlin.psi.KtObjectLiteralExpression public class NoEmptyClassBodyRule : StandardRule("no-empty-class-body") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == CLASS_BODY && node.firstChildNode?.let { n -> @@ -34,15 +35,15 @@ public class NoEmptyClassBodyRule : StandardRule("no-empty-class-body") { .none { it.text == "companion" } ) { emit(node.startOffset, "Unnecessary block (\"{}\")", true) - if (autoCorrect) { - val prevNode = node.treePrev - if (prevNode.elementType == WHITE_SPACE) { - // remove space between declaration and block - prevNode.treeParent.removeChild(prevNode) + .ifAutocorrectAllowed { + val prevNode = node.treePrev + if (prevNode.elementType == WHITE_SPACE) { + // remove space between declaration and block + prevNode.treeParent.removeChild(prevNode) + } + // remove block + node.treeParent.removeChild(node) } - // remove block - node.treeParent.removeChild(node) - } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFileRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFileRule.kt index 4da4869c08..b76ac6b8fd 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFileRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFileRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint @@ -17,8 +18,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class NoEmptyFileRule : StandardRule(id = "no-empty-file") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.isRoot() } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInClassBodyRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInClassBodyRule.kt index 9a280e1e79..ecf1ff3b03 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInClassBodyRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInClassBodyRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.IndentConfig import com.pinterest.ktlint.rule.engine.core.api.Rule @@ -10,6 +11,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -40,8 +42,7 @@ public class NoEmptyFirstLineInClassBodyRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == CLASS_BODY) { node @@ -58,8 +59,7 @@ public class NoEmptyFirstLineInClassBodyRule : whitespace.startOffset + 1, "Class body should not start with blank line", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (whitespace as LeafPsiElement).rawReplaceWithText(indentConfig.childIndentOf(node)) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInMethodBlockRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInMethodBlockRule.kt index b7f3ffb6ce..fd317e95b5 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInMethodBlockRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyFirstLineInMethodBlockRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE @@ -7,6 +8,7 @@ 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -19,8 +21,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class NoEmptyFirstLineInMethodBlockRule : StandardRule("no-empty-first-line-in-method-block") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isWhiteSpaceWithNewline() && node.prevLeaf()?.elementType == LBRACE && @@ -35,8 +36,7 @@ public class NoEmptyFirstLineInMethodBlockRule : StandardRule("no-empty-first-li node.startOffset + 1, "First line in a method block should not be empty", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (node as LeafPsiElement).rawReplaceWithText("${split.first()}\n${split.last()}") } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterElseRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterElseRule.kt index ea83c56504..387f89ab04 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterElseRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakAfterElseRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.IF_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -17,8 +19,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class NoLineBreakAfterElseRule : StandardRule("no-line-break-after-else") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node is PsiWhiteSpace && node.textContains('\n') @@ -27,9 +28,9 @@ public class NoLineBreakAfterElseRule : StandardRule("no-line-break-after-else") node.nextLeaf()?.elementType.let { it == IF_KEYWORD || it == LBRACE } ) { emit(node.startOffset + 1, "Unexpected line break after \"else\"", true) - if (autoCorrect) { - (node as LeafPsiElement).rawReplaceWithText(" ") - } + .ifAutocorrectAllowed { + (node as LeafPsiElement).rawReplaceWithText(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakBeforeAssignmentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakBeforeAssignmentRule.kt index 6d4b463c8d..a155a95283 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakBeforeAssignmentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoLineBreakBeforeAssignmentRule.kt @@ -1,9 +1,11 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -19,49 +21,47 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings public class NoLineBreakBeforeAssignmentRule : StandardRule("no-line-break-before-assignment") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == EQ) { - visitEquals(node, emit, autoCorrect) + visitEquals(node, emit) } } private fun visitEquals( assignmentNode: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { assignmentNode .prevSibling() .takeIf { it.isWhiteSpaceWithNewline() } ?.let { unexpectedNewlineBeforeAssignment -> emit(unexpectedNewlineBeforeAssignment.startOffset, "Line break before assignment is not allowed", true) - if (autoCorrect) { - val parent = assignmentNode.treeParent - // Insert assignment surrounded by whitespaces at new position - assignmentNode - .siblings(false) - .takeWhile { it.isWhiteSpace() || it.isPartOfComment() } - .last() - .let { before -> - if (!before.prevSibling().isWhiteSpace()) { - parent.addChild(PsiWhiteSpaceImpl(" "), before) + .ifAutocorrectAllowed { + val parent = assignmentNode.treeParent + // Insert assignment surrounded by whitespaces at new position + assignmentNode + .siblings(false) + .takeWhile { it.isWhiteSpace() || it.isPartOfComment() } + .last() + .let { before -> + if (!before.prevSibling().isWhiteSpace()) { + parent.addChild(PsiWhiteSpaceImpl(" "), before) + } + parent.addChild(LeafPsiElement(EQ, "="), before) + if (!before.isWhiteSpace()) { + parent.addChild(PsiWhiteSpaceImpl(" "), before) + } } - parent.addChild(LeafPsiElement(EQ, "="), before) - if (!before.isWhiteSpace()) { - parent.addChild(PsiWhiteSpaceImpl(" "), before) + // Cleanup old assignment and whitespace after it. The indent before the old assignment is kept unchanged + assignmentNode + .nextSibling() + .takeIf { it.isWhiteSpace() } + ?.let { whiteSpaceAfterEquals -> + parent.removeChild(whiteSpaceAfterEquals) } - } - // Cleanup old assignment and whitespace after it. The indent before the old assignment is kept unchanged - assignmentNode - .nextSibling() - .takeIf { it.isWhiteSpace() } - ?.let { whiteSpaceAfterEquals -> - parent.removeChild(whiteSpaceAfterEquals) - } - parent.removeChild(assignmentNode) - } + parent.removeChild(assignmentNode) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoMultipleSpacesRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoMultipleSpacesRule.kt index a6ceb95499..60b9fab25b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoMultipleSpacesRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoMultipleSpacesRule.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_MARKDOWN_LINK import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_TAG 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace @@ -14,8 +16,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class NoMultipleSpacesRule : StandardRule("no-multi-spaces") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { node is PsiWhiteSpace } @@ -24,10 +25,10 @@ public class NoMultipleSpacesRule : StandardRule("no-multi-spaces") { val beforeIndentation = node.removeIndentation() if (beforeIndentation.length > 1) { emit(node.startOffset + 1, "Unnecessary long whitespace", true) - if (autoCorrect) { - val remainder = node.text.substring(beforeIndentation.length) - (node as LeafPsiElement).rawReplaceWithText(" $remainder") - } + .ifAutocorrectAllowed { + val remainder = node.text.substring(beforeIndentation.length) + (node as LeafPsiElement).rawReplaceWithText(" $remainder") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt index 007ea1423b..1f82840129 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.ENUM_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_KEYWORD @@ -8,6 +9,7 @@ import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRu 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.nextLeaf @@ -41,8 +43,7 @@ public class NoSemicolonsRule : ) { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType != SEMICOLON) { return @@ -50,13 +51,13 @@ public class NoSemicolonsRule : val nextLeaf = node.nextLeaf() if (nextLeaf.doesNotRequirePreSemi() && isNoSemicolonRequiredAfter(node)) { emit(node.startOffset, "Unnecessary semicolon", true) - if (autoCorrect) { - val prevLeaf = node.prevLeaf(true) - node.treeParent.removeChild(node) - if (prevLeaf.isWhiteSpace() && (nextLeaf == null || nextLeaf.isWhiteSpace())) { - node.treeParent.removeChild(prevLeaf!!) + .ifAutocorrectAllowed { + val prevLeaf = node.prevLeaf(true) + node.treeParent.removeChild(node) + if (prevLeaf.isWhiteSpace() && (nextLeaf == null || nextLeaf.isWhiteSpace())) { + node.treeParent.removeChild(prevLeaf!!) + } } - } } else if (nextLeaf !is PsiWhiteSpace) { val prevLeaf = node.prevLeaf() if (prevLeaf is PsiWhiteSpace && prevLeaf.textContains('\n')) { @@ -64,9 +65,9 @@ public class NoSemicolonsRule : } // todo: move to a separate rule emit(node.startOffset + 1, "Missing spacing after \";\"", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(" ") + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSingleLineBlockCommentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSingleLineBlockCommentRule.kt index 6095a27386..f067ce2be1 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSingleLineBlockCommentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSingleLineBlockCommentRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.Rule @@ -11,6 +12,7 @@ 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.editorconfig.INDENT_SIZE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -39,8 +41,7 @@ public class NoSingleLineBlockCommentRule : Rule.OfficialCodeStyle { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == BLOCK_COMMENT) { val afterBlockComment = @@ -54,9 +55,9 @@ public class NoSingleLineBlockCommentRule : afterBlockComment.nextLeaf().isWhitespaceWithNewlineOrNull() ) { emit(node.startOffset, "Replace the block comment with an EOL comment", true) - if (autoCorrect) { - node.replaceWithEndOfLineComment() - } + .ifAutocorrectAllowed { + node.replaceWithEndOfLineComment() + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt index 77886232a0..d0b209e09d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.parent @@ -17,8 +19,7 @@ import org.jetbrains.kotlin.kdoc.psi.api.KDoc public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isPartOfKDoc()) { if (node.elementType == WHITE_SPACE && node.hasTrailingSpacesBeforeNewline()) { @@ -30,13 +31,13 @@ public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { .dropLastWhile { it == ' ' } .length emit(node.startOffset + offsetOfFirstSpaceBeforeNewlineInText, "Trailing space(s)", true) - if (autoCorrect) { - node.removeTrailingSpacesBeforeNewline() - } + .ifAutocorrectAllowed { + node.removeTrailingSpacesBeforeNewline() + } } } else if (node.elementType == WHITE_SPACE || node.isPartOfComment()) { val lines = node.text.split("\n") - var violated = false + var autocorrect = false var violationOffset = node.startOffset val modifiedLines = @@ -53,7 +54,9 @@ public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { val modifiedLine = line.trimEnd() val firstTrailingSpaceOffset = violationOffset + modifiedLine.length emit(firstTrailingSpaceOffset, "Trailing space(s)", true) - violated = true + .ifAutocorrectAllowed { + autocorrect = true + } modifiedLine } @@ -62,7 +65,7 @@ public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { violationOffset += line.length + 1 modifiedLine } - if (violated && autoCorrect) { + if (autocorrect) { (node as LeafPsiElement).rawReplaceWithText(modifiedLines.joinToString(separator = "\n")) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRule.kt index d059ad119f..714d425ce2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnitReturnRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE @@ -7,6 +8,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER_LIS 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -15,8 +17,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class NoUnitReturnRule : StandardRule("no-unit-return") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == TYPE_REFERENCE && node.treeParent.elementType == FUN && @@ -24,13 +25,13 @@ public class NoUnitReturnRule : StandardRule("no-unit-return") { node.nextCodeLeaf(skipSubtree = true)?.elementType == LBRACE ) { emit(node.startOffset, "Unnecessary \"Unit\" return type", true) - if (autoCorrect) { - var prevNode = node - while (prevNode.treePrev.elementType != VALUE_PARAMETER_LIST) { - prevNode = prevNode.treePrev + .ifAutocorrectAllowed { + var prevNode = node + while (prevNode.treePrev.elementType != VALUE_PARAMETER_LIST) { + prevNode = prevNode.treePrev + } + node.treeParent.removeRange(prevNode, node.treeNext) } - node.treeParent.removeRange(prevNode, node.treeNext) - } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt index 83ffd8135d..9409ecb331 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BY_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE @@ -11,6 +12,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.REFERENCE_EXPRESSIO 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.isRoot import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -47,8 +49,7 @@ public class NoUnusedImportsRule : StandardRule("no-unused-imports") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isRoot()) { rootNode = node @@ -64,9 +65,9 @@ public class NoUnusedImportsRule : StandardRule("no-unused-imports") { if (imports.containsKey(importPath)) { // Emit directly when same import occurs more than once emit(node.startOffset, "Unused import", true) - if (autoCorrect) { - node.psi.delete() - } + .ifAutocorrectAllowed { + node.psi.delete() + } } else { imports[importPath] = node } @@ -117,8 +118,7 @@ public class NoUnusedImportsRule : StandardRule("no-unused-imports") { override fun afterVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == FILE) { val directCalls = ref.filter { !it.inDotQualifiedExpression }.map { it.text } @@ -129,10 +129,10 @@ public class NoUnusedImportsRule : StandardRule("no-unused-imports") { importPath.endsWith(".$parent") && directCalls.none { importPath.endsWith(".$it") } }.forEach { (importPath, importNode) -> emit(importNode.startOffset, "Unused import", true) - if (autoCorrect) { - imports.remove(importPath, importNode) - importNode.removeImportDirective() - } + .ifAutocorrectAllowed { + imports.remove(importPath, importNode) + importNode.removeImportDirective() + } } } @@ -150,9 +150,9 @@ public class NoUnusedImportsRule : StandardRule("no-unused-imports") { importPath.substring(packageName.length + 1).indexOf('.') == -1 ) { emit(node.startOffset, "Unnecessary import", true) - if (autoCorrect) { - importDirective.delete() - } + .ifAutocorrectAllowed { + importDirective.delete() + } } else if (name != null && (!ref.map { it.text }.contains(name) || !isAValidImport(importPath)) && !OPERATOR_SET.contains(name) && @@ -160,38 +160,38 @@ public class NoUnusedImportsRule : StandardRule("no-unused-imports") { !importPath.ignoreProvideDelegate() ) { emit(node.startOffset, "Unused import", true) - if (autoCorrect) { - val nextSibling = node.nextSibling() - if (nextSibling == null) { - // Last import - node - .lastChildLeafOrSelf() - .nextLeaf() - ?.takeIf { it.isWhiteSpaceWithNewline() } - ?.let { whitespace -> - if (node.prevLeaf() == null) { - // Also it was the first import, and it is not preceded by any other node containing some text. So - // all whitespace until the next is redundant - whitespace.treeParent.removeChild(whitespace) - } else { - val textAfterFirstNewline = - whitespace - .text - .substringAfter("\n") - if (textAfterFirstNewline.isNotBlank()) { - (whitespace as LeafElement).rawReplaceWithText(textAfterFirstNewline) + .ifAutocorrectAllowed { + val nextSibling = node.nextSibling() + if (nextSibling == null) { + // Last import + node + .lastChildLeafOrSelf() + .nextLeaf() + ?.takeIf { it.isWhiteSpaceWithNewline() } + ?.let { whitespace -> + if (node.prevLeaf() == null) { + // Also it was the first import, and it is not preceded by any other node containing some text. So + // all whitespace until the next is redundant + whitespace.treeParent.removeChild(whitespace) + } else { + val textAfterFirstNewline = + whitespace + .text + .substringAfter("\n") + if (textAfterFirstNewline.isNotBlank()) { + (whitespace as LeafElement).rawReplaceWithText(textAfterFirstNewline) + } } } - } - } else { - nextSibling - .takeIf { it.isWhiteSpaceWithNewline() } - ?.let { whitespace -> - whitespace.treeParent.removeChild(whitespace) - } + } else { + nextSibling + .takeIf { it.isWhiteSpaceWithNewline() } + ?.let { whitespace -> + whitespace.treeParent.removeChild(whitespace) + } + } + importDirective.delete() } - importDirective.delete() - } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt index 153df4f75f..d21a825530 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.IMPORT_DIRECTIVE import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint @@ -29,8 +30,7 @@ public class NoWildcardImportsRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == IMPORT_DIRECTIVE) { val importDirective = node.psi as KtImportDirective diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpacingRule.kt index c93222ec61..5e34601898 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NullableTypeSpacingRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.QUEST import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -16,8 +18,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class NullableTypeSpacingRule : StandardRule("nullable-type-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { node.elementType == QUEST } @@ -25,9 +26,9 @@ public class NullableTypeSpacingRule : StandardRule("nullable-type-spacing") { ?.takeIf { it.elementType == WHITE_SPACE } ?.let { whiteSpaceBeforeQuest -> emit(whiteSpaceBeforeQuest.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - (whiteSpaceBeforeQuest as LeafPsiElement).rawRemove() - } + .ifAutocorrectAllowed { + (whiteSpaceBeforeQuest as LeafPsiElement).rawRemove() + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PackageNameRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PackageNameRule.kt index 49b9033226..1a9fa01e38 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PackageNameRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PackageNameRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.PACKAGE_DIRECTIVE import com.pinterest.ktlint.rule.engine.core.api.RuleId @@ -17,8 +18,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class PackageNameRule : StandardRule("package-name") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { node.elementType == PACKAGE_DIRECTIVE } @@ -31,6 +31,7 @@ public class PackageNameRule : StandardRule("package-name") { // underscores as well. But as this has been forbidden by KtLint since early versions, this is still // prohibited. emit(expression.startOffset, "Package name must not contain underscore", false) + Unit } else if (!expression.text.matches(VALID_PACKAGE_NAME_REGEXP)) { emit(expression.startOffset, "Package name contains a disallowed character", false) } 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 3e0a0610dc..2d631c4a4a 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 @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.COMMA @@ -15,6 +16,7 @@ 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.ifAutocorrectAllowed 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.lineLength @@ -50,18 +52,16 @@ public class ParameterListSpacingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == VALUE_PARAMETER_LIST) { - visitValueParameterList(node, emit, autoCorrect) + visitValueParameterList(node, emit) } } private fun visitValueParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == VALUE_PARAMETER_LIST) val countValueParameters = @@ -80,11 +80,11 @@ public class ParameterListSpacingRule : when (el.elementType) { WHITE_SPACE -> { if (countValueParameters == 0 && node.containsNoComments()) { - removeUnexpectedWhiteSpace(el, emit, autoCorrect) + removeUnexpectedWhiteSpace(el, emit) } else if (valueParameterCount == 0 && el.isNotIndent()) { if (node.containsNoComments()) { // whitespace before first parameter - removeUnexpectedWhiteSpace(el, emit, autoCorrect) + removeUnexpectedWhiteSpace(el, emit) } else { // Avoid conflict with comment spacing rule which requires a whitespace before the // EOL-comment @@ -92,17 +92,17 @@ public class ParameterListSpacingRule : } else if (valueParameterCount == countValueParameters && el.isNotIndent()) { if (node.containsNoComments()) { // whitespace after the last parameter - removeUnexpectedWhiteSpace(el, emit, autoCorrect) + removeUnexpectedWhiteSpace(el, emit) } else { // Avoid conflict with comment spacing rule which requires a whitespace before the // EOL-comment } } else if (el.nextCodeSibling()?.elementType == COMMA) { // No whitespace between parameter name and comma allowed - removeUnexpectedWhiteSpace(el, emit, autoCorrect) + removeUnexpectedWhiteSpace(el, emit) } else if (el.elementType == WHITE_SPACE && el.isNotIndent() && el.isNotSingleSpace()) { require(el.prevCodeSibling()?.elementType == COMMA) - replaceWithSingleSpace(el, emit, autoCorrect) + replaceWithSingleSpace(el, emit) } } @@ -111,12 +111,12 @@ public class ParameterListSpacingRule : el .nextLeaf() ?.takeIf { it.elementType != WHITE_SPACE } - ?.let { addMissingWhiteSpaceAfterMe(el, emit, autoCorrect) } + ?.let { addMissingWhiteSpaceAfterMe(el, emit) } } VALUE_PARAMETER -> { valueParameterCount += 1 - visitValueParameter(el, emit, autoCorrect) + visitValueParameter(el, emit) } } } @@ -126,28 +126,25 @@ public class ParameterListSpacingRule : private fun visitValueParameter( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - visitModifierList(node, emit, autoCorrect) - removeWhiteSpaceBetweenParameterIdentifierAndColon(node, emit, autoCorrect) - fixWhiteSpaceAfterColonInParameter(node, emit, autoCorrect) + visitModifierList(node, emit) + removeWhiteSpaceBetweenParameterIdentifierAndColon(node, emit) + fixWhiteSpaceAfterColonInParameter(node, emit) } private fun visitModifierList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val modifierList = node.findChildByType(MODIFIER_LIST) ?: return - removeWhiteSpaceBetweenModifiersInList(modifierList, emit, autoCorrect) - removeWhiteSpaceBetweenModifierListAndParameterIdentifier(modifierList, emit, autoCorrect) + removeWhiteSpaceBetweenModifiersInList(modifierList, emit) + removeWhiteSpaceBetweenModifierListAndParameterIdentifier(modifierList, emit) } private fun removeWhiteSpaceBetweenModifiersInList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == MODIFIER_LIST) node @@ -155,52 +152,48 @@ public class ParameterListSpacingRule : .filter { it.elementType == WHITE_SPACE } // Store elements in list before changing them as otherwise only the first whitespace is being changed .toList() - .forEach { visitWhiteSpaceAfterModifier(it, emit, autoCorrect) } + .forEach { visitWhiteSpaceAfterModifier(it, emit) } } private fun visitWhiteSpaceAfterModifier( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeUnless { // Ignore when the modifier is an annotation which is placed on a separate line it.isIndent() && it.getPrecedingModifier()?.elementType == ANNOTATION_ENTRY }?.takeIf { it.isNotSingleSpace() } - ?.let { replaceWithSingleSpace(it, emit, autoCorrect) } + ?.let { replaceWithSingleSpace(it, emit) } } private fun removeWhiteSpaceBetweenModifierListAndParameterIdentifier( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == MODIFIER_LIST) node .nextSibling() ?.takeIf { it.elementType == WHITE_SPACE } - ?.let { visitWhiteSpaceAfterModifier(it, emit, autoCorrect) } + ?.let { visitWhiteSpaceAfterModifier(it, emit) } } private fun removeWhiteSpaceBetweenParameterIdentifierAndColon( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(COLON) ?.prevLeaf() ?.takeIf { it.elementType == WHITE_SPACE } ?.let { whiteSpaceBeforeColon -> - removeUnexpectedWhiteSpace(whiteSpaceBeforeColon, emit, autoCorrect) + removeUnexpectedWhiteSpace(whiteSpaceBeforeColon, emit) } } private fun fixWhiteSpaceAfterColonInParameter( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val colonNode = node.findChildByType(COLON) ?: return colonNode @@ -208,7 +201,7 @@ public class ParameterListSpacingRule : ?.takeIf { it.elementType == WHITE_SPACE } .let { whiteSpaceAfterColon -> if (whiteSpaceAfterColon == null) { - addMissingWhiteSpaceAfterMe(colonNode, emit, autoCorrect) + addMissingWhiteSpaceAfterMe(colonNode, emit) } else { if (node.isTypeReferenceWithModifierList() && whiteSpaceAfterColon.isIndent()) { // Allow the type to be wrapped to the next line when it has a modifier: @@ -226,7 +219,7 @@ public class ParameterListSpacingRule : // ) Unit } else if (whiteSpaceAfterColon.isNotSingleSpace()) { - replaceWithSingleSpace(whiteSpaceAfterColon, emit, autoCorrect) + replaceWithSingleSpace(whiteSpaceAfterColon, emit) } } } @@ -234,14 +227,13 @@ public class ParameterListSpacingRule : private fun addMissingWhiteSpaceAfterMe( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == COLON || node.elementType == COMMA) emit(node.startOffset, "Whitespace after '${node.text}' is missing", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(" ") + } } private fun ASTNode.isNotIndent(): Boolean = !isIndent() @@ -258,24 +250,22 @@ public class ParameterListSpacingRule : private fun removeUnexpectedWhiteSpace( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit(node.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - (node as LeafElement).rawRemove() - } + .ifAutocorrectAllowed { + (node as LeafElement).rawRemove() + } } private fun replaceWithSingleSpace( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit(node.startOffset, "Expected a single space", true) - if (autoCorrect) { - (node as LeafPsiElement).rawReplaceWithText(" ") - } + .ifAutocorrectAllowed { + (node as LeafPsiElement).rawReplaceWithText(" ") + } } private fun ASTNode.getPrecedingModifier(): ASTNode? = diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt index 3ff2a962e0..00cb9efd1c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_LITERAL @@ -22,6 +23,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -71,19 +73,17 @@ public class ParameterListWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { - NULLABLE_TYPE -> visitNullableType(node, emit, autoCorrect) - VALUE_PARAMETER_LIST -> visitParameterList(node, emit, autoCorrect) + NULLABLE_TYPE -> visitNullableType(node, emit) + VALUE_PARAMETER_LIST -> visitParameterList(node, emit) } } private fun visitNullableType( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == NULLABLE_TYPE) node @@ -101,8 +101,7 @@ public class ParameterListWrappingRule : lpar.startOffset + 1, "Expected new line before function type as it does not fit on a single line", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { lpar.upsertWhitespaceAfterMe(indentConfig.childIndentOf(node)) } } @@ -114,8 +113,7 @@ public class ParameterListWrappingRule : rpar.startOffset, "Expected new line after function type as it does not fit on a single line", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { rpar.upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(node)) } } @@ -188,15 +186,14 @@ public class ParameterListWrappingRule : private fun visitParameterList( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (isPrecededByComment(node)) { emit(node.startOffset, "Parameter list should not be preceded by a comment", false) } else if (node.needToWrapParameterList()) { node .children() - .forEach { child -> wrapParameterInList(child, emit, autoCorrect) } + .forEach { child -> wrapParameterInList(child, emit) } } } @@ -242,8 +239,7 @@ public class ParameterListWrappingRule : private fun wrapParameterInList( child: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (child.elementType) { LPAR -> { @@ -252,9 +248,9 @@ public class ParameterListWrappingRule : prevLeaf.isWhiteSpaceWithNewline() ) { emit(child.startOffset, errorMessage(child), true) - if (autoCorrect) { - (prevLeaf as PsiWhiteSpace).delete() - } + .ifAutocorrectAllowed { + (prevLeaf as PsiWhiteSpace).delete() + } } } @@ -275,20 +271,20 @@ public class ParameterListWrappingRule : } else { // The current child needs to be wrapped to a newline. emit(child.startOffset, errorMessage(child), true) - if (autoCorrect) { - // The indentation is purely based on the previous leaf only. Note that in - // autoCorrect mode the indent rule, if enabled, runs after this rule and - // determines the final indentation. But if the indent rule is disabled then the - // indent of this rule is kept. - (prevLeaf as LeafPsiElement).rawReplaceWithText(intendedIndent) - } + .ifAutocorrectAllowed { + // The indentation is purely based on the previous leaf only. Note that in + // autoCorrect mode the indent rule, if enabled, runs after this rule and + // determines the final indentation. But if the indent rule is disabled then the + // indent of this rule is kept. + (prevLeaf as LeafPsiElement).rawReplaceWithText(intendedIndent) + } } } else { // Insert a new whitespace element in order to wrap the current child to a new line. emit(child.startOffset, errorMessage(child), true) - if (autoCorrect) { - child.treeParent.addChild(PsiWhiteSpaceImpl(intendedIndent), child) - } + .ifAutocorrectAllowed { + child.treeParent.addChild(PsiWhiteSpaceImpl(intendedIndent), child) + } } // Indentation of child nodes need to be fixed by the IndentationRule. } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterWrappingRule.kt index 35f2dfa9be..2975241c27 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterWrappingRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON @@ -18,6 +19,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf @@ -68,18 +70,16 @@ public class ParameterWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == VALUE_PARAMETER) { - rearrangeValueParameter(node, autoCorrect, emit) + rearrangeValueParameter(node, emit) } } private fun rearrangeValueParameter( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == VALUE_PARAMETER) @@ -99,7 +99,7 @@ public class ParameterWrappingRule : ?.let { colon -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(colon) > maxLineLength) { fromNode.sumOfTextLengthUntil(colon) - requireNewlineAfterLeaf(colon, autoCorrect, emit) + requireNewlineAfterLeaf(colon, emit) return } } @@ -108,7 +108,7 @@ public class ParameterWrappingRule : .findChildByType(TYPE_REFERENCE) ?.let { typeReference -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(typeReference.orTrailingComma()) > maxLineLength) { - requireNewlineBeforeLeaf(typeReference, autoCorrect, emit) + requireNewlineBeforeLeaf(typeReference, emit) return } } @@ -117,7 +117,7 @@ public class ParameterWrappingRule : .findChildByType(EQ) ?.let { equal -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(equal.orTrailingComma()) > maxLineLength) { - requireNewlineAfterLeaf(equal, autoCorrect, emit) + requireNewlineAfterLeaf(equal, emit) return } } @@ -126,7 +126,7 @@ public class ParameterWrappingRule : .findChildByType(CALL_EXPRESSION) ?.let { callExpression -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(callExpression.orTrailingComma()) > maxLineLength) { - requireNewlineBeforeLeaf(callExpression, autoCorrect, emit) + requireNewlineBeforeLeaf(callExpression, emit) return } } @@ -147,35 +147,37 @@ public class ParameterWrappingRule : private fun requireNewlineBeforeLeaf( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit( node.startOffset - 1, """Missing newline before "${node.text}"""", true, - ) - LOGGER.trace { "$line: " + ((if (!autoCorrect) "would have " else "") + "inserted newline before ${node.text}") } - if (autoCorrect) { + ).also { autocorrectDecision -> + LOGGER.trace { + "$line: " + (if (autocorrectDecision == AutocorrectDecision.NO_AUTOCORRECT) "would have " else "") + + "inserted newline before ${node.text}" + } + }.ifAutocorrectAllowed { node.upsertWhitespaceBeforeMe(node.indent()) } } private fun requireNewlineAfterLeaf( nodeAfterWhichNewlineIsRequired: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, nodeToFix: ASTNode = nodeAfterWhichNewlineIsRequired, ) { emit( nodeAfterWhichNewlineIsRequired.startOffset + 1, """Missing newline after "${nodeAfterWhichNewlineIsRequired.text}"""", true, - ) - LOGGER.trace { - "$line: " + (if (!autoCorrect) "would have " else "") + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" - } - if (autoCorrect) { + ).also { autocorrectDecision -> + LOGGER.trace { + "$line: " + (if (autocorrectDecision == AutocorrectDecision.NO_AUTOCORRECT) "would have " else "") + + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" + } + }.ifAutocorrectAllowed { nodeToFix.upsertWhitespaceAfterMe(indentConfig.childIndentOf(nodeToFix)) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyNamingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyNamingRule.kt index 2a0da216ef..2139e0b1fb 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyNamingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyNamingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONST_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE @@ -30,8 +31,7 @@ import org.jetbrains.kotlin.lexer.KtTokens public class PropertyNamingRule : StandardRule("property-naming") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { node.elementType == PROPERTY } @@ -40,7 +40,7 @@ public class PropertyNamingRule : StandardRule("property-naming") { private fun visitProperty( property: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { property .findChildByType(IDENTIFIER) @@ -64,7 +64,7 @@ public class PropertyNamingRule : StandardRule("property-naming") { private fun visitConstProperty( identifier: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { identifier .text @@ -86,7 +86,7 @@ public class PropertyNamingRule : StandardRule("property-naming") { private fun visitNonConstProperty( identifier: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { identifier .text diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyWrappingRule.kt index 6e6764c768..b31d69d359 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/PropertyWrappingRule.kt @@ -1,6 +1,8 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON @@ -16,6 +18,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf @@ -65,18 +68,16 @@ public class PropertyWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == PROPERTY) { - rearrangeProperty(node, autoCorrect, emit) + rearrangeProperty(node, emit) } } private fun rearrangeProperty( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == PROPERTY) @@ -96,7 +97,7 @@ public class PropertyWrappingRule : ?.let { colon -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(colon) > maxLineLength) { fromNode.sumOfTextLengthUntil(colon) - requireNewlineAfterLeaf(colon, autoCorrect, emit) + requireNewlineAfterLeaf(colon, emit) return } } @@ -105,7 +106,7 @@ public class PropertyWrappingRule : .findChildByType(TYPE_REFERENCE) ?.let { typeReference -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(typeReference) > maxLineLength) { - requireNewlineBeforeLeaf(typeReference, autoCorrect, emit) + requireNewlineBeforeLeaf(typeReference, emit) return } } @@ -114,7 +115,7 @@ public class PropertyWrappingRule : .findChildByType(EQ) ?.let { equal -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(equal) > maxLineLength) { - requireNewlineAfterLeaf(equal, autoCorrect, emit) + requireNewlineAfterLeaf(equal, emit) return } } @@ -123,7 +124,7 @@ public class PropertyWrappingRule : .findChildByType(CALL_EXPRESSION) ?.let { callExpression -> if (baseIndentLength + fromNode.sumOfTextLengthUntil(callExpression) > maxLineLength) { - requireNewlineBeforeLeaf(callExpression, autoCorrect, emit) + requireNewlineBeforeLeaf(callExpression, emit) return } } @@ -138,35 +139,36 @@ public class PropertyWrappingRule : private fun requireNewlineBeforeLeaf( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit( node.startOffset - 1, """Missing newline before "${node.text}"""", true, - ) - LOGGER.trace { "$line: " + ((if (!autoCorrect) "would have " else "") + "inserted newline before ${node.text}") } - if (autoCorrect) { + ).also { autocorrectDecision -> + LOGGER.trace { + "$line: " + (if (autocorrectDecision == NO_AUTOCORRECT) "would have " else "") + "inserted newline before ${node.text}" + } + }.ifAutocorrectAllowed { node.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(node)) } } private fun requireNewlineAfterLeaf( nodeAfterWhichNewlineIsRequired: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, nodeToFix: ASTNode = nodeAfterWhichNewlineIsRequired, ) { emit( nodeAfterWhichNewlineIsRequired.startOffset + 1, """Missing newline after "${nodeAfterWhichNewlineIsRequired.text}"""", true, - ) - LOGGER.trace { - "$line: " + (if (!autoCorrect) "would have " else "") + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" - } - if (autoCorrect) { + ).also { autocorrectDecision -> + LOGGER.trace { + "$line: " + (if (autocorrectDecision == NO_AUTOCORRECT) "would have " else "") + + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" + } + }.ifAutocorrectAllowed { nodeToFix.upsertWhitespaceAfterMe(indentConfig.childIndentOf(nodeToFix)) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRule.kt index d016ea680d..e49113f200 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_ARGUMENT_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_PARAMETER_LIST @@ -9,6 +10,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithoutNewline import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -20,8 +22,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement public class SpacingAroundAngleBracketsRule : StandardRule("spacing-around-angle-brackets") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType != TYPE_PARAMETER_LIST && node.elementType != TYPE_ARGUMENT_LIST) { return @@ -35,9 +36,9 @@ public class SpacingAroundAngleBracketsRule : StandardRule("spacing-around-angle // Ignore when the whitespace is preceded by certain keywords, e.g. fun func(arg: T) {} if (!ELEMENT_TYPES_ALLOWING_PRECEDING_WHITESPACE.contains(beforeLeftAngle.prevLeaf()?.elementType)) { emit(beforeLeftAngle.startOffset, "Unexpected spacing before \"<\"", true) - if (autoCorrect) { - beforeLeftAngle.treeParent.removeChild(beforeLeftAngle) - } + .ifAutocorrectAllowed { + beforeLeftAngle.treeParent.removeChild(beforeLeftAngle) + } } } @@ -45,11 +46,11 @@ public class SpacingAroundAngleBracketsRule : StandardRule("spacing-around-angle val afterLeftAngle = openingBracket.nextLeaf() if (afterLeftAngle?.elementType == WHITE_SPACE) { if (afterLeftAngle.isWhiteSpaceWithoutNewline()) { - // when spacing does not include any new lines, e.g. Map< String, Int> emit(afterLeftAngle.startOffset, "Unexpected spacing after \"<\"", true) - if (autoCorrect) { - afterLeftAngle.treeParent.removeChild(afterLeftAngle) - } + .ifAutocorrectAllowed { + // when spacing does not include any new lines, e.g. Map< String, Int> + afterLeftAngle.treeParent.removeChild(afterLeftAngle) + } } else { // when spacing contains at least one new line, e.g. // SomeGenericType<[whitespace] @@ -59,8 +60,11 @@ public class SpacingAroundAngleBracketsRule : StandardRule("spacing-around-angle // SomeGenericType< // String, Int, String> val newLineWithIndent = afterLeftAngle.text.trimBeforeLastLine() - if (autoCorrect) { - (afterLeftAngle as LeafElement).rawReplaceWithText(newLineWithIndent) + if (newLineWithIndent != afterLeftAngle.text) { + emit(afterLeftAngle.startOffset, "Single newline expected after \"<\"", true) + .ifAutocorrectAllowed { + (afterLeftAngle as LeafElement).rawReplaceWithText(newLineWithIndent) + } } } } @@ -72,11 +76,11 @@ public class SpacingAroundAngleBracketsRule : StandardRule("spacing-around-angle // Check for rogue spacing before a closing bracket if (beforeRightAngle?.elementType == WHITE_SPACE) { if (beforeRightAngle.isWhiteSpaceWithoutNewline()) { - // when spacing does not include any new lines, e.g. Map emit(beforeRightAngle.startOffset, "Unexpected spacing before \">\"", true) - if (autoCorrect) { - beforeRightAngle.treeParent.removeChild(beforeRightAngle) - } + .ifAutocorrectAllowed { + // when spacing does not include any new lines, e.g. Map + beforeRightAngle.treeParent.removeChild(beforeRightAngle) + } } else { // when spacing contains at least one new line, e.g. // SomeGenericType val newLineWithIndent = beforeRightAngle.text.trimBeforeLastLine() - if (autoCorrect) { - (beforeRightAngle as LeafElement).rawReplaceWithText(newLineWithIndent) + if (newLineWithIndent != beforeRightAngle.text) { + emit(beforeRightAngle.startOffset, "Single newline expected before \">\"", true) + .ifAutocorrectAllowed { + (beforeRightAngle as LeafElement).rawReplaceWithText(newLineWithIndent) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt index 5dd34b8650..c79581ec9e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -34,157 +36,153 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull public class SpacingAroundColonRule : StandardRule("colon-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == COLON) { - removeUnexpectedNewlineBefore(node, emit, autoCorrect) - removeUnexpectedSpacingAround(node, emit, autoCorrect) - addMissingSpacingAround(node, emit, autoCorrect) + removeUnexpectedNewlineBefore(node, emit) + removeUnexpectedSpacingAround(node, emit) + addMissingSpacingAround(node, emit) } } private fun removeUnexpectedNewlineBefore( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val psiParent = node.psi.parent val prevLeaf = node.prevLeaf() if (prevLeaf != null && prevLeaf.isWhiteSpaceWithNewline()) { emit(prevLeaf.startOffset, "Unexpected newline before \":\"", true) - if (autoCorrect) { - val prevNonCodeElements = - node - .siblings(forward = false) - .takeWhile { it.isWhiteSpace() || it.isPartOfComment() } - .toList() - .reversed() - when { - psiParent is KtProperty || psiParent is KtNamedFunction -> { - val equalsSignElement = - node - .siblings(forward = true) - .firstOrNull { it.elementType == EQ } - if (equalsSignElement != null) { - equalsSignElement - .treeNext - ?.let { treeNext -> - prevNonCodeElements.forEach { - node.treeParent.addChild(it, treeNext) - } - if (treeNext.isWhiteSpace()) { - equalsSignElement.treeParent.removeChild(treeNext) - } - Unit - } - } - val blockElement = - node - .siblings(forward = true) - .firstIsInstanceOrNull() - if (blockElement != null) { - val before = - blockElement - .firstChildNode - .nextSibling() - prevNonCodeElements - .let { - if (it.first().isWhiteSpace()) { - blockElement.treeParent.removeChild(it.first()) - it.drop(1) + .ifAutocorrectAllowed { + val prevNonCodeElements = + node + .siblings(forward = false) + .takeWhile { it.isWhiteSpace() || it.isPartOfComment() } + .toList() + .reversed() + when { + psiParent is KtProperty || psiParent is KtNamedFunction -> { + val equalsSignElement = + node + .siblings(forward = true) + .firstOrNull { it.elementType == EQ } + if (equalsSignElement != null) { + equalsSignElement + .treeNext + ?.let { treeNext -> + prevNonCodeElements.forEach { + node.treeParent.addChild(it, treeNext) + } + if (treeNext.isWhiteSpace()) { + equalsSignElement.treeParent.removeChild(treeNext) + } + Unit } - if (it.last().isWhiteSpaceWithNewline()) { - blockElement.treeParent.removeChild(it.last()) - it.dropLast(1) - } else { - it + } + val blockElement = + node + .siblings(forward = true) + .firstIsInstanceOrNull() + if (blockElement != null) { + val before = + blockElement + .firstChildNode + .nextSibling() + prevNonCodeElements + .let { + if (it.first().isWhiteSpace()) { + blockElement.treeParent.removeChild(it.first()) + it.drop(1) + } + if (it.last().isWhiteSpaceWithNewline()) { + blockElement.treeParent.removeChild(it.last()) + it.dropLast(1) + } else { + it + } + }.forEach { + blockElement.addChild(it, before) } - }.forEach { - blockElement.addChild(it, before) - } + } } - } - prevLeaf.prevLeaf()?.isPartOfComment() == true -> { - val nextLeaf = node.nextLeaf() - prevNonCodeElements.forEach { - node.treeParent.addChild(it, nextLeaf) - } - if (nextLeaf != null && nextLeaf.isWhiteSpace()) { - node.treeParent.removeChild(nextLeaf) + prevLeaf.prevLeaf()?.isPartOfComment() == true -> { + val nextLeaf = node.nextLeaf() + prevNonCodeElements.forEach { + node.treeParent.addChild(it, nextLeaf) + } + if (nextLeaf != null && nextLeaf.isWhiteSpace()) { + node.treeParent.removeChild(nextLeaf) + } } - } - else -> { - val text = prevLeaf.text - if (node.spacingBefore) { - (prevLeaf as LeafPsiElement).rawReplaceWithText(" ") - } else { - prevLeaf.treeParent.removeChild(prevLeaf) + else -> { + val text = prevLeaf.text + if (node.spacingBefore) { + (prevLeaf as LeafPsiElement).rawReplaceWithText(" ") + } else { + prevLeaf.treeParent.removeChild(prevLeaf) + } + node.upsertWhitespaceAfterMe(text) } - node.upsertWhitespaceAfterMe(text) } } - } } } private fun removeUnexpectedSpacingAround( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.prevSibling().isWhiteSpaceWithoutNewline() && node.noSpacingBefore) { emit(node.startOffset, "Unexpected spacing before \":\"", true) - if (autoCorrect) { - node - .prevSibling() - ?.let { prevSibling -> - prevSibling.treeParent.removeChild(prevSibling) - } - } + .ifAutocorrectAllowed { + node + .prevSibling() + ?.let { prevSibling -> + prevSibling.treeParent.removeChild(prevSibling) + } + } } if (node.nextSibling().isWhiteSpaceWithoutNewline() && node.spacingAfter) { emit(node.startOffset, "Unexpected spacing after \":\"", true) - if (autoCorrect) { - node - .nextSibling() - ?.let { nextSibling -> - nextSibling.treeParent.removeChild(nextSibling) - } - } + .ifAutocorrectAllowed { + node + .nextSibling() + ?.let { nextSibling -> + nextSibling.treeParent.removeChild(nextSibling) + } + } } } private fun addMissingSpacingAround( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val missingSpacingBefore = !node.prevSibling().isWhiteSpace() && node.spacingBefore val missingSpacingAfter = !node.nextSibling().isWhiteSpace() && node.noSpacingAfter when { missingSpacingBefore && missingSpacingAfter -> { emit(node.startOffset, "Missing spacing around \":\"", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + node.upsertWhitespaceAfterMe(" ") + } } missingSpacingBefore -> { emit(node.startOffset, "Missing spacing before \":\"", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + } } missingSpacingAfter -> { emit(node.startOffset + 1, "Missing spacing after \":\"", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCommaRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCommaRule.kt index efd6a89cc1..a3c59d55df 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCommaRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCommaRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.GT import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACKET import com.pinterest.ktlint.rule.engine.core.api.ElementType.RPAR 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfString import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.nextLeaf @@ -26,36 +28,35 @@ public class SpacingAroundCommaRule : StandardRule("comma-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node is LeafPsiElement && node.textMatches(",") && !node.isPartOfString()) { val prevLeaf = node.prevLeaf() if (prevLeaf is PsiWhiteSpace) { emit(prevLeaf.startOffset, "Unexpected spacing before \"${node.text}\"", true) - if (autoCorrect) { - val isPrecededByComment = prevLeaf.prevLeaf { it !is PsiWhiteSpace } is PsiComment - if (isPrecededByComment && prevLeaf.isWhiteSpaceWithNewline()) { - // If comma is on new line and preceded by a comment, it should be moved before this comment - // https://github.com/pinterest/ktlint/issues/367 - val previousStatement = node.prevCodeLeaf()!! - previousStatement.treeParent.addChild(node.clone(), previousStatement.nextSibling()) - val nextLeaf = node.nextLeaf() - if (nextLeaf is PsiWhiteSpace) { - nextLeaf.treeParent.removeChild(nextLeaf) + .ifAutocorrectAllowed { + val isPrecededByComment = prevLeaf.prevLeaf { it !is PsiWhiteSpace } is PsiComment + if (isPrecededByComment && prevLeaf.isWhiteSpaceWithNewline()) { + // If comma is on new line and preceded by a comment, it should be moved before this comment + // https://github.com/pinterest/ktlint/issues/367 + val previousStatement = node.prevCodeLeaf()!! + previousStatement.treeParent.addChild(node.clone(), previousStatement.nextSibling()) + val nextLeaf = node.nextLeaf() + if (nextLeaf is PsiWhiteSpace) { + nextLeaf.treeParent.removeChild(nextLeaf) + } + node.treeParent.removeChild(node) + } else { + prevLeaf.treeParent.removeChild(prevLeaf) } - node.treeParent.removeChild(node) - } else { - prevLeaf.treeParent.removeChild(prevLeaf) } - } } val nextLeaf = node.nextLeaf() if (nextLeaf !is PsiWhiteSpace && nextLeaf?.elementType !in rTokenSet) { emit(node.startOffset + 1, "Missing spacing after \"${node.text}\"", true) - if (autoCorrect) { - (node as ASTNode).upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + (node as ASTNode).upsertWhitespaceAfterMe(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCurlyRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCurlyRule.kt index 5a9e620dc3..05131d4eb6 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCurlyRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundCurlyRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.AT import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLONCOLON @@ -26,6 +27,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERT 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isLeaf import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isPartOfString @@ -72,98 +74,103 @@ public class SpacingAroundCurlyRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isLeaf() && !node.isPartOfString()) { val prevLeaf = node.prevLeaf() val nextLeaf = node.nextLeaf() val spacingBefore: Boolean val spacingAfter: Boolean - if (node.elementType == LBRACE) { - spacingBefore = - prevLeaf is PsiWhiteSpace || - prevLeaf?.elementType == AT || - ( - prevLeaf?.elementType == LPAR && - ((node as LeafPsiElement).parent is KtLambdaExpression || node.parent.parent is KtLambdaExpression) - ) - spacingAfter = nextLeaf is PsiWhiteSpace || nextLeaf?.elementType == RBRACE - if (prevLeaf is PsiWhiteSpace && - !prevLeaf.textContains('\n') && - prevLeaf.prevLeaf()?.let { - it.elementType == LPAR || it.elementType == AT - } == true - ) { - emit(node.startOffset, "Unexpected space before \"${node.text}\"", true) - if (autoCorrect) { - prevLeaf.node.treeParent.removeChild(prevLeaf.node) + when (node.elementType) { + LBRACE -> { + spacingBefore = + prevLeaf is PsiWhiteSpace || + prevLeaf?.elementType == AT || + ( + prevLeaf?.elementType == LPAR && + ((node as LeafPsiElement).parent is KtLambdaExpression || node.parent.parent is KtLambdaExpression) + ) + spacingAfter = nextLeaf is PsiWhiteSpace || nextLeaf?.elementType == RBRACE + if (prevLeaf is PsiWhiteSpace && + !prevLeaf.textContains('\n') && + prevLeaf.prevLeaf()?.let { + it.elementType == LPAR || it.elementType == AT + } == true + ) { + emit(node.startOffset, "Unexpected space before \"${node.text}\"", true) + .ifAutocorrectAllowed { + prevLeaf.node.treeParent.removeChild(prevLeaf.node) + } } - } - prevLeaf - ?.takeIf { it.isWhiteSpaceWithNewline() } - ?.takeIf { - prevLeaf.prevLeaf()?.let { it.elementType == RPAR || KtTokens.KEYWORDS.contains(it.elementType) } == true || - node.treeParent.elementType == CLASS_BODY || - // allow newline for lambda return type - (prevLeaf.treeParent.elementType == FUN && prevLeaf.treeNext.elementType != LAMBDA_EXPRESSION) - }?.run { - emit(node.startOffset, "Unexpected newline before \"${node.text}\"", true) - if (autoCorrect) { - if (isPrecededByEolComment()) { - // All consecutive whitespaces and comments preceding the curly have to be moved after the curly brace - leavesIncludingSelf(forward = false) - .takeWhile { it.isWhiteSpace() || it.isPartOfComment() } - .toList() - .reversed() - .takeIf { it.isNotEmpty() } - ?.let { leavesToMoveAfterCurly -> - node.treeParent.addChildren( - leavesToMoveAfterCurly.first(), - leavesToMoveAfterCurly.last(), - node.treeNext, - ) + prevLeaf + ?.takeIf { it.isWhiteSpaceWithNewline() } + ?.takeIf { + prevLeaf.prevLeaf()?.let { it.elementType == RPAR || KtTokens.KEYWORDS.contains(it.elementType) } == true || + node.treeParent.elementType == CLASS_BODY || + // allow newline for lambda return type + (prevLeaf.treeParent.elementType == FUN && prevLeaf.treeNext.elementType != LAMBDA_EXPRESSION) + }?.run { + emit(node.startOffset, "Unexpected newline before \"${node.text}\"", true) + .ifAutocorrectAllowed { + if (isPrecededByEolComment()) { + // All consecutive whitespaces and comments preceding the curly have to be moved after the curly brace + leavesIncludingSelf(forward = false) + .takeWhile { it.isWhiteSpace() || it.isPartOfComment() } + .toList() + .reversed() + .takeIf { it.isNotEmpty() } + ?.let { leavesToMoveAfterCurly -> + node.treeParent.addChildren( + leavesToMoveAfterCurly.first(), + leavesToMoveAfterCurly.last(), + node.treeNext, + ) + } } - } - (this as LeafPsiElement).rawReplaceWithText(" ") + (this as LeafPsiElement).rawReplaceWithText(" ") + } } - } - } else if (node.elementType == RBRACE) { - spacingBefore = prevLeaf is PsiWhiteSpace || prevLeaf?.elementType == LBRACE - spacingAfter = nextLeaf == null || nextLeaf is PsiWhiteSpace || shouldNotToBeSeparatedBySpace(nextLeaf) - nextLeaf - .takeIf { it.isWhiteSpaceWithoutNewline() } - ?.takeIf { shouldNotToBeSeparatedBySpace(it.nextLeaf()) } - ?.let { leaf -> - emit(node.startOffset, "Unexpected space after \"${node.text}\"", true) - if (autoCorrect) { - leaf.treeParent.removeChild(leaf) + } + + RBRACE -> { + spacingBefore = prevLeaf is PsiWhiteSpace || prevLeaf?.elementType == LBRACE + spacingAfter = nextLeaf == null || nextLeaf is PsiWhiteSpace || shouldNotToBeSeparatedBySpace(nextLeaf) + nextLeaf + .takeIf { it.isWhiteSpaceWithoutNewline() } + ?.takeIf { shouldNotToBeSeparatedBySpace(it.nextLeaf()) } + ?.let { leaf -> + emit(node.startOffset, "Unexpected space after \"${node.text}\"", true) + .ifAutocorrectAllowed { + leaf.treeParent.removeChild(leaf) + } } - } - } else { - return + } + + else -> { + return + } } when { !spacingBefore && !spacingAfter -> { emit(node.startOffset, "Missing spacing around \"${node.text}\"", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + node.upsertWhitespaceAfterMe(" ") + } } !spacingBefore -> { emit(node.startOffset, "Missing spacing before \"${node.text}\"", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + } } !spacingAfter -> { emit(node.startOffset + 1, "Missing spacing after \"${node.text}\"", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDotRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDotRule.kt index ac2d3f15b3..33a7278713 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDotRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDotRule.kt @@ -1,8 +1,10 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isPartOfString import com.pinterest.ktlint.rule.engine.core.api.nextLeaf @@ -16,23 +18,22 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class SpacingAroundDotRule : StandardRule("dot-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node is LeafPsiElement && node.textMatches(".") && !node.isPartOfString() && !node.isPartOfComment()) { val prevLeaf = node.prevLeaf() if (prevLeaf is PsiWhiteSpace && !prevLeaf.textContains('\n')) { emit(prevLeaf.startOffset, "Unexpected spacing before \"${node.text}\"", true) - if (autoCorrect) { - prevLeaf.node.treeParent.removeChild(prevLeaf.node) - } + .ifAutocorrectAllowed { + prevLeaf.node.treeParent.removeChild(prevLeaf.node) + } } val nextLeaf = node.nextLeaf() if (nextLeaf is PsiWhiteSpace) { emit(nextLeaf.startOffset, "Unexpected spacing after \"${node.text}\"", true) - if (autoCorrect) { - nextLeaf.node.treeParent.removeChild(nextLeaf.node) - } + .ifAutocorrectAllowed { + nextLeaf.node.treeParent.removeChild(nextLeaf.node) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt index 1462c3d322..a1c4d6dfe7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALLABLE_REFERENCE_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_LITERAL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLONCOLON 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -18,8 +20,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class SpacingAroundDoubleColonRule : StandardRule("double-colon-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == COLONCOLON) { val prevLeaf = node.prevLeaf() @@ -45,24 +46,24 @@ public class SpacingAroundDoubleColonRule : StandardRule("double-colon-spacing") when { spacingBefore && spacingAfter -> { emit(node.startOffset, "Unexpected spacing around \"${node.text}\"", true) - if (autoCorrect) { - prevLeaf!!.removeSelf(removeSingleWhiteSpace) - nextLeaf!!.treeParent.removeChild(nextLeaf) - } + .ifAutocorrectAllowed { + prevLeaf!!.removeSelf(removeSingleWhiteSpace) + nextLeaf!!.treeParent.removeChild(nextLeaf) + } } spacingBefore -> { emit(prevLeaf!!.startOffset, "Unexpected spacing before \"${node.text}\"", true) - if (autoCorrect) { - prevLeaf.removeSelf(removeSingleWhiteSpace) - } + .ifAutocorrectAllowed { + prevLeaf.removeSelf(removeSingleWhiteSpace) + } } spacingAfter -> { emit(nextLeaf!!.startOffset, "Unexpected spacing after \"${node.text}\"", true) - if (autoCorrect) { - nextLeaf.treeParent.removeChild(nextLeaf) - } + .ifAutocorrectAllowed { + nextLeaf.treeParent.removeChild(nextLeaf) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundKeywordRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundKeywordRule.kt index fa773504d7..eb6fd39197 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundKeywordRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundKeywordRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CATCH_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.DO_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE_KEYWORD @@ -16,6 +17,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe @@ -50,23 +52,22 @@ public class SpacingAroundKeywordRule : StandardRule("keyword-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node is LeafPsiElement) { if (tokenSet.contains(node.elementType) && node.parent !is KDocName && node.nextLeaf() !is PsiWhiteSpace) { emit(node.startOffset + node.text.length, "Missing spacing after \"${node.text}\"", true) - if (autoCorrect) { - (node as ASTNode).upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + (node as ASTNode).upsertWhitespaceAfterMe(" ") + } } else if (keywordsWithoutSpaces.contains(node.elementType) && node.nextLeaf() is PsiWhiteSpace) { val parent = node.parent val nextLeaf = node.nextLeaf() if (parent is KtPropertyAccessor && parent.hasBody() && nextLeaf != null) { emit(node.startOffset, "Unexpected spacing after \"${node.text}\"", true) - if (autoCorrect) { - nextLeaf.treeParent.removeChild(nextLeaf) - } + .ifAutocorrectAllowed { + nextLeaf.treeParent.removeChild(nextLeaf) + } } } if (noLFBeforeSet.contains(node.elementType)) { @@ -84,9 +85,9 @@ public class SpacingAroundKeywordRule : StandardRule("keyword-spacing") { (!isElseKeyword || parentOfRBrace.treeParent?.treeParent == node.treeParent) ) { emit(node.startOffset, "Unexpected newline before \"${node.text}\"", true) - if (autoCorrect) { - (prevLeaf as LeafElement).rawReplaceWithText(" ") - } + .ifAutocorrectAllowed { + (prevLeaf as LeafElement).rawReplaceWithText(" ") + } } } } 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 a50826b942..c05cb938a6 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 @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANDAND import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW import com.pinterest.ktlint.rule.engine.core.api.ElementType.DIV @@ -29,6 +30,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.parent @@ -47,8 +49,7 @@ import org.jetbrains.kotlin.psi.KtPrefixExpression public class SpacingAroundOperatorsRule : StandardRule("op-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isUnaryOperator()) { // Allow: @@ -86,24 +87,24 @@ public class SpacingAroundOperatorsRule : StandardRule("op-spacing") { when { !spacingBefore && !spacingAfter -> { emit(node.startOffset, "Missing spacing around \"${node.text}\"", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + node.upsertWhitespaceAfterMe(" ") + } } !spacingBefore -> { emit(node.startOffset, "Missing spacing before \"${node.text}\"", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + } } !spacingAfter -> { emit(node.startOffset + node.textLength, "Missing spacing after \"${node.text}\"", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt index b86eac62c9..171007e505 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_TYPE @@ -15,6 +16,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER_LIS 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithoutNewline import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -31,8 +33,7 @@ import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace public class SpacingAroundParensRule : StandardRule("paren-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == LPAR || node.elementType == RPAR) { val prevLeaf = node.prevLeaf() @@ -68,24 +69,24 @@ public class SpacingAroundParensRule : StandardRule("paren-spacing") { when { spacingBefore && spacingAfter -> { emit(node.startOffset, "Unexpected spacing around \"${node.text}\"", true) - if (autoCorrect) { - prevLeaf!!.treeParent.removeChild(prevLeaf) - nextLeaf!!.treeParent.removeChild(nextLeaf) - } + .ifAutocorrectAllowed { + prevLeaf!!.treeParent.removeChild(prevLeaf) + nextLeaf!!.treeParent.removeChild(nextLeaf) + } } spacingBefore -> { emit(prevLeaf!!.startOffset, "Unexpected spacing before \"${node.text}\"", true) - if (autoCorrect) { - prevLeaf.treeParent.removeChild(prevLeaf) - } + .ifAutocorrectAllowed { + prevLeaf.treeParent.removeChild(prevLeaf) + } } spacingAfter -> { emit(node.startOffset + 1, "Unexpected spacing after \"${node.text}\"", true) - if (autoCorrect) { - nextLeaf!!.treeParent.removeChild(nextLeaf) - } + .ifAutocorrectAllowed { + nextLeaf!!.treeParent.removeChild(nextLeaf) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundRangeOperatorRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundRangeOperatorRule.kt index d4293263e5..090e5fc2a7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundRangeOperatorRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundRangeOperatorRule.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.RANGE import com.pinterest.ktlint.rule.engine.core.api.ElementType.RANGE_UNTIL 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -16,8 +18,7 @@ import org.jetbrains.kotlin.lexer.KtSingleValueToken public class SpacingAroundRangeOperatorRule : StandardRule("range-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == RANGE || node.elementType == RANGE_UNTIL) { val prevLeaf = node.prevLeaf() @@ -25,24 +26,24 @@ public class SpacingAroundRangeOperatorRule : StandardRule("range-spacing") { when { prevLeaf is PsiWhiteSpace && nextLeaf is PsiWhiteSpace -> { emit(node.startOffset, "Unexpected spacing around \"${node.elementTypeDescription()}\"", true) - if (autoCorrect) { - prevLeaf.node.treeParent.removeChild(prevLeaf.node) - nextLeaf.node.treeParent.removeChild(nextLeaf.node) - } + .ifAutocorrectAllowed { + prevLeaf.node.treeParent.removeChild(prevLeaf.node) + nextLeaf.node.treeParent.removeChild(nextLeaf.node) + } } prevLeaf is PsiWhiteSpace -> { emit(prevLeaf.node.startOffset, "Unexpected spacing before \"${node.elementTypeDescription()}\"", true) - if (autoCorrect) { - prevLeaf.node.treeParent.removeChild(prevLeaf.node) - } + .ifAutocorrectAllowed { + prevLeaf.node.treeParent.removeChild(prevLeaf.node) + } } nextLeaf is PsiWhiteSpace -> { emit(nextLeaf.node.startOffset, "Unexpected spacing after \"${node.elementTypeDescription()}\"", true) - if (autoCorrect) { - nextLeaf.node.treeParent.removeChild(nextLeaf.node) - } + .ifAutocorrectAllowed { + nextLeaf.node.treeParent.removeChild(nextLeaf.node) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundSquareBracketsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundSquareBracketsRule.kt index c7d0dea9f7..28f536d187 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundSquareBracketsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundSquareBracketsRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLLECTION_LITERAL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_MARKDOWN_LINK import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACKET @@ -8,6 +9,7 @@ import com.pinterest.ktlint.rule.engine.core.api.Rule 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithoutNewline import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -25,8 +27,7 @@ public class SpacingAroundSquareBracketsRule : Rule.Experimental { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == LBRACKET || node.elementType == RBRACKET) { val prevLeaf = node.prevLeaf() @@ -76,24 +77,24 @@ public class SpacingAroundSquareBracketsRule : when { spacingBefore && spacingAfter -> { emit(node.startOffset, "Unexpected spacing around '${node.text}'", true) - if (autoCorrect) { - prevLeaf!!.treeParent.removeChild(prevLeaf) - nextLeaf!!.treeParent.removeChild(nextLeaf) - } + .ifAutocorrectAllowed { + prevLeaf!!.treeParent.removeChild(prevLeaf) + nextLeaf!!.treeParent.removeChild(nextLeaf) + } } spacingBefore -> { emit(prevLeaf!!.startOffset, "Unexpected spacing before '${node.text}'", true) - if (autoCorrect) { - prevLeaf.treeParent.removeChild(prevLeaf) - } + .ifAutocorrectAllowed { + prevLeaf.treeParent.removeChild(prevLeaf) + } } spacingAfter -> { emit(node.startOffset + 1, "Unexpected spacing after '${node.text}'", true) - if (autoCorrect) { - nextLeaf!!.treeParent.removeChild(nextLeaf) - } + .ifAutocorrectAllowed { + nextLeaf!!.treeParent.removeChild(nextLeaf) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundUnaryOperatorRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundUnaryOperatorRule.kt index a8fa5f048d..2d3972042e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundUnaryOperatorRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundUnaryOperatorRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType 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.SinceKtlint.Status.STABLE import com.pinterest.ktlint.rule.engine.core.api.children +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -21,8 +23,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class SpacingAroundUnaryOperatorRule : StandardRule("unary-op-spacing") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == ElementType.PREFIX_EXPRESSION || node.elementType == ElementType.POSTFIX_EXPRESSION @@ -39,10 +40,9 @@ public class SpacingAroundUnaryOperatorRule : StandardRule("unary-op-spacing") { .firstOrNull { it.isWhiteSpace() } ?: return emit(whiteSpace.startOffset, "Unexpected spacing in ${node.text.replace("\n", "\\n")}", true) - - if (autoCorrect) { - node.removeChild(whiteSpace) - } + .ifAutocorrectAllowed { + node.removeChild(whiteSpace) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt index 703a0b131e..bb0e59c8c7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST 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.SinceKtlint.Status.STABLE import com.pinterest.ktlint.rule.engine.core.api.children +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -28,18 +30,16 @@ import org.jetbrains.kotlin.psi.psiUtil.leaves public class SpacingBetweenDeclarationsWithAnnotationsRule : StandardRule("spacing-between-declarations-with-annotations") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.psi is KtDeclaration && node.isAnnotated()) { - visitDeclaration(node, emit, autoCorrect) + visitDeclaration(node, emit) } } private fun visitDeclaration( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .psi @@ -52,8 +52,7 @@ public class SpacingBetweenDeclarationsWithAnnotationsRule : StandardRule("spaci prevLeaf.startOffset + 1, "Declarations and declarations with annotations should have an empty space between.", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { prevLeaf.upsertWhitespaceBeforeMe("\n".plus(node.indent())) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt index 960c21c7b3..8663d50e5e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.prevSibling import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -25,8 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset public class SpacingBetweenDeclarationsWithCommentsRule : StandardRule("spacing-between-declarations-with-comments") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node is PsiComment) { val declaration = node.parent as? KtDeclaration ?: return @@ -43,8 +44,7 @@ public class SpacingBetweenDeclarationsWithCommentsRule : StandardRule("spacing- node.startOffset, "Declarations and declarations with comments should have an empty space between.", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { val indent = node.prevLeaf()?.text?.trim('\n') ?: "" (declaration.prevSibling.node as LeafPsiElement).rawReplaceWithText("\n\n$indent") } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRule.kt index cd038ba79f..e4b7c37df7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenFunctionNameAndOpeningParenthesisRule.kt @@ -1,11 +1,13 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE 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.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -15,8 +17,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class SpacingBetweenFunctionNameAndOpeningParenthesisRule : StandardRule("spacing-between-function-name-and-opening-parenthesis") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { node.elementType == ElementType.FUN } @@ -25,9 +26,9 @@ public class SpacingBetweenFunctionNameAndOpeningParenthesisRule : StandardRule( ?.takeIf { it.elementType == WHITE_SPACE } ?.let { whiteSpace -> emit(whiteSpace.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - whiteSpace.treeParent.removeChild(whiteSpace) - } + .ifAutocorrectAllowed { + whiteSpace.treeParent.removeChild(whiteSpace) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt index c96ee71e5d..4c91958844 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW @@ -22,6 +23,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf @@ -59,30 +61,28 @@ public class StatementWrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { BLOCK -> if (node.treeParent.elementType == FUNCTION_LITERAL) { // LBRACE and RBRACE are outside of BLOCK - visitBlock(node.treeParent, emit, autoCorrect) + visitBlock(node.treeParent, emit) } else { - visitBlock(node, emit, autoCorrect) + visitBlock(node, emit) } CLASS_BODY, WHEN -> - visitBlock(node, emit, autoCorrect) + visitBlock(node, emit) SEMICOLON -> - visitSemiColon(node, autoCorrect, emit) + visitSemiColon(node, emit) } } private fun visitBlock( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeUnless { @@ -113,13 +113,13 @@ public class StatementWrappingRule : val nextCodeLeaf = lbraceOrArrow.nextCodeLeaf() if (nextCodeLeaf != null && noNewLineInClosedRange(lbraceOrArrow, nextCodeLeaf)) { emit(nextCodeLeaf.startOffset, "Missing newline after '${lbraceOrArrow.text}'", true) - if (autoCorrect) { - if (node.elementType == WHEN) { - lbraceOrArrow.upsertWhitespaceAfterMe(lbraceOrArrow.indentAsChild) - } else { - lbraceOrArrow.upsertWhitespaceAfterMe(lbraceOrArrow.indentAsSibling) + .ifAutocorrectAllowed { + if (node.elementType == WHEN) { + lbraceOrArrow.upsertWhitespaceAfterMe(lbraceOrArrow.indentAsChild) + } else { + lbraceOrArrow.upsertWhitespaceAfterMe(lbraceOrArrow.indentAsSibling) + } } - } } node @@ -128,9 +128,9 @@ public class StatementWrappingRule : val prevCodeLeaf = rbrace.prevCodeLeaf() if (prevCodeLeaf != null && noNewLineInClosedRange(prevCodeLeaf, rbrace)) { emit(rbrace.startOffset, "Missing newline before '}'", true) - if (autoCorrect) { - rbrace.upsertWhitespaceBeforeMe(rbrace.indentAsParent) - } + .ifAutocorrectAllowed { + rbrace.upsertWhitespaceBeforeMe(rbrace.indentAsParent) + } } } } @@ -193,8 +193,7 @@ public class StatementWrappingRule : private fun visitSemiColon( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val previousCodeLeaf = node.prevCodeLeaf()?.lastChildLeafOrSelf() ?: return val nextCodeLeaf = node.nextCodeLeaf()?.firstChildLeafOrSelf() ?: return @@ -205,9 +204,9 @@ public class StatementWrappingRule : } if (noNewLineInClosedRange(previousCodeLeaf, nextCodeLeaf)) { emit(node.startOffset + 1, """Missing newline after '${node.text}'""", true) - if (autoCorrect) { - node.upsertWhitespaceAfterMe(previousCodeLeaf.indent()) - } + .ifAutocorrectAllowed { + node.upsertWhitespaceAfterMe(previousCodeLeaf.indent()) + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateIndentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateIndentRule.kt index b4d10ee441..0989fdea13 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateIndentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateIndentRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION @@ -26,6 +27,7 @@ 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.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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.nextCodeSibling @@ -89,8 +91,7 @@ public class StringTemplateIndentRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .takeIf { it.elementType == STRING_TEMPLATE } @@ -103,9 +104,9 @@ public class StringTemplateIndentRule : ?.takeUnless { it.isFunctionBodyExpressionOnSameLine() } ?.let { whiteSpace -> emit(stringTemplate.startOffset, "Expected newline before multiline string template", true) - if (autoCorrect) { - whiteSpace.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(whiteSpace.treeParent)) - } + .ifAutocorrectAllowed { + whiteSpace.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(whiteSpace.treeParent)) + } } stringTemplate .getFirstLeafAfterTrimIndent() @@ -116,9 +117,9 @@ public class StringTemplateIndentRule : it.treeParent.elementType == BINARY_EXPRESSION && it.nextSibling()?.elementType == OPERATION_REFERENCE }?.let { nextLeaf -> emit(nextLeaf.startOffset, "Expected newline after multiline string template", true) - if (autoCorrect) { - nextLeaf.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(stringTemplate.treeParent)) - } + .ifAutocorrectAllowed { + nextLeaf.upsertWhitespaceBeforeMe(indentConfig.childIndentOf(stringTemplate.treeParent)) + } } if (stringTemplate.containsMixedIndentationCharacters()) { @@ -134,7 +135,7 @@ public class StringTemplateIndentRule : } val indent = stringTemplate.getIndent() - indentStringTemplate(node, indent, emit, autoCorrect) + indentStringTemplate(node, indent, emit) } } } @@ -219,8 +220,7 @@ public class StringTemplateIndentRule : private fun indentStringTemplate( node: ASTNode, newIndent: String, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // Get the max prefix length that all lines in the multiline string have in common. All whitespace characters are counted as // one single position. Note that the way of counting should be in sync with the way this is done by the trimIndent @@ -242,7 +242,7 @@ public class StringTemplateIndentRule : .map { it.indentLength() } .minOrNull() ?: 0 - checkAndFixNewLineAfterOpeningQuotes(node, newIndent, emit, autoCorrect) + checkAndFixNewLineAfterOpeningQuotes(node, newIndent, emit) node .children() @@ -271,7 +271,6 @@ public class StringTemplateIndentRule : newIndent = expectedIndent, newContent = currentContent, emit = emit, - autoCorrect = autoCorrect, ) } else if (currentIndent != expectedIndent) { checkAndFixIndent( @@ -279,21 +278,19 @@ public class StringTemplateIndentRule : oldIndentLength = currentIndent.length, newIndent = expectedIndent, newContent = currentContent, - autoCorrect = autoCorrect, emit = emit, ) } } } - checkAndFixNewLineBeforeClosingQuotes(node, newIndent, emit, autoCorrect) + checkAndFixNewLineBeforeClosingQuotes(node, newIndent, emit) } private fun checkAndFixNewLineAfterOpeningQuotes( node: ASTNode, indent: String, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val firstNodeAfterOpeningQuotes = node.firstChildNode.nextLeaf() ?: return if (firstNodeAfterOpeningQuotes.text.isNotBlank()) { @@ -301,8 +298,7 @@ public class StringTemplateIndentRule : firstNodeAfterOpeningQuotes.startOffset + firstNodeAfterOpeningQuotes.text.length, "Missing newline after the opening quotes of the raw string literal", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (firstNodeAfterOpeningQuotes as LeafPsiElement).rawInsertBeforeMe( LeafPsiElement(REGULAR_STRING_PART, "\n" + indent), ) @@ -315,15 +311,13 @@ public class StringTemplateIndentRule : oldIndent: String, newIndent: String, newContent: String, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit( node.startOffset + oldIndent.indexOf(wrongIndentChar), "Unexpected '$wrongIndentDescription' character(s) in margin of multiline string", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (node.firstChildNode as LeafPsiElement).rawReplaceWithText( newIndent + newContent, ) @@ -335,28 +329,26 @@ public class StringTemplateIndentRule : oldIndentLength: Int, newIndent: String, newContent: String, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { emit(node.startOffset + oldIndentLength, "Unexpected indent of raw string literal", true) - if (autoCorrect) { - if (node.elementType == CLOSING_QUOTE) { - (node as LeafPsiElement).rawInsertBeforeMe( - LeafPsiElement(REGULAR_STRING_PART, newIndent), - ) - } else { - (node.firstChildLeafOrSelf() as LeafPsiElement).rawReplaceWithText( - newIndent + newContent, - ) + .ifAutocorrectAllowed { + if (node.elementType == CLOSING_QUOTE) { + (node as LeafPsiElement).rawInsertBeforeMe( + LeafPsiElement(REGULAR_STRING_PART, newIndent), + ) + } else { + (node.firstChildLeafOrSelf() as LeafPsiElement).rawReplaceWithText( + newIndent + newContent, + ) + } } - } } private fun checkAndFixNewLineBeforeClosingQuotes( node: ASTNode, indent: String, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val lastNodeBeforeClosingQuotes = node.lastChildNode.prevLeaf() ?: return if (lastNodeBeforeClosingQuotes.text.isNotBlank()) { @@ -364,8 +356,7 @@ public class StringTemplateIndentRule : lastNodeBeforeClosingQuotes.startOffset + lastNodeBeforeClosingQuotes.text.length, "Missing newline before the closing quotes of the raw string literal", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (lastNodeBeforeClosingQuotes as LeafPsiElement).rawInsertAfterMe( LeafPsiElement(REGULAR_STRING_PART, "\n" + indent), ) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateRule.kt index 9f66ecf17c..68f3102e09 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StringTemplateRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLOSING_QUOTE import com.pinterest.ktlint.rule.engine.core.api.ElementType.LITERAL_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.LONG_STRING_TEMPLATE_ENTRY @@ -8,6 +9,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.LONG_TEMPLATE_ENTRY 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.STABLE +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory @@ -28,8 +30,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getChildOfType public class StringTemplateRule : StandardRule("string-template") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val elementType = node.elementType // code below is commented out because (setting aside potentially dangerous replaceChild part) @@ -50,8 +51,7 @@ public class StringTemplateRule : StandardRule("string-template") { entryExpression.operationTokenNode.startOffset, "Redundant \"toString()\" call in string template", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { entryExpression .node .let { entryExpressionNode -> @@ -75,9 +75,9 @@ public class StringTemplateRule : StandardRule("string-template") { } ) { emit(node.treePrev.startOffset + 2, "Redundant curly braces", true) - if (autoCorrect) { - node.removeCurlyBracesIfRedundant() - } + .ifAutocorrectAllowed { + node.removeCurlyBracesIfRedundant() + } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt index 85e8b090f6..40e123e356 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLLECTION_LITERAL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.COMMA @@ -15,6 +16,7 @@ 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.EditorConfigProperty +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isCodeLeaf import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.nextSibling @@ -56,24 +58,22 @@ public class TrailingCommaOnCallSiteRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // Keep processing of element types in sync with Intellij Kotlin formatting settings. // https://github.com/JetBrains/intellij-kotlin/blob/master/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt when (node.elementType) { - COLLECTION_LITERAL_EXPRESSION -> visitCollectionLiteralExpression(node, autoCorrect, emit) - INDICES -> visitIndices(node, autoCorrect, emit) - TYPE_ARGUMENT_LIST -> visitTypeList(node, autoCorrect, emit) - VALUE_ARGUMENT_LIST -> visitValueList(node, autoCorrect, emit) + COLLECTION_LITERAL_EXPRESSION -> visitCollectionLiteralExpression(node, emit) + INDICES -> visitIndices(node, emit) + TYPE_ARGUMENT_LIST -> visitTypeList(node, emit) + VALUE_ARGUMENT_LIST -> visitValueList(node, emit) else -> Unit } } private fun visitCollectionLiteralExpression( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val inspectNode = node @@ -83,7 +83,6 @@ public class TrailingCommaOnCallSiteRule : inspectNode = inspectNode, emit = emit, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, ) } @@ -91,8 +90,7 @@ public class TrailingCommaOnCallSiteRule : private fun visitIndices( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val inspectNode = node @@ -102,14 +100,12 @@ public class TrailingCommaOnCallSiteRule : inspectNode = inspectNode, emit = emit, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, ) } private fun visitValueList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.treeParent.elementType != ElementType.FUNCTION_LITERAL) { node @@ -120,7 +116,6 @@ public class TrailingCommaOnCallSiteRule : inspectNode = inspectNode, emit = emit, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, ) } } @@ -128,8 +123,7 @@ public class TrailingCommaOnCallSiteRule : private fun visitTypeList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val inspectNode = node @@ -139,15 +133,13 @@ public class TrailingCommaOnCallSiteRule : inspectNode = inspectNode, emit = emit, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, ) } private fun ASTNode.reportAndCorrectTrailingCommaNodeBefore( inspectNode: ASTNode, isTrailingCommaAllowed: Boolean, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val prevLeaf = inspectNode.prevLeaf() val trailingCommaNode = prevLeaf?.findPreviousTrailingCommaNodeOrNull() @@ -163,8 +155,7 @@ public class TrailingCommaOnCallSiteRule : trailingCommaNode!!.startOffset, "Unnecessary trailing comma before \"${inspectNode.text}\"", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { this.removeChild(trailingCommaNode) } } @@ -176,8 +167,7 @@ public class TrailingCommaOnCallSiteRule : prevNode.startOffset + prevNode.textLength, "Missing trailing comma before \"${inspectNode.text}\"", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { inspectNode .prevCodeSibling() ?.nextSibling() @@ -192,8 +182,7 @@ public class TrailingCommaOnCallSiteRule : trailingCommaNode!!.startOffset, "Unnecessary trailing comma before \"${inspectNode.text}\"", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { this.removeChild(trailingCommaNode) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt index cf07130557..31ac49a2c2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS @@ -21,6 +22,7 @@ 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.EditorConfigProperty +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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.nextLeaf @@ -80,26 +82,24 @@ public class TrailingCommaOnDeclarationSiteRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // Keep processing of element types in sync with Intellij Kotlin formatting settings. // https://github.com/JetBrains/intellij-kotlin/blob/master/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt when (node.elementType) { - CLASS -> visitClass(node, emit, autoCorrect) - DESTRUCTURING_DECLARATION -> visitDestructuringDeclaration(node, autoCorrect, emit) - FUNCTION_LITERAL -> visitFunctionLiteral(node, autoCorrect, emit) - TYPE_PARAMETER_LIST -> visitTypeList(node, autoCorrect, emit) - VALUE_PARAMETER_LIST -> visitValueList(node, autoCorrect, emit) - WHEN_ENTRY -> visitWhenEntry(node, autoCorrect, emit) + CLASS -> visitClass(node, emit) + DESTRUCTURING_DECLARATION -> visitDestructuringDeclaration(node, emit) + FUNCTION_LITERAL -> visitFunctionLiteral(node, emit) + TYPE_PARAMETER_LIST -> visitTypeList(node, emit) + VALUE_PARAMETER_LIST -> visitValueList(node, emit) + WHEN_ENTRY -> visitWhenEntry(node, emit) else -> Unit } } private fun visitDestructuringDeclaration( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val inspectNode = node @@ -108,7 +108,6 @@ public class TrailingCommaOnDeclarationSiteRule : node.reportAndCorrectTrailingCommaNodeBefore( inspectNode = inspectNode, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, emit = emit, ) } @@ -117,8 +116,7 @@ public class TrailingCommaOnDeclarationSiteRule : private fun visitFunctionLiteral( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val inspectNode = node @@ -129,15 +127,13 @@ public class TrailingCommaOnDeclarationSiteRule : node.reportAndCorrectTrailingCommaNodeBefore( inspectNode = inspectNode, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, emit = emit, ) } private fun visitValueList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.treeParent.elementType != FUNCTION_LITERAL) { node @@ -147,7 +143,6 @@ public class TrailingCommaOnDeclarationSiteRule : node.reportAndCorrectTrailingCommaNodeBefore( inspectNode = inspectNode, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, emit = emit, ) } @@ -156,8 +151,7 @@ public class TrailingCommaOnDeclarationSiteRule : private fun visitTypeList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val inspectNode = node @@ -166,15 +160,13 @@ public class TrailingCommaOnDeclarationSiteRule : node.reportAndCorrectTrailingCommaNodeBefore( inspectNode = inspectNode, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, emit = emit, ) } private fun visitWhenEntry( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val psi = node.psi require(psi is KtWhenEntry) @@ -190,15 +182,13 @@ public class TrailingCommaOnDeclarationSiteRule : node.reportAndCorrectTrailingCommaNodeBefore( inspectNode = inspectNode, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, emit = emit, ) } private fun visitClass( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val psi = node.psi require(psi is KtClass) @@ -216,7 +206,6 @@ public class TrailingCommaOnDeclarationSiteRule : node.reportAndCorrectTrailingCommaNodeBefore( inspectNode = nodeAfterLastEnumEntry, isTrailingCommaAllowed = false, - autoCorrect = autoCorrect, emit = emit, ) } @@ -225,7 +214,6 @@ public class TrailingCommaOnDeclarationSiteRule : node.reportAndCorrectTrailingCommaNodeBefore( inspectNode = nodeAfterLastEnumEntry, isTrailingCommaAllowed = node.isTrailingCommaAllowed(), - autoCorrect = autoCorrect, emit = emit, ) } @@ -262,8 +250,7 @@ public class TrailingCommaOnDeclarationSiteRule : private fun ASTNode.reportAndCorrectTrailingCommaNodeBefore( inspectNode: ASTNode, isTrailingCommaAllowed: Boolean, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val prevLeaf = inspectNode.prevLeaf() val trailingCommaNode = prevLeaf?.findPreviousTrailingCommaNodeOrNull() @@ -286,8 +273,7 @@ public class TrailingCommaOnDeclarationSiteRule : trailingCommaNode!!.startOffset, "Expected a newline between the trailing comma and \"${inspectNode.text}\"", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { lastNodeBeforeArrow.upsertWhitespaceAfterMe(inspectNode.treeParent.indent()) } } @@ -297,8 +283,7 @@ public class TrailingCommaOnDeclarationSiteRule : trailingCommaNode!!.startOffset, "Unnecessary trailing comma before \"${inspectNode.text}\"", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { this.removeChild(trailingCommaNode) } } @@ -323,8 +308,7 @@ public class TrailingCommaOnDeclarationSiteRule : "Missing trailing comma before \"${inspectNode.text}\"", true, ) - } - if (autoCorrect) { + }.ifAutocorrectAllowed { if (addNewLine) { val indent = prevNode @@ -368,8 +352,7 @@ public class TrailingCommaOnDeclarationSiteRule : trailingCommaNode!!.startOffset, "Unnecessary trailing comma before \"${inspectNode.text}\"", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { this.removeChild(trailingCommaNode) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TryCatchFinallySpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TryCatchFinallySpacingRule.kt index 13938a6547..2859e30a5d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TryCatchFinallySpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TryCatchFinallySpacingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.CATCH import com.pinterest.ktlint.rule.engine.core.api.ElementType.FINALLY @@ -16,6 +17,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.prevLeaf @@ -53,8 +55,7 @@ public class TryCatchFinallySpacingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isPartOfComment() && node.treeParent.elementType in TRY_CATCH_FINALLY_TOKEN_SET) { emit(node.startOffset, "No comment expected at this location", false) @@ -62,19 +63,18 @@ public class TryCatchFinallySpacingRule : } when (node.elementType) { BLOCK -> { - visitBlock(node, emit, autoCorrect) + visitBlock(node, emit) } CATCH, FINALLY -> { - visitClause(node, emit, autoCorrect) + visitClause(node, emit) } } } private fun visitBlock( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.treeParent.elementType !in TRY_CATCH_FINALLY_TOKEN_SET) { return @@ -86,9 +86,9 @@ public class TryCatchFinallySpacingRule : val nextSibling = lbrace.nextSibling { !it.isPartOfComment() }!! if (!nextSibling.text.startsWith("\n")) { emit(lbrace.startOffset + 1, "Expected a newline after '{'", true) - if (autoCorrect) { - lbrace.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(node)) - } + .ifAutocorrectAllowed { + lbrace.upsertWhitespaceAfterMe(indentConfig.siblingIndentOf(node)) + } } } @@ -98,24 +98,23 @@ public class TryCatchFinallySpacingRule : val prevSibling = rbrace.prevSibling { !it.isPartOfComment() }!! if (!prevSibling.text.startsWith("\n")) { emit(rbrace.startOffset, "Expected a newline before '}'", true) - if (autoCorrect) { - rbrace.upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(node)) - } + .ifAutocorrectAllowed { + rbrace.upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(node)) + } } } } private fun visitClause( node: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val prevLeaf = node.prevLeaf { !it.isPartOfComment() }!! if (prevLeaf.text != " ") { emit(node.startOffset, "A single space is required before '${node.elementTypeName()}'", true) - if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + node.upsertWhitespaceBeforeMe(" ") + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentCommentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentCommentRule.kt index a6049632c6..3e60ccd8bf 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentCommentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentCommentRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_ARGUMENT_LIST @@ -25,8 +26,7 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet public class TypeArgumentCommentRule : StandardRule("type-argument-comment") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isPartOfComment() && node.treeParent.elementType in typeArgumentTokenSet) { when (node.elementType) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt index 3f9c2dbf57..26059894aa 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.IndentConfig import com.pinterest.ktlint.rule.engine.core.api.RuleId @@ -10,6 +11,7 @@ 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.findCompositeParentElementOfType +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfCompositeElementOfType import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithoutNewline @@ -48,31 +50,29 @@ public class TypeArgumentListSpacingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { ElementType.TYPE_ARGUMENT_LIST -> { - visitFunctionDeclaration(node, autoCorrect, emit) - visitInsideTypeArgumentList(node, autoCorrect, emit) + visitFunctionDeclaration(node, emit) + visitInsideTypeArgumentList(node, emit) } ElementType.SUPER_TYPE_LIST, ElementType.SUPER_EXPRESSION -> - visitInsideTypeArgumentList(node, autoCorrect, emit) + visitInsideTypeArgumentList(node, emit) } } private fun visitFunctionDeclaration( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // No whitespace expected before type argument list of function call // val list = listOf () node .prevLeaf(includeEmpty = true) ?.takeIf { it.elementType == ElementType.WHITE_SPACE } - ?.let { noWhitespaceExpected(it, autoCorrect, emit) } + ?.let { noWhitespaceExpected(it, emit) } // No whitespace expected after type argument list of function call // val list = listOf () @@ -89,13 +89,12 @@ public class TypeArgumentListSpacingRule : }?.lastChildNode ?.nextLeaf(includeEmpty = true) ?.takeIf { it.elementType == ElementType.WHITE_SPACE } - ?.let { noWhitespaceExpected(it, autoCorrect, emit) } + ?.let { noWhitespaceExpected(it, emit) } } private fun visitInsideTypeArgumentList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val multiline = node.textContains('\n') val expectedIndent = @@ -113,9 +112,9 @@ public class TypeArgumentListSpacingRule : if (nextSibling.text != expectedIndent) { if (nextSibling.isWhiteSpaceWithoutNewline()) { emit(nextSibling.startOffset, "Expected newline", true) - if (autoCorrect) { - nextSibling.upsertWhitespaceAfterMe(expectedIndent) - } + .ifAutocorrectAllowed { + nextSibling.upsertWhitespaceAfterMe(expectedIndent) + } } else { // Let Indentation rule fix the indentation } @@ -124,7 +123,7 @@ public class TypeArgumentListSpacingRule : if (nextSibling.isWhiteSpace()) { // Disallow // val list = listOf< String>() - noWhitespaceExpected(nextSibling, autoCorrect, emit) + noWhitespaceExpected(nextSibling, emit) } } } @@ -137,9 +136,9 @@ public class TypeArgumentListSpacingRule : if (prevSibling.text != expectedIndent) { if (prevSibling.isWhiteSpaceWithoutNewline()) { emit(prevSibling.startOffset, "Expected newline", true) - if (autoCorrect) { - prevSibling.upsertWhitespaceBeforeMe(expectedIndent) - } + .ifAutocorrectAllowed { + prevSibling.upsertWhitespaceBeforeMe(expectedIndent) + } } else { // Let Indentation rule fix the indentation } @@ -148,7 +147,7 @@ public class TypeArgumentListSpacingRule : if (prevSibling.isWhiteSpace()) { // Disallow // val list = listOf() - noWhitespaceExpected(prevSibling, autoCorrect, emit) + noWhitespaceExpected(prevSibling, emit) } } } @@ -156,16 +155,14 @@ public class TypeArgumentListSpacingRule : private fun noWhitespaceExpected( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.text != "") { emit( node.startOffset, "No whitespace expected at this position", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { node.treeParent.removeChild(node) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterCommentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterCommentRule.kt index 1a9e7a41bd..e8e4999b6e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterCommentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterCommentRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_PARAMETER @@ -25,8 +26,7 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet public class TypeParameterCommentRule : StandardRule("type-parameter-comment") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isPartOfComment() && node.treeParent.elementType in typeParameterTokenSet) { when (node.elementType) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt index 250182acd2..568724b72d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_KEYWORD @@ -19,6 +20,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithoutNewline import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling @@ -57,32 +59,30 @@ public class TypeParameterListSpacingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType != TYPE_PARAMETER_LIST) { return } when (node.treeParent.elementType) { - CLASS -> visitClassDeclaration(node, autoCorrect, emit) - TYPEALIAS -> visitTypeAliasDeclaration(node, autoCorrect, emit) - FUN -> visitFunctionDeclaration(node, autoCorrect, emit) + CLASS -> visitClassDeclaration(node, emit) + TYPEALIAS -> visitTypeAliasDeclaration(node, emit) + FUN -> visitFunctionDeclaration(node, emit) } - visitInsideTypeParameterList(node, autoCorrect, emit) + visitInsideTypeParameterList(node, emit) } private fun visitClassDeclaration( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // No white space expected between class name and parameter list // class Bar node .prevSibling() ?.takeIf { it.elementType == WHITE_SPACE } - ?.let { visitWhitespace(it, autoCorrect, emit) } + ?.let { visitWhitespace(it, emit) } // No white space expected between parameter type list and the constructor except when followed by compound // constructor @@ -116,14 +116,14 @@ public class TypeParameterListSpacingRule : // class Bar @SomeAnnotation constructor(...) if (whiteSpace.text != " ") { emit(whiteSpace.startOffset, "Expected a single space", true) - if (autoCorrect) { - // If line is to be wrapped this should have been done by other rules before running this rule - whiteSpace.upsertWhitespaceBeforeMe(" ") - } + .ifAutocorrectAllowed { + // If line is to be wrapped this should have been done by other rules before running this rule + whiteSpace.upsertWhitespaceBeforeMe(" ") + } } } } else { - visitWhitespace(whiteSpace, autoCorrect, emit) + visitWhitespace(whiteSpace, emit) } } @@ -132,33 +132,31 @@ public class TypeParameterListSpacingRule : node .nextSibling() ?.takeIf { it.elementType == WHITE_SPACE && it.nextCodeSibling()?.elementType == CLASS_BODY } - ?.let { singleSpaceExpected(it, autoCorrect, emit) } + ?.let { singleSpaceExpected(it, emit) } } private fun visitTypeAliasDeclaration( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // No white space expected between typealias keyword name and parameter list // typealias Bar node .prevSibling() ?.takeIf { it.elementType == WHITE_SPACE } - ?.let { visitWhitespace(it, autoCorrect, emit) } + ?.let { visitWhitespace(it, emit) } // No white space expected between parameter type list and equals sign // typealias Bar = ... node .nextSibling() ?.takeIf { it.elementType == WHITE_SPACE && it.nextCodeSibling()?.elementType == EQ } - ?.let { singleSpaceExpected(it, autoCorrect, emit) } + ?.let { singleSpaceExpected(it, emit) } } private fun visitFunctionDeclaration( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { // Single space expected before type parameter list of function // fun foo(...) @@ -166,9 +164,9 @@ public class TypeParameterListSpacingRule : .prevLeaf(includeEmpty = true) ?.let { prevLeaf -> if (prevLeaf.elementType == WHITE_SPACE) { - singleSpaceExpected(prevLeaf, autoCorrect, emit) + singleSpaceExpected(prevLeaf, emit) } else { - singleSpaceExpected(node.firstChildNode, autoCorrect, emit) + singleSpaceExpected(node.firstChildNode, emit) } } @@ -179,14 +177,13 @@ public class TypeParameterListSpacingRule : .lastChildNode .nextLeaf(includeEmpty = true) ?.let { nextSibling -> - singleSpaceExpected(nextSibling, autoCorrect, emit) + singleSpaceExpected(nextSibling, emit) } } private fun visitInsideTypeParameterList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .findChildByType(LT) @@ -199,7 +196,7 @@ public class TypeParameterListSpacingRule : } else { "" } - visitWhitespace(it, autoCorrect, emit, expectedWhitespace) + visitWhitespace(it, emit, expectedWhitespace) } node @@ -213,14 +210,13 @@ public class TypeParameterListSpacingRule : } else { "" } - visitWhitespace(it, autoCorrect, emit, expectedWhitespace) + visitWhitespace(it, emit, expectedWhitespace) } } private fun visitWhitespace( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, expectedWhitespace: String = "", ) { if (node.text == expectedWhitespace) { @@ -233,8 +229,7 @@ public class TypeParameterListSpacingRule : node.startOffset, "No whitespace expected", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { node.treeParent.removeChild(node) } } @@ -244,8 +239,7 @@ public class TypeParameterListSpacingRule : node.startOffset, "Expected a newline", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (node as LeafPsiElement).rawReplaceWithText(expectedWhitespace) } } @@ -255,8 +249,7 @@ public class TypeParameterListSpacingRule : node.startOffset, "Expected a single space", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (node as LeafPsiElement).rawReplaceWithText(expectedWhitespace) } } @@ -265,8 +258,7 @@ public class TypeParameterListSpacingRule : private fun singleSpaceExpected( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when { node.text == " " -> Unit @@ -276,8 +268,7 @@ public class TypeParameterListSpacingRule : node.startOffset, "Expected a single space instead of newline", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { (node as LeafPsiElement).rawReplaceWithText(" ") } } @@ -287,8 +278,7 @@ public class TypeParameterListSpacingRule : node.startOffset, "Expected a single space", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { node.upsertWhitespaceBeforeMe(" ") } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParenthesesBeforeTrailingLambdaRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParenthesesBeforeTrailingLambdaRule.kt index 2b6c9092a5..0948c3ae7f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParenthesesBeforeTrailingLambdaRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/UnnecessaryParenthesesBeforeTrailingLambdaRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.LAMBDA_ARGUMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.LPAR @@ -10,6 +11,7 @@ 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.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -23,8 +25,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class UnnecessaryParenthesesBeforeTrailingLambdaRule : StandardRule("unnecessary-parentheses-before-trailing-lambda") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isPartOf(CALL_EXPRESSION) && node.isEmptyArgumentList() && @@ -34,8 +35,7 @@ public class UnnecessaryParenthesesBeforeTrailingLambdaRule : StandardRule("unne node.startOffset, "Empty parentheses in function call followed by lambda are unnecessary", true, - ) - if (autoCorrect) { + ).ifAutocorrectAllowed { node.removeChild(node) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentCommentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentCommentRule.kt index 464cbbac3b..ca6f2f0c79 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentCommentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueArgumentCommentRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint @@ -19,8 +20,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class ValueArgumentCommentRule : StandardRule("value-argument-comment") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isPartOfComment() && node.treeParent.elementType == VALUE_ARGUMENT) { // Disallow: diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCommentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCommentRule.kt index 57def81637..828abc2a76 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCommentRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCommentRule.kt @@ -1,5 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER import com.pinterest.ktlint.rule.engine.core.api.RuleId @@ -20,8 +21,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode public class ValueParameterCommentRule : StandardRule("value-parameter-comment") { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.isPartOfComment() && node.treeParent.elementType == VALUE_PARAMETER) { if (node.elementType == KDOC && node.treeParent.firstChildNode == node) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt index f4add90fca..3d4e5e0fb3 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt @@ -1,6 +1,8 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW @@ -55,6 +57,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PR import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY_OFF import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.hasNewLineInClosedRange +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed 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 @@ -128,25 +131,23 @@ public class WrappingRule : override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { - BLOCK -> beforeVisitBlock(node, autoCorrect, emit) - LPAR, LBRACKET -> rearrangeBlock(node, autoCorrect, emit) - SUPER_TYPE_LIST -> rearrangeSuperTypeList(node, autoCorrect, emit) - VALUE_PARAMETER_LIST, VALUE_ARGUMENT_LIST -> rearrangeValueList(node, autoCorrect, emit) - TYPE_ARGUMENT_LIST, TYPE_PARAMETER_LIST -> rearrangeTypeArgumentList(node, autoCorrect, emit) - ARROW -> rearrangeArrow(node, autoCorrect, emit) + BLOCK -> beforeVisitBlock(node, emit) + LPAR, LBRACKET -> rearrangeBlock(node, emit) + SUPER_TYPE_LIST -> rearrangeSuperTypeList(node, emit) + VALUE_PARAMETER_LIST, VALUE_ARGUMENT_LIST -> rearrangeValueList(node, emit) + TYPE_ARGUMENT_LIST, TYPE_PARAMETER_LIST -> rearrangeTypeArgumentList(node, emit) + ARROW -> rearrangeArrow(node, emit) WHITE_SPACE -> line += node.text.count { it == '\n' } - CLOSING_QUOTE -> rearrangeClosingQuote(node, autoCorrect, emit) + CLOSING_QUOTE -> rearrangeClosingQuote(node, emit) } } private fun beforeVisitBlock( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { require(node.elementType == BLOCK) @@ -170,7 +171,7 @@ public class WrappingRule : ?.takeIf { it.elementType == RBRACE } ?.let { rbrace -> if (hasNewLineInClosedRange(lbrace, rbrace)) { - requireNewlineAfterLeaf(lbrace, autoCorrect, emit) + requireNewlineAfterLeaf(lbrace, emit) } } @@ -187,7 +188,7 @@ public class WrappingRule : .takeWhile { !it.isWhiteSpaceWithNewline() } .sumOf { it.textLength } if (lengthUntilBeginOfLine + lengthUntilEndOfLine > maxLineLength) { - requireNewlineAfterLeaf(lbrace, autoCorrect, emit) + requireNewlineAfterLeaf(lbrace, emit) } } } @@ -206,8 +207,7 @@ public class WrappingRule : private fun rearrangeBlock( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val closingElementType = MATCHING_RTOKEN_MAP[node.elementType] var newlineInBetween = false @@ -281,17 +281,16 @@ public class WrappingRule : // } node.treeNext?.elementType != CONDITION ) { - requireNewlineAfterLeaf(node, autoCorrect, emit) + requireNewlineAfterLeaf(node, emit) } if (!closingElement.prevLeaf().isWhiteSpaceWithNewline()) { - requireNewlineBeforeLeaf(closingElement, autoCorrect, emit, indentConfig.parentIndentOf(node)) + requireNewlineBeforeLeaf(closingElement, emit, indentConfig.parentIndentOf(node)) } } private fun rearrangeSuperTypeList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val entries = (node.psi as KtSuperTypeList).entries if ( @@ -321,7 +320,7 @@ public class WrappingRule : !colon.prevLeaf().isWhiteSpaceWithNewline() && colon.prevCodeLeaf().let { it?.elementType != RPAR || !it.prevLeaf().isWhiteSpaceWithNewline() } ) { - requireNewlineAfterLeaf(colon, autoCorrect, emit) + requireNewlineAfterLeaf(colon, emit) } } // put entries on separate lines @@ -332,7 +331,6 @@ public class WrappingRule : ) { requireNewlineAfterLeaf( nodeAfterWhichNewlineIsRequired = c, - autoCorrect = autoCorrect, emit = emit, indent = node.indent(), ) @@ -347,8 +345,7 @@ public class WrappingRule : private fun rearrangeValueList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { for (c in node.children()) { val hasLineBreak = @@ -376,7 +373,7 @@ public class WrappingRule : prevSibling?.elementType == COMMA && !prevSibling.treeNext.isWhiteSpaceWithNewline() ) { - requireNewlineAfterLeaf(prevSibling, autoCorrect, emit) + requireNewlineAfterLeaf(prevSibling, emit) } // insert \n after multi-line value val nextSibling = c.nextSibling { it.elementType != WHITE_SPACE } @@ -391,7 +388,7 @@ public class WrappingRule : // c, d nextSibling.treeNext?.treeNext?.psi !is PsiComment ) { - requireNewlineAfterLeaf(nextSibling, autoCorrect, emit) + requireNewlineAfterLeaf(nextSibling, emit) } } } @@ -403,8 +400,7 @@ public class WrappingRule : private fun rearrangeTypeArgumentList( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.textContains('\n')) { // Each type projection must be preceded with a whitespace containing a newline @@ -417,9 +413,9 @@ public class WrappingRule : .let { prevSibling -> if (prevSibling?.elementType == LT || prevSibling.isWhiteSpaceWithoutNewline()) { emit(typeProjection.startOffset, "A newline was expected before '${typeProjection.text}'", true) - if (autoCorrect) { - typeProjection.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) - } + .ifAutocorrectAllowed { + typeProjection.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) + } } } } @@ -431,9 +427,9 @@ public class WrappingRule : val prevSibling = closingAngle.prevSibling { !it.isPartOfComment() } if (prevSibling?.elementType != WHITE_SPACE || prevSibling.isWhiteSpaceWithoutNewline()) { emit(closingAngle.startOffset, "A newline was expected before '${closingAngle.text}'", true) - if (autoCorrect) { - closingAngle.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) - } + .ifAutocorrectAllowed { + closingAngle.upsertWhitespaceBeforeMe(indentConfig.siblingIndentOf(node)) + } } } } @@ -441,8 +437,7 @@ public class WrappingRule : private fun rearrangeClosingQuote( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node .treeParent @@ -465,12 +460,15 @@ public class WrappingRule : node.startOffset, "Missing newline before \"\"\"", true, - ) - if (autoCorrect) { + ).also { autocorrectDecision -> + LOGGER.trace { + "$line: " + (if (autocorrectDecision == NO_AUTOCORRECT) "would have " else "") + + "inserted newline before (closing) \"\"\"" + } + }.ifAutocorrectAllowed { node as LeafPsiElement node.rawInsertBeforeMe(LeafPsiElement(LITERAL_STRING_TEMPLATE_ENTRY, "\n")) } - LOGGER.trace { "$line: " + (if (!autoCorrect) "would have " else "") + "inserted newline before (closing) \"\"\"" } } } @@ -500,8 +498,7 @@ public class WrappingRule : private fun rearrangeArrow( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { val p = node.treeParent if ( @@ -530,35 +527,35 @@ public class WrappingRule : return } if (!node.nextCodeLeaf()?.prevLeaf().isWhiteSpaceWithNewline()) { - requireNewlineAfterLeaf(node, autoCorrect, emit) + requireNewlineAfterLeaf(node, emit) } val r = node.nextSibling { it.elementType == RBRACE } ?: return if (!r.prevLeaf().isWhiteSpaceWithNewline()) { - requireNewlineBeforeLeaf(r, autoCorrect, emit, node.indent()) + requireNewlineBeforeLeaf(r, emit, node.indent()) } } private fun requireNewlineBeforeLeaf( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, indent: String, ) { emit( node.startOffset - 1, """Missing newline before "${node.text}"""", true, - ) - LOGGER.trace { "$line: " + ((if (!autoCorrect) "would have " else "") + "inserted newline before ${node.text}") } - if (autoCorrect) { + ).also { autocorrectDecision -> + LOGGER.trace { + "$line: " + (if (autocorrectDecision == NO_AUTOCORRECT) "would have " else "") + "inserted newline before ${node.text}" + } + }.ifAutocorrectAllowed { node.upsertWhitespaceBeforeMe(indent) } } private fun requireNewlineAfterLeaf( nodeAfterWhichNewlineIsRequired: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, indent: String? = null, nodeToFix: ASTNode = nodeAfterWhichNewlineIsRequired, ) { @@ -566,11 +563,12 @@ public class WrappingRule : nodeAfterWhichNewlineIsRequired.startOffset + 1, """Missing newline after "${nodeAfterWhichNewlineIsRequired.text}"""", true, - ) - LOGGER.trace { - "$line: " + (if (!autoCorrect) "would have " else "") + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" - } - if (autoCorrect) { + ).also { autocorrectDecision -> + LOGGER.trace { + "$line: " + (if (autocorrectDecision == NO_AUTOCORRECT) "would have " else "") + + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" + } + }.ifAutocorrectAllowed { val tempIndent = indent ?: (indentConfig.childIndentOf(nodeToFix)) nodeToFix.upsertWhitespaceAfterMe(tempIndent) } @@ -645,8 +643,7 @@ public class WrappingRule : override fun afterVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == BLOCK) { val lbrace = @@ -662,7 +659,6 @@ public class WrappingRule : if (hasNewLineInClosedRange(lbrace, rbrace)) { requireNewlineBeforeLeaf( rbrace, - autoCorrect, emit, indentConfig.parentIndentOf(node), ) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt index 395bab814d..240139ffe6 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt @@ -2804,7 +2804,9 @@ internal class IndentationRuleTest { .withEditorConfigOverride(INDENT_STYLE_TAB) .hasLintViolations( LintViolation(2, 1, "Unexpected space character(s)"), + LintViolation(2, 1, "Unexpected indentation (4) (should be 1)"), LintViolation(3, 1, "Unexpected space character(s)"), + LintViolation(3, 1, "Unexpected indentation (2) (should be 0)"), ).isFormattedAs(formattedCode) } @@ -2825,9 +2827,9 @@ internal class IndentationRuleTest { indentationRuleAssertThat(code) .hasLintViolations( LintViolation(2, 1, "Unexpected tab character(s)"), - LintViolation(2, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(2, 1, "Unexpected indentation (2) (should be 4)"), LintViolation(3, 1, "Unexpected tab character(s)"), - LintViolation(3, 1, "Unexpected indentation (4) (should be 0)"), + LintViolation(3, 1, "Unexpected indentation (1) (should be 0)"), ).isFormattedAs(formattedCode) } @@ -2852,8 +2854,11 @@ internal class IndentationRuleTest { indentationRuleAssertThat(code) .hasLintViolations( LintViolation(2, 1, "Unexpected tab character(s)"), + LintViolation(2, 1, "Unexpected indentation (1) (should be 4)"), LintViolation(3, 1, "Unexpected tab character(s)"), + LintViolation(3, 1, "Unexpected indentation (2) (should be 8)"), LintViolation(4, 1, "Unexpected tab character(s)"), + LintViolation(4, 1, "Unexpected indentation (1) (should be 4)"), ).isFormattedAs(formattedCode) } @@ -2879,8 +2884,11 @@ internal class IndentationRuleTest { .withEditorConfigOverride(INDENT_SIZE_PROPERTY to 2) .hasLintViolations( LintViolation(2, 1, "Unexpected tab character(s)"), + LintViolation(2, 1, "Unexpected indentation (1) (should be 2)"), LintViolation(3, 1, "Unexpected tab character(s)"), + LintViolation(3, 1, "Unexpected indentation (2) (should be 4)"), LintViolation(4, 1, "Unexpected tab character(s)"), + LintViolation(4, 1, "Unexpected indentation (1) (should be 2)"), ).isFormattedAs(formattedCode) } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt index 84aa1c3e3c..c998dca9bb 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt @@ -91,6 +91,12 @@ class SpacingAroundAngleBracketsRuleTest { List< String > > > {} + public class Foo8< + + Bar1 : String, + Bar2 : String + + > {} """.trimIndent() val formattedCode = """ @@ -107,6 +113,10 @@ class SpacingAroundAngleBracketsRuleTest { List > > {} + public class Foo8< + Bar1 : String, + Bar2 : String + > {} """.trimIndent() spacingAroundAngleBracketsRuleAssertThat(code) .addAdditionalRuleProvider { IndentationRule() } @@ -120,6 +130,8 @@ class SpacingAroundAngleBracketsRuleTest { LintViolation(8, 19, "Unexpected spacing before \">\""), LintViolation(13, 14, "Unexpected spacing after \"<\""), LintViolation(13, 21, "Unexpected spacing before \">\""), + LintViolation(16, 19, "Single newline expected after \"<\""), + LintViolation(19, 18, "Single newline expected before \">\""), ).isFormattedAs(formattedCode) } diff --git a/ktlint-ruleset-template/src/main/kotlin/yourpkgname/NoVarRule.kt b/ktlint-ruleset-template/src/main/kotlin/yourpkgname/NoVarRule.kt index 55427908db..b1ca7882dd 100644 --- a/ktlint-ruleset-template/src/main/kotlin/yourpkgname/NoVarRule.kt +++ b/ktlint-ruleset-template/src/main/kotlin/yourpkgname/NoVarRule.kt @@ -1,7 +1,9 @@ package yourpkgname +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -14,11 +16,11 @@ public class NoVarRule : repositoryUrl = "https://github.com/your/project/", issueTrackerUrl = "https://github.com/your/project/issues", ), - ) { + ), + RuleAutocorrectApproveHandler { override fun beforeVisitChildNodes( node: ASTNode, - autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { if (node.elementType == ElementType.VAR_KEYWORD) { emit(node.startOffset, "Unexpected var, use val instead", false) diff --git a/ktlint-test/src/main/kotlin/com/pinterest/ktlint/test/KtLintAssertThat.kt b/ktlint-test/src/main/kotlin/com/pinterest/ktlint/test/KtLintAssertThat.kt index 54aa9561bf..533d7f41d1 100644 --- a/ktlint-test/src/main/kotlin/com/pinterest/ktlint/test/KtLintAssertThat.kt +++ b/ktlint-test/src/main/kotlin/com/pinterest/ktlint/test/KtLintAssertThat.kt @@ -9,6 +9,7 @@ import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride.Companion.EMPTY import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride.Companion.plus import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine import com.pinterest.ktlint.rule.engine.api.LintError +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.Rule import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule.Mode.ONLY_WHEN_RUN_AFTER_RULE_IS_LOADED_AND_ENABLED import com.pinterest.ktlint.rule.engine.core.api.RuleId @@ -699,7 +700,12 @@ public class KtLintAssertThatAssertable( private fun format(): Pair> { val lintErrors = mutableSetOf() - val formattedCode = createKtLintRuleEngine().format(code) { lintError, _ -> lintErrors.add(lintError) } + val formattedCode = + createKtLintRuleEngine() + .format(code) { lintError -> + lintErrors.add(lintError) + AutocorrectDecision.ALLOW_AUTOCORRECT + } return Pair(formattedCode, lintErrors) }