ktlint v0.47.0 Release Notes

Release Date: 2022-08-19 // over 1 year ago
  • API Changes & RuleSet providers

    ๐Ÿš€ If you are not an API consumer nor a RuleSet provider, then you can safely skip this section. Otherwise, please read below carefully and upgrade your usage of ktlint. In this and coming releases, we are changing and adapting important parts of our API in order to increase maintainability and flexibility for future changes. Please avoid skipping a releases as that will make it harder to migrate.

    ๐Ÿ—„ Rule lifecycle hooks / deprecate RunOnRootOnly visitor modifier

    ๐Ÿ‘• Up until ktlint 0.46 the Rule class provided only one life cycle hook. This "visit" hook was called in a depth-first-approach on all nodes in the file. A rule like the IndentationRule used the RunOnRootOnly visitor modifier to call this lifecycle hook for the root node only in combination with an alternative way of traversing the ASTNodes. Downside of this approach was that suppression of the rule on blocks inside a file was not possible (#631). More generically, this applied to all rules, applying alternative traversals of the AST.

    The Rule class now offers new life cycle hooks:

    • beforeFirstNode: This method is called once before the first node is visited. It can be used to initialize the state of the rule before processing of nodes starts. The ".editorconfig" properties (including overrides) are provided as parameter.
    • ๐Ÿ‘• beforeVisitChildNodes: This method is called on a node in AST before visiting its child nodes. This is repeated recursively for the child nodes resulting in a depth first traversal of the AST. This method is the equivalent of the "visit" life cycle hooks. However, note that in KtLint 0.48, the UserData of the rootnode no longer provides access to the ".editorconfig" properties. This method can be used to emit Lint Violations and to autocorrect if applicable.
    • ๐Ÿ‘• afterVisitChildNodes: This method is called on a node in AST after all its child nodes have been visited. This method can be used to emit Lint Violations and to autocorrect if applicable.
    • afterLastNode: This method is called once after the last node in the AST is visited. It can be used for teardown of the state of the rule.

    ๐Ÿ‘€ Optionally, a rule can stop the traversal of the remainder of the AST whenever the goal of the rule has been achieved. See KDoc on Rule class for more information.

    The "visit" life cycle hook will be removed in Ktlint 0.48. In KtLint 0.47 the "visit" life cycle hook will be called only when hook "beforeVisitChildNodes" is not overridden. It is recommended to migrate to the new lifecycle hooks in KtLint 0.47. Please create an issue, in case you need additional assistence to implement the new life cycle hooks in your rules.

    Ruleset providing by Custom Rule Set Provider

    ๐Ÿ‘• The KtLint engine needs a more fine-grained control on the instantiation of new Rule instances. Currently, a new instance of a rule can be created only once per file. However, when formatting files the same rule instance is reused for a second processing iteration in case a Lint violation has been autocorrected. By re-using the same rule instance, state of that rule might leak from the first to the second processing iteration.

    ๐Ÿ‘• Providers of custom rule sets have to migrate the custom rule set JAR file. The current RuleSetProvider interface which is implemented in the custom rule set is deprecated and marked for removal in KtLint 0.48. Custom rule sets using the old RuleSetProvider interface will not be run in KtLint 0.48 or above.

    ๐Ÿ‘• For now, it is advised to implement the new RuleSetProviderV2 interface without removing the old RuleSetProvider interface. In this way, KtLint 0.47 and above use the RuleSetProviderV2 interface and ignore the old RuleSetProvider interface completely. KtLint 0.46 and below only use the old RuleSetProvider interface.

    โž• Adding the new interface is straight forward, as can be seen below:

    // Current implementation
    public class CustomRuleSetProvider : RuleSetProvider {
        override fun get(): RuleSet = RuleSet(
            "custom",
            CustomRule1(),
            CustomRule2(),
        )
    }
    
    // New implementation
    public class CustomRuleSetProvider :
        RuleSetProviderV2(CUSTOM_RULE_SET_ID),
        RuleSetProvider {
        override fun get(): RuleSet =
            RuleSet(
                CUSTOM_RULE_SET_ID,
                CustomRule1(),
                CustomRule2()
            )
    
        override fun getRuleProviders(): Set<RuleProvider> =
            setOf(
                RuleProvider { CustomRule1() },
                RuleProvider { CustomRule2() }
            )
    
        private companion object {
            const val CUSTOM_RULE_SET_ID = custom"
        }
    }
    
    

    ๐Ÿ‘• Also note that file 'resource/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2' needs to be added. In case your custom rule set provider implements both RuleSetProvider and RuleSetProviderV2, the resource directory contains files for both implementation. The content of those files is identical as the interfaces are implemented on the same class.

    ๐Ÿ‘• Once above has been implemented, rules no longer have to clean up their internal state as the KtLint rule engine can request a new instance of the Rule at any time it suspects that the internal state of the Rule is tampered with (e.g. as soon as the Rule instance is used for traversing the AST).

    Rule set providing by API Consumer

    ๐Ÿ‘• The KtLint engine needs a more fine-grained control on the instantiation of new Rule instances. Currently, a new instance of a rule can be created only once per file. However, when formatting files the same rule instance is reused for a second processing iteration in case a Lint violation has been autocorrected. By re-using the same rule instance, state of that rule might leak from the first to the second processing iteration.

    ๐Ÿ‘• The ExperimentalParams parameter which is used to invoke "KtLint.lint" and "KtLint.format" contains a new parameter "ruleProviders" which will replace the "ruleSets" parameter in KtLint 0.48. Exactly one of those parameters should be a non-empty set. It is preferred that API consumers migrate to using "ruleProviders".

    // Old style using "ruleSets"
    KtLint.format(
        KtLint.ExperimentalParams(
            ...
            ruleSets = listOf(
                RuleSet(
                    "custom",
                    CustomRule1(),
                    CustomRule2()
                )
            ),
            ...
        )
    )
    
    // New style using "ruleProviders"
    KtLint.format(
        KtLint.ExperimentalParams(
            ...
            ruleProviders = setOf(
                RuleProvider { CustomRule1() },
                RuleProvider { CustomRule2() }
            ),
            cb = { _, _ -> }
        )
    )
    

    ๐Ÿ‘• Once above has been implemented, rules no longer have to clean up their internal state as the KtLint rule engine can request a new instance of the Rule at any time it suspects that the internal state of the Rule is tampered with (e.g. as soon as the Rule instance is used for traversing the AST).

    Format callback

    The callback function provided as parameter to the format function is now called for all errors regardless whether the error has been autocorrected. Existing consumers of the format function should now explicitly check the autocorrected flag in the callback result and handle it appropriately (in most case this will be ignoring the callback results for which autocorrected has value true).

    CurrentBaseline

    ๐Ÿ‘• Class com.pinterest.ktlint.core.internal.CurrentBaseline has been replaced with com.pinterest.ktlint.core.api.Baseline.

    Noteworthy changes:

    • ๐Ÿ‘• Field baselineRules (nullable) is replaced with `lintErrorsPerFile (non-nullable).
    • Field baselineGenerationNeeded (boolean) is replaced with status (type Baseline.Status).

    ๐Ÿ‘• The utility functions provided via com.pinterest.ktlint.core.internal.CurrentBaseline are moved to the new class. One new method List<LintError>.doesNotContain(lintError: LintError) is added.

    .editorconfig property "disabled_rules"

    ๐Ÿ‘• The .editorconfig property disabled_rules (api property DefaultEditorConfigProperties.disabledRulesProperty) has been deprecated and will be removed in a future version. Use ktlint_disabled_rules (api property DefaultEditorConfigProperties.ktlintDisabledRulesProperty) instead as it more clearly identifies that ktlint is the owner of the property. This property is to be renamed in .editorconfig files and ExperimentalParams.editorConfigOverride.

    ๐Ÿ‘• Although, Ktlint 0.47.0 falls back on property disabled_rules whenever ktlint_disabled_rules is not found, this result in a warning message being printed.

    0๏ธโƒฃ Default/alternative .editorconfig

    ๐Ÿ—„ Parameter "ExperimentalParams.editorConfigPath" is deprecated in favor of the new parameter "ExperimentalParams.editorConfigDefaults". When used in the old implementation this resulted in ignoring all ".editorconfig" files on the path to the file. The new implementation uses properties from the "editorConfigDefaults"parameter only when no ".editorconfig" files on the path to the file supplies this property for the filepath.

    0๏ธโƒฃ API consumers can easily create the EditConfigDefaults by calling 0๏ธโƒฃ "EditConfigDefaults.load(path)" or creating it programmatically.

    Reload of .editorconfig file

    ๐Ÿ‘• Some API Consumers keep a long-running instance of the KtLint engine alive. In case an .editorconfig file is changed, which was already loaded into the internal cache of the KtLint engine this change would not be taken into account by KtLint. One way to deal with this, was to clear the entire KtLint cache after each change in an .editorconfig file.

    Now, the API consumer can reload an .editorconfig. If the .editorconfig with given path is actually found in the cached, it will be replaced with the new value directly. If the file is not yet loaded in the cache, loading will be deferred until the file is actually requested again.

    Example:

    KtLint.reloadEditorConfigFile("/some/path/to/.editorconfig")
    

    Miscellaneous

    ๐Ÿ‘• Several methods for which it is unlikely that they are used by API consumers have been marked for removal from the public API in KtLint 0.48.0. Please create an issue in case you have a valid business case to keep such methods in the public API.

    โž• Added

    • โž• Add format reporter. This reporter prints a one-line-summary of the formatting status per file. (#621).

    ๐Ÿ›  Fixed

    • ๐Ÿ›  Fix cli argument "--disabled_rules" (#1520).
    • ๐Ÿ‘• A file which contains a single top level declaration of type function does not need to be named after the function but only needs to adhere to the PascalCase convention. filename (#1521).
    • ๐Ÿ‘• Disable/enable IndentationRule on blocks in middle of file. (indent) #631
    • ๐Ÿ‘ Allow usage of letters with diacritics in enum values and filenames (enum-entry-name-case, filename) (#1530).
    • Fix resolving of Java version when JAVA_TOOL_OPTIONS is set (#1543)
    • ๐Ÿ‘• When a glob is specified then ensure that it matches files in the current directory and not only in subdirectories of the current directory (#1533).
    • ๐Ÿ‘• Execute ktlint cli on default kotlin extensions only when an (existing) path to a directory is given. (#917).
    • ๐Ÿ‘• Invoke callback on format function for all errors including errors that are autocorrected (#1491)
    • ๐Ÿ‘• Merge first line of body expression with function signature only when it fits on the same line function-signature (#1527)
    • โž• Add missing whitespace when else is on same line as true condition multiline-if-else (#1560)
    • ๐Ÿ›  Fix multiline if-statements multiline-if-else (#828)
    • Prevent class cast exception on ".editorconfig" property ktlint_code_style (#1559)
    • ๐Ÿ‘• Handle trailing comma in enums trailing-comma (#1542)
    • ๐Ÿ‘ Allow EOL comment after annotation (#1539)
    • ๐Ÿ‘• Split rule trailing-comma into trailing-comma-on-call-site and trailing-comma-on-declaration-site (#1555)
    • ๐Ÿ‘Œ Support globs containing directories in the ".editorconfig" supplied via CLI "--editorconfig" (#1551)
    • ๐Ÿ›  Fix indent of when entry with a dot qualified expression instead of simple value when trailing comma is required (#1519)
    • ๐Ÿ›  Fix whitespace between trailing comma and arrow in when entry when trailing comma is required (#1519)
    • ๐Ÿ‘• Prevent false positive in parameter list for which the last value parameter is a destructuring declaration followed by a trailing comma wrapping (#1578)

    ๐Ÿ”„ Changed

    • ๐Ÿ‘• Print an error message and return with non-zero exit code when no files are found that match with the globs (#629).
    • ๐Ÿ‘• Invoke callback on format function for all errors including errors that are autocorrected (#1491)
    • ๐Ÿ‘Œ Improve rule annotation (#1574)
    • ๐Ÿ‘• Rename .editorconfig property disabled_rules to ktlint_disabled_rules (#701)
    • ๐Ÿ‘ Allow file and directory paths in CLI-parameter "--editorconfig" (#1580)
    • โšก๏ธ Update Kotlin development version to 1.7.20-beta and Kotlin version to 1.7.10.
    • ๐Ÿ“š Update release scripting to set version number in mkdocs documentation (#1575).
    • โšก๏ธ Update Gradle to 7.5.1 version

    โœ‚ Removed

    • โœ‚ Remove support to generate IntelliJ IDEA configuration files as this no longer fits the scope of the ktlint project (#701)