An anti-bikeshedding Kotlin linter with built-in formatter
WARNING: The new rules have accidentally not been marked as experimental. This will be fixed in a bugfix!
Break dependency between string-template-indent and multiline-expression-wrapping - #2505, by @paul-dingemans
Allow string template to exceed max line length when it is the only element on a line - #2480, by @paul-dingemans
Add configuration setting for ignoring argument-list-wrapping
above treshold of argument - #2481, by @paul-dingemans
NOTE: In code style ktlint_official
this threshold is unset
so that arguments are always wrapped. If this impacts your code too much, you can make it backward compatible by setting .editorconfig
property ktlint_argument_list_wrapping_ignore_when_parameter_count_greater_or_equal_than
to value 8
. For other code styles this property is initialized with value 8
and as of that backward compatible by default.
Ignore EOL comment that causes max_line_length to be exceeded, except in max-line-length rule - #2516, by @paul-dingemans
Add new rule for disallowing KDoc at non-whitelisted locations - #2548, by @paul-dingemans
Improve insert of suppression - #2546, by @paul-dingemans
Ignore modifier of backing property in android_studio
code style - #2552, by @paul-dingemans
Add rule to check spacing around square brackets 'square-brackets-spacing' - #2555, by @paul-dingemans
Add rule blank-line-between-when-conditions
- #2564, by @paul-dingemans
Prevent IllegalArgumentException in argument-list-wrapping
rule - #2500, by @paul-dingemans
Ignore function which is returned as result in a function body - #2526, by @paul-dingemans
Fix false positive newline expected before comment in enum - #2527, by @paul-dingemans
Report violation when parameter list is preceded by a comment - #2541, by @paul-dingemans
Ignore EOL comments in value-argument-comment
and value-parameter-comment
- #2551, by @paul-dingemans
Do not indent string template starting at first position of line - #2553, by @paul-dingemans
Prevent conflict when curly closing brace is followed by range (until) operator - #2554, by @paul-dingemans
Run argument-list-wrapping after function-signature - #2568, by @paul-dingemans
Ignore simple reference expressions in chain-method-continuation
- #2569, by @paul-dingemans
chore(deps): update plugin org.gradle.toolchains.foojay-resolver-convention to v0.8.0 - #2503, by @renovate[bot]
fix(deps): update dependency io.github.oshai:kotlin-logging-jvm to v6 - #2440, by @renovate[bot]
Replace gradle/gradle-build-action@v3 with gradle/actions/setup-gradle@v3 - #2518, by @paul-dingemans
chore(deps): update plugin kotlinx-binary-compatibiltiy-validator to v0.14.0 - #2522, by @renovate[bot]
chore(deps): update gradle/wrapper-validation-action action to v2 - #2523, by @renovate[bot]
chore(deps): update ffurrer2/extract-release-notes action to v2 - #2515, by @renovate[bot]
chore(deps): update dependency gradle to v8.6 - #2531, by @renovate[bot]
fix(deps): update dependency org.assertj:assertj-core to v3.25.3 - #2536, by @renovate[bot]
fix(deps): update dependency org.junit.jupiter:junit-jupiter to v5.10.2 - #2534, by @renovate[bot]
fix(deps): update dependency org.slf4j:slf4j-simple to v2.0.12 - #2538, by @renovate[bot]
fix(deps): update dependency org.codehaus.janino:janino to v3.1.12 - #2559, by @renovate[bot]
None
Fix incorrect generateEditorConfig example in documentation - #2444, by @stay7
Fix insert of suppression on binary expression - #2463, by @paul-dingemans
Loosen dependency between chain-method-continuation and argument-list-wrapping - #2468, by @paul-dingemans
Keep arrow when both parameter list and block of function literal are empty - #2469, by @paul-dingemans
Improve wrapping of binary expressions - #2479, by @paul-dingemans
Resolve conflict between parameter-list-spacing and parameter-list-wrapping - #2491, by @paul-dingemans
Do not wrap binary expression value argument if it is already preceded by a newline - #2493, by @paul-dingemans
Fix operator spacing - #2473, by @paul-dingemans
Run argument-list-wrapping
, class-signature
and function-signature
when comment rules are disabled - #2466, by @paul-dingemans
fix(deps): update kotlin monorepo to v1.9.22 - #2456, by @renovate[bot]
chore(deps): update actions/setup-python action to v5 - #2417, by @renovate[bot]
fix(deps): update dependency org.slf4j:slf4j-simple to v2.0.10 - #2470, by @renovate[bot]
fix(deps): update dependency dev.drewhamilton.poko:poko-gradle-plugin to v0.15.2 - #2485, by @renovate[bot]
fix(deps): update dependency org.assertj:assertj-core to v3.25.1 - #2486, by @renovate[bot]
Compile with java 21 instead of 20 - #2320, by @paul-dingemans
Improve checking on backing property - #2346, by @paul-dingemans
Add multiline-loop to complement multiline-if-else - #2298, by @hendraanggrian
Add "UnusedImport" as @Suppress alias - #2357, by @paul-dingemans
Allow backing property to be correlated to a public function - #2356, by @paul-dingemans
Add helper function 'fromSnippetWithPath' to create a Code instance - #2359, by @paul-dingemans
Support logging and exception throwing when loading baseline - #2362, by @paul-dingemans
Allow factory methods to use generics, and to overload other factory … - #2366, by @paul-dingemans
Remove dependencies on discouraged-comment-location rule - #2371, by @paul-dingemans
Ignore imports for rangeUntil
in no-unused-imports
rule - #2376, by @paul-dingemans
Ignore imports for assign
in no-unused-imports
rule - #2382, by @paul-dingemans
Ignore invalid function names when importing from "junit.framework" - #2386, by @paul-dingemans
Add experimental rules condition-wrapping
and mixed-condition-operators
- #2401, by @paul-dingemans
Allow property, function and class name to be same as keyword wrapped with backticks - #2405, by @paul-dingemans
Set offset of max-line-length
violation to the last position at which a newline can be inserted to fix the violation - #2419, by @paul-dingemans
Add support for API Consumers to add suppressions - #2428, by @paul-dingemans
Disallow else-if (..) <statement>
as single line construct - #2430, by @paul-dingemans
Allow empty constructor for expected class declaration - #2431, by @paul-dingemans
Disallow comments in try-catch-finally at unexpected locations - #2432, by @paul-dingemans
Make ktlint.bat more environment agnostic - #2421, by @TWiStErRob
Suppress property-naming rule via @Suppress("ConstPropertyName")
- #2442, by @paul-dingemans
Remove obsolete configuration files - #2321, by @paul-dingemans
De-indent the closing angle bracket of the type argument list and type parameter lists in ktlint_official code style - #2302, by @paul-dingemans
docs: Fix artifact url of Maven Central Badge - #2327, by @guicamest
Remove redundant arrow in function literal without parameters / fix documentation - #2365, by @paul-dingemans
Move curly brace before all consecutive comments preceding that curly brace - #2375, by @paul-dingemans
Prevent stack overflow exception when code provided via stdin can not be parsed as Kotlin, nor Kotlin script - #2380, by @paul-dingemans
Fix searching from inside a hidden directory - #2377, by @kitterion
Prevent unwanted joining of KDoc with preceding type-parameter-list - #2381, by @paul-dingemans
Fix false positive violation in annotation
rule - #2400, by @paul-dingemans
Replace all function bodies with body expressions in a single run - #2395, by @paul-dingemans
Fix offset for violation when final newline is missing - #2407, by @paul-dingemans
Fix path to ktlint JAR file in ktlint.bat
- #2408, by @paul-dingemans
Simplify max-line-length
implementation - #2410, by @paul-dingemans
Remove deprecated cli parameters --experimental
, --code-style
, --disabled-rules
- #2411, by @paul-dingemans
Fix adding blank line between declaration and an annotated declaration which is preceded by comment - #2429, by @paul-dingemans
Update CODE_OF_CONDUCT with correct path - #2437, by @OriginalMHV
fix(deps): update dependency org.jetbrains.dokka:dokka-gradle-plugin to v1.9.10 - #2323, by @renovate[bot]
fix(deps): update dependency org.junit.jupiter:junit-jupiter to v5.10.1 - #2342, by @renovate[bot]
fix(deps): update kotlin monorepo to v1.9.21 - #2374, by @renovate[bot]
fix(deps): update dependency org.codehaus.janino:janino to v3.1.11 - #2387, by @renovate[bot]
fix(deps): update dependency dev.drewhamilton.poko:poko-gradle-plugin to v0.15.1 - #2389, by @renovate[bot]
chore(deps): update dependency gradle to v8.5 - #2392, by @renovate[bot]
chore(deps): update actions/checkout digest to b4ffde6 - #2329, by @renovate[bot]
chore(deps): update actions/setup-java action to v4 - #2393, by @renovate[bot]
fix(deps): update dependency ch.qos.logback:logback-classic to v1.3.14 - #2406, by @renovate[bot]
fix(deps): update dependency io.github.hakky54:logcaptor to v2.9.2 - #2409, by @renovate[bot]
fix(deps): update dependency io.github.oshai:kotlin-logging-jvm to v5.1.4 - #2439, by @renovate[bot]
.editorconfig
property ktlint_function_naming_ignore_when_annotated_with
so that rule function-naming
can be ignored based on annotations on that rule. See function-naming.Update badge for Maven Central - #2245, by @Goooler
Fix code style parameter in cli - #2241, by @paul-dingemans
Anonymous function in assignment - #2263, by @paul-dingemans
Fix indent of multiline object declaration inside class - #2266, by @paul-dingemans
Do not replace function body with multiple exit points - #2273, by @paul-dingemans
Ignore override of function in rule function-naming
- #2274, by @paul-dingemans
Suppress function-naming
based on annotations - #2275, by @paul-dingemans
Force blank line before object declaration - #2287, by @paul-dingemans
Multiline expression wrapping - #2290, by @paul-dingemans
Ignore function naming in Kotest classes - #2291, by @paul-dingemans
Improve violation message in discouraged-comment-location
- #2293, by @paul-dingemans
Fix malformed AST when &&
or ||
is at start of line chain-wrapping
- #2300, by @paul-dingemans
Do not report false positives type-argument-list-spacing
and type-parameter-list-spacing
- #2303, by @paul-dingemans
Fix chain method continuation containing higher order function call - #2305, by @paul-dingemans
Update dependency io.github.detekt.sarif4k:sarif4k to v0.5.0 - #2277, by @renovate[bot]
Update dependency gradle to v8.4 - #2294, by @renovate[bot]
Update actions/checkout action to v4 - #2225, by @renovate[bot]
Update actions/checkout digest to 8ade135 - #2295, by @renovate[bot]
Update and align Maven coordinates - #2195, by @paul-dingemans
Be sure to update Maven coordinates below, to get latest changes!
Old Maven coordinates | New Maven coordinates |
---|---|
com.pinterest:ktlint | com.pinterest.ktlint:ktlint-cli |
com.pinterest.ktlint:ktlint-reporter-baseline | com.pinterest.ktlint:ktlint-cli-reporter-baseline |
com.pinterest.ktlint:ktlint-reporter-checkstyle | com.pinterest.ktlint:ktlint-cli-reporter-checkstyle |
com.pinterest.ktlint:ktlint-cli-reporter | com.pinterest.ktlint:ktlint-cli-reporter-core |
com.pinterest.ktlint:ktlint-reporter-format | com.pinterest.ktlint:ktlint-cli-reporter-format |
com.pinterest.ktlint:ktlint-reporter-html | com.pinterest.ktlint:ktlint-cli-reporter-html |
com.pinterest.ktlint:ktlint-reporter-json | com.pinterest.ktlint:ktlint-cli-reporter-json |
com.pinterest.ktlint:ktlint-reporter-plain | com.pinterest.ktlint:ktlint-cli-reporter-plain |
com.pinterest.ktlint:ktlint-reporter-plain-summary | com.pinterest.ktlint:ktlint-cli-reporter-plain-summary |
com.pinterest.ktlint:ktlint-reporter-sarif | com.pinterest.ktlint:ktlint-cli-reporter-sarif |
Add binary compatibility validator - #2131, by @mateuszkwiecinski
Replace kotlin public data class
es with Poko compiler plugin generated ones - #2136, by @mateuszkwiecinski
As a part of public API stabilization, data classes are no longer used in the public API. As of that, functions like copy()
or componentN()
(used for destructuring declarations) are not available anymore.
Promote experimental rules - #2218, by @paul-dingemans
The rules below have been promoted to non-experimental rules:
Fix statement-wrapping and align rule classes - #2178, by @paul-dingemans
Rule class MultilineExpressionWrapping
has been renamed to MultilineExpressionWrappingRule
. Rule class StatementWrapping
has been renamed to StatementWrappingRule
. RULE_ID
constants below are moved to a different Java class at compile time. Each rule provided by Ktlint is to be accompanied by a RULE_ID
constant that can be used in the VisitorModifier.RunAfter
. Filenames did not comply with standard that it should end with Rule
suffix.
RULE ID | Old Java class name | New Java class name |
---|---|---|
FUNCTION_EXPRESSION_BODY_RULE_ID |
FunctionExpressionBodyKt | FunctionExpressionBodyRuleKt |
FUNCTION_LITERAL_RULE_ID |
FunctionLiteralKt | FunctionLiteralRuleKt |
MULTILINE_EXPRESSION_WRAPPING_RULE_ID |
MultilineExpressionWrappingKt | MultilineExpressionWrappingRuleKt |
NO_BLANK_LINE_IN_LIST_RULE_ID |
NoBlankLineInListKt | NoBlankLineInListRuleKt |
NO_EMPTY_FILE_RULE_ID |
(not applicable) | NoEmptyFileRuleKt |
Update to Kotlin 1.9 & remove TreeCopyHandler extension - #2113, by @paul-dingemans
Class org.jetbrains.kotlin.com.intellij.treeCopyHandler
is no longer registered as extension point for the compiler as this is not supported in Kotlin 1.9. Please test your custom rules. In case of unexpected exceptions during formatting of code, see #2044 for possible remediation.
Change default code style to ktlint_official
- #2144, by @paul-dingemans
Add new experimental rule class-signature
- #2119, by @paul-dingemans
Add new experimental rule function-expression-body
- #2151, by @paul-dingemans
Add new experimental rule chain-method-continuation
- #2088, by @atulgpt
Add new experimental rule function-literal
- #2137, by @paul-dingemans
Add new experimental rule function-type-modifier-spacing
rule - #2216, by @t-kameyama
Define EditorConfigOverride
for dynamically loaded ruleset - #2194, by @paul-dingemans
The EditorConfigOverride
parameter of the KtlintRuleEngine
can be defined using the factory method EditorConfigOverride.from(vararg properties: Pair<EditorConfigProperty<*>, *>)
. This requires the EditorConfigProperty
's to be available at compile time. Some common EditorConfigProperty
's are defined in ktlint-rule-engine-core
which is loaded as transitive dependency of ktlint-rule-engine
and as of that are available at compile.
If an EditorConfigProperty
is defined in a Rule
that is only provided via a runtime dependency, it gets a bit more complicated. The ktlint-api-consumer
example has now been updated to show how the EditorConfigProperty
can be retrieved from the Rule
.
Move wrapping on semicolon from wrapping
rule to statement-wrapping
rule - #2222, by @paul-dingemans
Do not indent class body for classes having a long super type list - #2116, by @paul-dingemans
Fix indent of explicit constructor - #2118, by @paul-dingemans
Fix incorrect formatting of nested function literal - #2107, by @paul-dingemans
Add property to disable ktlint for a glob in .editorconfig
- #2108, by @paul-dingemans
Fix spacing around colon in annotations - #2126, by @paul-dingemans
Fix solving problems in 3 consecutive runs - #2132, by @paul-dingemans
Fix indent parenthesized expression - #2127, by @paul-dingemans
Fix indent of IS_EXPRESSION, PREFIX_EXPRESSION and POSTFIX_EXPRESSION - #2125, by @paul-dingemans
Do not wrap a binary expression after an elvis operator - #2134, by @paul-dingemans
Drop obsolete class LintError in ktlint-api-consumer - #2145, by @paul-dingemans
Fix null pointer exception for if-else statement with empty THEN block - #2142, by @paul-dingemans
Fix false positive in property-naming - #2141, by @paul-dingemans
Store relative path of file in baseline file - #2147, by @paul-dingemans
Fix url of build status badge - #2162, by @paul-dingemans
Update CONTRIBUTING.md - #2163, by @oshai
Fix statement-wrapping and align rule classes - #2178, by @paul-dingemans
Fix alignment of type constraints after where
keyword in function - #2180, by @paul-dingemans
Fix wrapping of multiline postfix expression - #2184, by @paul-dingemans
Do not wrap expression after a spread operator - #2193, by @paul-dingemans
Do not remove parenthesis after explicit class constructor without arguments - #2226, by @paul-dingemans
Fix conflict between rules due to annotated super type call - #2227, by @paul-dingemans
Fix indentation of super type list of class in case it is preceded by a comment - #2228, by @paul-dingemans
Super type list starting with an annotation having a parameters - #2230, by @paul-dingemans
Do not wrap values in a single line enum when it is preceded by a comment or an annotation - #2229, by @paul-dingemans
Update dependency org.codehaus.janino:janino to v3.1.10 - #2110, by @renovate[bot]
Update dependency com.google.jimfs:jimfs to v1.3.0 - #2112, by @renovate[bot]
Update dependency org.junit.jupiter:junit-jupiter to v5.10.0 - #2148, by @renovate[bot]
Update dependency io.github.oshai:kotlin-logging-jvm to v5.1.0 - #2174, by @renovate[bot]
Update dependency dev.drewhamilton.poko:poko-gradle-plugin to v0.15.0 - #2173, by @renovate[bot]
Update plugin org.gradle.toolchains.foojay-resolver-convention to v0.7.0 - #2187, by @renovate[bot]
Update dependency gradle to v8.3 - #2186, by @renovate[bot]
Update kotlin monorepo to v1.9.10 - #2197, by @renovate[bot]
Update dependency info.picocli:picocli to v4.7.5 - #2215, by @renovate[bot]
Update dependency org.jetbrains.dokka:dokka-gradle-plugin to v1.9.0 - #2221, by @renovate[bot]
Update dependency org.slf4j:slf4j-simple to v2.0.9 - #2224, by @renovate[bot]
The ktlint-disable
and ktlint-enable
directives are no longer supported. Ktlint rules can now only be suppressed using the @Suppress
or @SuppressWarnings
annotations. A new rule, internal:ktlint-suppression
, is provided to replace the directives with annotations.
API consumers do not need to provide this rule, but it does no harm when done.
The internal:ktlint-suppression
rule can not be disabled via the .editorconfig
nor via @Suppress
or @SuppressWarnings
annotations.
In Kotlin 1.9 the extension points of the embedded kotlin compiler will change. Ktlint only uses the org.jetbrains.kotlin.com.intellij.treeCopyHandler
extension point. This extension is not yet supported in 1.9, neither is it documented (#KT-58704). Without this extension point it might happen that your custom rules will throw exceptions during runtime. See #1981.
In Ktlint, 7 out of 77 rules needed small and sometimes bigger changes to become independent of the extension point org.jetbrains.kotlin.com.intellij.treeCopyHandler
. The impact on your custom rules may vary dependent on the way the autocorrect has been implemented. When manipulating ASTNode
s there seems to be no impact. When, manipulating PsiElement
s, some functions consistently result in a runtime exception.
Based on the refactoring of the rules as provided by ktlint-ruleset-standard
in Ktlint 0.49.x
the suggested refactoring is as follows:
LeafElement.replaceWithText(String)
with LeafElement.rawReplaceWithText(String)
.PsiElement.addAfter(PsiElement, PsiElement)
with AstNode.addChild(AstNode, AstNode)
. Note that this method inserts the new node (first) argument before the second argument node and as of that is not a simple replacement of the PsiElement.addAfter(PsiElement, PsiElement)
.PsiElement.replace(PsiElement)
with a sequence of AstNode.addChild(AstNode, AstNode)
and AstNode.removeChild(AstNode)
.Be aware that your custom rules might use other functions which also throw exceptions when the extension point org.jetbrains.kotlin.com.intellij.treeCopyHandler
is no longer supported.
In order to help you to analyse and fix the problems with your custom rules, ktlint temporarily supports to disable the extension point org.jetbrains.kotlin.com.intellij.treeCopyHandler
using a flag. This flag is available in the Ktlint CLI and in the KtlintRuleEngine
. By default, the extension point is enabled like it was in previous versions of ktlint.
At least you should analyse the problems by running your test suits by running ktlint and disabling the extension point. Next you can start with fixing and releasing the updated rules. All rules in this version of ktlint have already been refactored and are not dependent on the extension point anymore. In Ktlint CLI the flag is to be activated with parameter --disable-kotlin-extension-point
. API Consumers that use the KtlintRuleEngine
directly, have to set property enableKotlinCompilerExtensionPoint
to false
.
At this point in time, it is not yet decided what the next steps will be. Ktlint might drop the support of the extension points entirely. Or, if the extension point org.jetbrains.kotlin.com.intellij.treeCopyHandler
is fully supported at the time that ktlint will be based on kotlin 1.9 it might be kept. In either case, the flag will be dropped in a next version of ktlint.
binary-expression-wrapping
. This rule wraps a binary expression in case the max line length is exceeded (#1940)org.jetbrains.kotlin.com.intellij.treeCopyHandler
to analyse impact on custom rules #1981
no-empty-file
for all code styles. A kotlin (script) file may not be empty (#1074)statement-wrapping
which ensures function, class, or other blocks statement body doesn't start or end at starting or ending braces of the block (#1938)blank-line-before-declaration
. This rule requires a blank line before class, function or property declarations (#1939)wrapping
(#1078)ktlint-suppression
to replace the ktlint-disable
and ktlint-enable
directives with annotations. This rule can not be disabled via the .editorconfig
(#1947)--format
option of KtLint CLI when finding a violation that can be autocorrected (#1071)0.49.x
is removed. Consult changelog of 0.49.x` released for more information. Summary of removed code:property-naming
(#2024)serialVersionUID
in property-naming
(#2045)parameter-list-wrapping
(#1324)else
branch when body contains only chained calls or binary expression (#2057)RuleId
and RuleSetId
classes. Those classes were defined as value classes in 0.49.0
and 0.49.1
. Although the classes were marked with @JvmInline
it seems that it is not possible to uses those classes from Java base API Consumers like Spotless. The classes have now been replaced with data classes (#2041)info.picocli:picocli
to v4.7.4org.junit.jupiter:junit-jupiter
to v5.9.31.8.22
and Kotlin version to 1.8.22
.--code-style=android_studio
in Ktlint CLI identical to deprecated parameter --android
(#1982)no-consecutive-blank-lines
(#1987).editorconfig
property ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than
when using ktlint_official
code style function-signature
(#1977)indent
(#1993)indent
(#1976)ktlint_official
code style to keep formatting of other code styles consistent with 0.48.x
and before indent
(#1971)no-single-line-block-comment
from comment-wrapping
rule. The no-single-line-block-comment
rule is added as experimental rule to the ktlint_official
code style, but it can be enabled explicitly for the other code styles as well. (#1980)Baseline
from ktlint-cli
to ktlint-cli-reporter-baseline
so that Baseline functionality is reusable for API Consumers.WARNING: This version of KtLint contains a number of breaking changes in KtLint CLI and KtLint API. If you are using KtLint with custom ruleset jars or custom reporter jars, then those need to be upgraded before you can use them with this version of ktlint. Please contact the maintainers of those jars and ask them to upgrade a.s.a.p.
All rule id's in the output of Ktlint are now prefixed with a rule set. Although KtLint currently supports standard rules to be unqualified, users are encouraged to include the rule set id in all references to rules. This includes following:
--disabled-rules
parameter in KtLint CLI..editorconfig
properties used to enable or disable rule and rule sets. Note that properties disabled_rules
and ktlint_disabled_rules
have been removed in this release. See disabled rules for more information.source
element in the KtLint CLI baseline.xml
file. Regenerating this file, fixes all rule references automatically.ktlint-enable
/ ktlint-disable
and the @Suppress('ktlint:...')
annotations.VisitorModifier.RunAfterRule
.The experimental
rule set has been merged with standard
rule set. The rules which formerly were part of the experimental
rule set are still being treated as before. The rules will only be run in case .editorconfig
property ktlint_experimental
is enabled or in case the rule is explicitly enabled.
Note that the prefix experimental:
has to be removed from all references to this rule. Check references in:
--disabled-rules
parameter in KtLint CLI..editorconfig
properties used to enable or disable rule and rule sets. Note that properties disabled_rules
and ktlint_disabled_rules
have been removed in this release. See disabled rules for more information.ktlint-enable
/ ktlint-disable
and the @Suppress('ktlint:...')
annotations.VisitorModifier.RunAfterRule
.The rules below have been promoted to non-experimental rules:
Note that this only affects users that have enabled the standard ruleset while having the experimental rules disabled.
This release is intended to be the last release before the 1.0.x release of ktlint. If all goes as planned, the 1.0.x release does not contain any new breaking changes with except of removal of functionality which is deprecated in this release.
This release contains a lot of breaking changes which aims to improve the future maintainability of Ktlint. If you get stuck while migrating, please reach out to us by creating an issue.
Rules in custom rule sets can now be marked as experimental by implementing the Rule.Experimental
interface on the rule. Rules marked with this interface will only be executed by Ktlint if .editorconfig
property ktlint_experimental
is enabled or if the rule itself has been enabled explicitly.
When using this feature, experimental rules should not be defined in a separate rule set as that would require a distinct rule set id. When moving a rule from an experimental rule set to a non-experimental rule set this would mean that the qualified rule id changes. For users of such rules this means that ktlint directives to suppress the rule and properties in the .editorconfig
files have to be changed.
Field defaultAndroidValue
in class EditorConfigProperty
has been renamed to androidStudioCodeStyleDefaultValue
. New fields ktlintOfficialCodeStyleDefaultValue
and intellijIdeaCodeStyleDefaultValue
have been added. Read more about this in the section "Ktlint Official code style".
The .editorconfig
properties disabled_rules
and ktlint_disabled_rules
are no longer supported. Specifying those properties in the editorConfigOverride
or editorConfigDefaults
result in warnings at runtime.
A new code style ktlint_official
is introduced. This code style is work in progress but will become the default code style in the 1.0
release. Please try out the new code style and provide your feedback via the issue tracker.
This ktlint_official
code style combines the best elements from the Kotlin Coding conventions and Android's Kotlin styleguide. This code style also provides additional formatting on topics which are not (explicitly) mentioned in those conventions and style guide. But do note that this code style sometimes formats code in a way which is not accepted by the default code formatters in IntelliJ IDEA and Android Studio. The formatters of those editors produce nicely formatted code in the vast majority of cases. But in a number of edge cases, the formatting contains bugs which are waiting to be fixed for several years. The new code style formats code in a way which is compatible with the default formatting of the editors whenever possible. When using this codestyle, it is best to disable (e.g. not use) code formatting in the editor.
The existing code styles have been renamed to make more clear what the basis of the code style is.
The official
code style has been renamed to intellij_idea
. Code formatted with this code style aims to be compatible with default formatter of IntelliJ IDEA. This code style is based on Kotlin Coding conventions. If .editorconfig
property ktlint_code_style
has been set to android
then do not forget to change the value of that property to intellij_idea
. When not set, this is still the default code style of ktlint 0.49
. Note that the default code style will be changed to ktlint_official
in the 1.0
release.
Code style android
has been renamed to android_studio
. Code formatted with this code style aims to be compatible with default formatter of Android Studio. This code style is based on Android's Kotlin styleguide. If .editorconfig
property ktlint_code_style
has been set to android
then do not forget to change the value of that property to android_studio
.
The internal structure of the Ktlint project has been revised. The Ktlint CLI and KtLint API modules have been decoupled where possible. Modules have been restructured and renamed. See API Overview for more information.
This is the last release that contains module ktlint-core
as it had too many responsibilities. All classes in this module are relocated to other modules. Some classes have also been renamed. See tables below for details. Classes that are left behind in the ktlint-core
module are deprecated and were kept in this version for backwards compatibility only. The ktlint-core
module will be removed in Ktlint 0.50.x
.
Classes below have been moved from module ktlint-core
to the new module ktlint-rule-engine-core
:
Old class/package name in ktlint-core |
New class/package name in ktlint-rule-engine-core |
---|---|
com.pinterest.ktlint.core.api.editorconfig | com.pinterest.ktlint.rule.engine.core.api.editorconfig |
com.pinterest.ktlint.core.api.EditorConfigProperties | com.pinterest.ktlint.rule.engine.core.api.EditorConfig |
com.pinterest.ktlint.core.api.OptInFeatures | com.pinterest.ktlint.rule.engine.core.api.OptInFeatures |
com.pinterest.ktlint.core.ast.ElementType | com.pinterest.ktlint.rule.engine.core.api.ElementType |
com.pinterest.ktlint.core.ast.package | com.pinterest.ktlint.rule.engine.core.api.ASTNodeExtension |
com.pinterest.ktlint.core.IndentConfig | com.pinterest.ktlint.rule.engine.core.api.IndentConfig |
com.pinterest.ktlint.core.Rule | com.pinterest.ktlint.rule.engine.core.api.Rule |
com.pinterest.ktlint.core.RuleProvider | com.pinterest.ktlint.rule.engine.core.api.RuleProvider |
Classes below have been moved from module ktlint-core
to the new module ktlint-rule-engine
:
Old class/package name in ktlint-core |
New class/package name in ktlint-rule-engine |
---|---|
com.pinterest.ktlint.core.api.EditorConfigDefaults | com.pinterest.ktlint.rule.engine.api.EditorConfigDefaults |
com.pinterest.ktlint.core.api.EditorConfigOverride | com.pinterest.ktlint.rule.engine.api.EditorConfigOverride |
com.pinterest.ktlint.core.api.KtLintParseException | com.pinterest.ktlint.rule.engine.api.KtLintParseException |
com.pinterest.ktlint.core.api.KtLintRuleException | com.pinterest.ktlint.rule.engine.api.KtLintRuleException |
com.pinterest.ktlint.core.KtLint | com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine |
com.pinterest.ktlint.core.LintError | com.pinterest.ktlint.rule.engine.api.LintError |
Class com.pinterest.ktlint.core.KtLint.Code.CodeFile
has been replaced with factory method com.pinterest.ktlint.rule.engine.api.Code.fromFile
. Likewise, class com.pinterest.ktlint.core.KtLint.Code.CodeSnippet
has been replaced with factory method com.pinterest.ktlint.rule.engine.api.Code.fromSnippet
.
Class below has been moved from module ktlint-core
to the new module ktlint-cli-ruleset-core
:
Old class/package name in ktlint-core |
New class/package name in ktlint-cli-ruleset-core |
---|---|
com.pinterest.ktlint.core.RuleSetProviderV2 | com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3 |
Class below has been moved from module ktlint-core
to the new module ktlint-cli-reporter-core
:
Old class/package name in ktlint-core |
New class/package name in ktlint-cli-reporter-core |
---|---|
com.pinterest.ktlint.core.KtlintVersion | com.pinterest.ktlint.cli.reporter.core.api.KtlintVersion |
Class below has been moved from module ktlint-core
to the new module ktlint-logger
:
Old class/package name in ktlint-core |
New class/package name in ktlint-logger |
---|---|
com.pinterest.ktlint.core.KtLintKLoggerInitializer.kt | com.pinterest.ktlint.logger.api.KtLintKLoggerInitializer.kt |
Class below has been relocated from module ktlint-core
to module ktlint-cli
:
Old class/package name in ktlint-core |
New class/package name in ktlint-cli |
---|---|
com.pinterest.ktlint.core.api.Baseline | com.pinterest.ktlint.cli.api.Baseline |
Module ktlint-reporter-baseline
has been renamed to ktlint-cli-reporter-baseline
. Class below has been relocated:
Old class/package name in ktlint-reporter-baseline |
New class/package name in ktlint-cli-reporter-baseline |
---|---|
com.pinterest.ktlint.reporter.baseline | com.pinterest.ktlint.cli.reporter.baseline |
Module ktlint-reporter-checkstyle
has been renamed to ktlint-cli-reporter-checkstyle
. Class below has been relocated:
Old class/package name in ktlint-reporter-checkstyle |
New class/package name in ktlint-cli-reporter-checkstyle |
---|---|
com.pinterest.ktlint.reporter.checkstyle | com.pinterest.ktlint.cli.reporter.checkstyle |
Module ktlint-reporter-format
has been renamed to ktlint-cli-reporter-format
. Class below has been relocated:
Old class/package name in ktlint-reporter-format |
New class/package name in ktlint-cli-reporter-format |
---|---|
com.pinterest.ktlint.reporter.format | com.pinterest.ktlint.cli.reporter.format |
Module ktlint-reporter-html
has been renamed to ktlint-cli-reporter-html
. Class below has been relocated:
Old class/package name in ktlint-reporter-html |
New class/package name in ktlint-cli-reporter-html |
---|---|
com.pinterest.ktlint.reporter.html | com.pinterest.ktlint.cli.reporter.html |
Module ktlint-reporter-json
has been renamed to ktlint-cli-reporter-json
. Class below has been relocated:
Old class/package name in ktlint-reporter-json |
New class/package name in ktlint-cli-reporter-json |
---|---|
com.pinterest.ktlint.reporter.json | com.pinterest.ktlint.cli.reporter.json |
Module ktlint-reporter-plain
has been renamed to ktlint-cli-reporter-plain
. Class below has been relocated:
Old class/package name in ktlint-reporter-plain |
New class/package name in ktlint-cli-reporter-plain |
---|---|
com.pinterest.ktlint.reporter.plain | com.pinterest.ktlint.cli.reporter.plain |
Module ktlint-reporter-plain-summary
has been renamed to ktlint-cli-reporter-plain-summary
. Class below has been relocated:
Old class/package name in ktlint-reporter-plain-summary |
New class/package name in ktlint-cli-reporter-plain-summary |
---|---|
com.pinterest.ktlint.reporter.plain | com.pinterest.ktlint.cli.reporter.plainsummary |
Module ktlint-reporter-sarif
has been renamed to ktlint-cli-reporter-sarif
. Class below has been relocated:
Old class/package name in ktlint-reporter-sarif |
New class/package name in ktlint-cli-reporter-sarif |
---|---|
com.pinterest.ktlint.reporter.sarif | com.pinterest.ktlint.cli.reporter.sarif |
RuleSetProviderV2
Custom rule sets build for older versions of KtLint are no longer supported by this version of KtLint. The com.pinterest.ktlint.core.RuleSetProviderV2
interface has been replaced with RuleSetProviderV3
. The accompanying interfaces com.pinterest.ktlint.core.RuleProvider
and com.pinterest.ktlint.core.Rule
have been replaced with com.pinterest.ktlint.ruleset.core.api.RuleProvider
and com.pinterest.ktlint.ruleset.core.api.Rule
respectively.
Contrary to RuleSetProviderV2
, the RuleSetProviderV3
no longer contains information about the rule set. About information now has to be specified in the new Rule
class. This allows custom rule set providers to combine rules originating from different rule sets into a new rule set without loosing information about its origin. The type of the id of the rule set is changed from String
to RuleSetId
.
Note that due to renaming and relocation of the RuleSetProviderV3
interface the name of the service provider in the custom reporter needs to be changed from resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2
to resources/META-INF/services/com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3
.
The rule id's in com.pinterest.ktlint.ruleset.core.api.Rule
have been changed from type String
to RuleId
. A RuleId
has a value that must adhere the convention "rule-set-id
:rule-id
". The rule set id standard
is reserved for rules which are maintained by the KtLint project. Rules created by custom rule set providers and API Consumers should use a prefix other than standard
to mark the origin of rules which are not maintained by the KtLint project.
When wrapping a rule from the ktlint project and modifying its behavior, please change the ruleId
and about
fields in the wrapped rule, so that it is clear to users whenever they use the original rule provided by KtLint versus a modified version which is not maintained by the KtLint project.
The typealias com.pinterest.ktlint.core.api.EditorConfigProperties
has been replaced with com.pinterest.ktlint.rule.engine.core.api.EditorConfig
. The interface com.pinterest.ktlint.core.api.UsesEditorConfigProperties
has been removed. Instead, the Rule property usesEditorConfigProperties
needs to be set. As a result of those changes, the beforeFirstNode
function in each rule has to changed to something like below:
public class SomeRule : Rule(
ruleId = RuleId("some-rule-set:some-rule"),
usesEditorConfigProperties = setOf(MY_EDITOR_CONFIG_PROPERTY),
) {
private lateinit var myEditorConfigProperty: MyEditorConfigProperty
override fun beforeFirstNode(editorConfig: EditorConfig) {
myEditorConfigProperty = editorConfig[MY_EDITOR_CONFIG_PROPERTY]
}
...
}
Fields loadOnlyWhenOtherRuleIsLoaded
and runOnlyWhenOtherRuleIsEnabled
have been removed from class com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule
and are replaced with a single field mode
. The mode
either contains value REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED
or ONLY_WHEN_RUN_AFTER_RULE_IS_LOADED_AND_ENABLED
.
ReporterProvider
Custom Reporters build for older versions of KtLint are no longer supported by this version of KtLint. The com.pinterest.ktlint.core.ReporterProvider
interface has been replaced with com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2
. The accompanying interface com.pinterest.ktlint.core.Reporter
has been replaced with com.pinterest.ktlint.cli.reporter.core.api.ReporterV2
.
Note that due to renaming and relocation of the ReporterProviderV2
interface the name of the service provider in the custom reporter needs to be changed from resources/META-INF/services/com.pinterest.ktlint.core.ReporterProvider
to resources/META-INF/services/com.pinterest.ktlint.cli.reporter.core.api.ReporterProviderV2
.
The biggest change in the ReporterV2
is the replacement of the LintError
class with KtlintCliError
class. The latter class now contains a status field which more clearly explains the difference between a lint error which can be autocorrected versus a lint error that actually has been autocorrected.
API Consumers provide a set of rules directly to the Ktlint Rule Engine. The com.pinterest.ktlint.core.Rule
has been replaced with com.pinterest.ktlint.ruleset.core.api.Rule
.
The type of the rule id's has been changed from type String
to RuleId
. A RuleId
has a value that must adhere to the convention "rule-set-id
:rule-id
". Rule set id standard
is reserved for rules which are maintained by the KtLint project. Custom rules created by the API Consumer should use a prefix other than standard
to clearly mark the origin of rules which are not maintained by the KtLint project.
Also, the field About
has been added. This field specifies the name of the maintainer, and the repository url and issue tracker url of the rule. The about information of a rule is printed whenever a rule throws an exception which is caught by the Ktlint Rule Engine.
When wrapping a rule from the ktlint project and modifying its behavior, please change the ruleId
and about
fields in the wrapped rule, so that it is clear to users whenever they use the original rule provided by KtLint versus a modified version which is not maintained by the KtLint project.
The typealias com.pinterest.ktlint.core.api.EditorConfigProperties
has been replaced with com.pinterest.ktlint.rule.engine.core.api.EditorConfig
. The interface com.pinterest.ktlint.core.api.UsesEditorConfigProperties
has been removed. Instead, the Rule property usesEditorConfigProperties
needs to be set. As a result of those changes, the beforeFirstNode
function in each rule has to changed to something like below:
public class SomeRule : Rule(
ruleId = RuleId("some-rule-set:some-rule"),
usesEditorConfigProperties = setOf(MY_EDITOR_CONFIG_PROPERTY),
) {
private lateinit var myEditorConfigProperty: MyEditorConfigProperty
override fun beforeFirstNode(editorConfig: EditorConfig) {
myEditorConfigProperty = editorConfig[MY_EDITOR_CONFIG_PROPERTY]
}
...
}
Fields loadOnlyWhenOtherRuleIsLoaded
and runOnlyWhenOtherRuleIsEnabled
have been removed from class com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule
and are replaced with a single field mode
. The mode
either contains value REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED
or ONLY_WHEN_RUN_AFTER_RULE_IS_LOADED_AND_ENABLED
.
Like before, the API Consumer can still offer a mix of rules originating from ktlint-ruleset-standard
as well as custom rules.
.editorconfig
property max_line_length
default valuePreviously, the default value for .editorconfig
property max_line_length
was set to -1
in ktlint unless the property was defined explicitly in the .editorconfig
or when ktlint_code_style
was set to Android. As a result of that rules have to check that max_line_length contains a positive value before checking that the actual line length is exceeding the maximum. Now the value Int.MAX_VALUE
(use constant MAX_LINE_LENGTH_PROPERTY_OFF
to refer to that value) is used instead.
Constant KtLint.FILE_PATH_USER_DATA_KEY
has been removed. The file path is passed correctly to the node with element type FILE and can be retrieved as follows:
if (node.isRoot()) {
val filePath = (node.psi as? KtFile)?.virtualFilePath
...
}
ktlint_offical
. The code style is work in progress and should be considered a preview. It is intended to become the default code style in the next release. Please try it out and give your feedback. See code styles for more information. The following rules have been added to the ktlint_official
code style (the rules can also be run for other code styles when enabled explicitly):
no-empty-first-line-in-class-body
. This rule disallows a class to start with a blank line.if-else-bracing
. This rules enforces consistent usage of braces in all branches of a single if, if-else or if-else-if statement.no-consecutive-comments
. This rule disallows consecutive comments except EOL comments (see [examples](See https://pinterest.github.io/ktlint/rules/experimental/#disallow-consecutive-comments)).try-catch-finally-spacing
. This rule enforces consistent spacing in try-catch, try-finally and try-catch-finally statement. This rule can also be run for other code styles, but then it needs to be enabled explicitly.no-blank-line-in-list
. This rule disallows blank lines to be used in super type lists, type argument lists, type constraint lists, type parameter lists, value argument lists, and value parameter lists. (#1224)multiline-expression-wrapping
. This forces a multiline expression as value in an assignment to start on a separate line. (#1217)string-template-indent
. This forces multiline string templates which are post-fixed with .trimIndent()
to be formatted consistently. The opening and closing """
are placed on separate lines and the indentation of the content of the template is aligned with the """
. (#925)if-else-wrapping
. This enforces that a single line if-statement is kept simple. A single line if-statement may contain no more than one else-branch. The branches a single line if-statement may not be wrapped in a block. (#812)parameter-wrapping
(#1846)property-wrapping
(#1846)stdin
with KtLint CLI (#1832)build.gradle
like:
dependencies {
implementation(platform("com.pinterest:ktlint-bom:0.49.0"))
implementation("com.pinterest:ktlint-core")
implementation("com.pinterest:ktlint-reporter-html")
implementation("com.pinterest:ktlint-ruleset-standard")
...
}
enum-wrapping
for all code styles. An enum should either be a single line, or each enum entry should be defined on a separate line. (#1903).editorconfig
properties disabled_rules
and ktlint_disabled_rules
. See disabled rules for more information.--print-ast
. Use IntelliJ IDEA PsiViewer plugin instead. (#1925)no-semi
(#1733)function-signature
.multiline
. function-signature
.standard:filename
rule whenever Ktlint CLI is run with option --stdin
(#1742)ktlint_official
code style) indent
(#1756)trailing-comma-on-declaration-site
(#1711)ktlint_official
code style only indent
(#1540).editorconfig
use value off
for the max_line_length
property instead of value -1
to denote that lines are not restricted to a maximum length (#1824)trailing-comma-on-declaration-site
(#1786)function-return-type-spacing
(#1764)annotation
, wrapping
(#1725)indent
(#1788)indent
(#1788)spacing-between-declarations-with-annotations
(#1802)wrapping
(#1808)indent
(#1830)..<
similar to the range operator ..
in range-spacing
(#1858).editorconfig
property ij_kotlin_imports_layout
contains a |
but no import exists that match any pattern before the first |
then do not report a violation nor insert a blank line import-ordering
(#1845)**/*.kt
and **/*.kts
) so that all Kotlin files excluding the files matching the negate-patterns will be processed (#1847)type-parameter-list-spacing
(#1867)wrapping
(#1867)indent
(#1217)trailing-comma-on-declaration-site
(#1905)ktlint_official
only. function-signature
, parameter-list-wrapping
(#1908)annotation
(#1909)trailing-comma-on-declaration-site
(#1911)ktlint_official
. annotation
(#1916)annotation
(#1917)multiline-if-else
(#1904)ktlint_official
code style) parameter-list-wrapping
(#1681).--patterns-from-stdin
is specified (#1793)1.8.20
and Kotlin version to 1.8.20
.ktlint_official
, do not allow wildcard imports java.util
and kotlinx.android.synthetic
by default. Important: .editorconfig
property ij_kotlin_packages_to_use_import_on_demand
needs to be set to value unset
in order to enforce IntelliJ IDEA default formatter to not generate wildcard imports no-wildcard-imports
(#1797)comment-wrapping
(#1941)comment-wrapping
(#1942)0.48.0
and 0.48.1
Starting with Ktlint 0.48.x
, rule and rule sets can be enabled/disabled with a separate property per rule (set). Please read deprecation of (ktlint_)disable_rules property for more information.
API Consumers that provide experimental rules to the KtLintRuleEngine, must also enable the experimental rules or instruct their users to do so in the .editorconfig
file. From the perspective of the API Consumer it might be confusing or unnecessary to do so as the experimental rule was already provided explicitly.
Ktlint wants to provide the user (e.g. a developer) a uniform and consistent user experience. The .editorconfig
becomes more and more central to store configuration for Ktlint. This to ensure that all team members use the exact same configuration when running ktlint regardless whether the Ktlint CLI or an API Consumer is being used.
The .editorconfig
is a powerful configuration tool which can be used in very different ways. Most projects use a single .editorconfig
file containing one common section for kotlin and kotlin scripts files. For example, the .editorconfig
file of the Ktlint project contains following section:
[*.{kt,kts}]
ij_kotlin_imports_layout = *
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
Other projects might contain multiple .editorconfig
files for different parts of the project directory hierarchy. Or, use a single .editorconfig
file containing multiple sections with different globs. Like all other configuration settings in Ktlint, the user should be able to enable and disable the experimental rules. Both for the entire set of experimental rules and for individual experimental rules.
Ktlint allows API Consumers to set default values and override values for the .editorconfig
. Specifying a default value means that the user does not need to define the property in the .editorconfig
file but if the user specifies the value, it will take precedence. Specifying the override value ensures that this takes precedence on a value specified by the user in the .editorconfig
.
From the Ktlint perspective, it is advised that API Consumers provide the default value. See example below, for how to specify the editorConfigDefault
property:
KtLintRuleEngine(
ruleProviders = ruleProviders,
editorConfigDefaults = EditorConfigDefaults(
EditorConfig
.builder()
.section(
Section
.builder()
.glob(Glob("*.{kt,kts}"))
.properties(
Property
.builder()
.name("ktlint_experimental")
.value("enabled"),
),
)
.build()
)
)
If the user has set property ktlint_experimental
explicitly than that value will be used. If the value is not defined, the value provided via editorConfigDefaults
will be used.
If you do want to ignore the value of ktlint_experimental
as set by the user, than you can set the EditorConfigOverride property. But as said before that is discouraged as the user might not understand why the .editorconfig
property is being ignored (provided that the value set is not equal to the value provided by the API Consumer).
annotation
(#1765).editorconfig
properties disabled_rules
or ktlint_disabled_rules
are set. (#1771)max-line-length
should be ignored by rule function-signature
(#1773)class-naming
, function-naming
, package-name
, property-naming
(#1757)file-name
rule on code snippets (#1768).editorconfig
property ij_kotlin_imports_layout
's entries (#1770)