From 6fdb80e7d25257d744a74645daa7f0edce98581c Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sun, 7 Aug 2022 11:38:38 +0200 Subject: [PATCH] Remove support to generate IntelliJ IDEA configuration files. Rename `.editorconfig` property `disabled_rules` to `ktlint_disabled_rules`. (#1570) Closes #701 Closes #734 --- CHANGELOG.md | 8 + docs/install/cli.md | 4 - docs/install/integrations.md | 89 ------ docs/mkdocs.yml | 77 ------ docs/rules/configuration-intellij-idea.md | 36 +++ ...nfiguration.md => configuration-ktlint.md} | 0 .../core/api/UsesEditorConfigProperties.kt | 39 ++- .../ktlint/core/internal/VisitorProvider.kt | 21 +- .../ktlint/core/DisabledRulesTest.kt | 194 +++++++++---- .../main/kotlin/com/pinterest/ktlint/Main.kt | 6 - .../internal/ApplyToIDEACommandHelper.kt | 68 ----- .../internal/ApplyToIDEAGloballySubCommand.kt | 39 --- .../internal/ApplyToIDEAProjectSubCommand.kt | 39 --- .../internal/IntellijIDEAIntegration.kt | 260 ------------------ mkdocs.yml | 3 +- 15 files changed, 236 insertions(+), 647 deletions(-) delete mode 100644 docs/mkdocs.yml create mode 100644 docs/rules/configuration-intellij-idea.md rename docs/rules/{configuration.md => configuration-ktlint.md} (100%) delete mode 100644 ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEACommandHelper.kt delete mode 100644 ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAGloballySubCommand.kt delete mode 100644 ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAProjectSubCommand.kt delete mode 100644 ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 64527f31ad..050550acc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,6 +122,12 @@ Noteworthy changes: The utility functions provided via `com.pinterest.ktlint.core.internal.CurrentBaseline` are moved to the new class. One new method `List.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. + ### Added * Add `format` reporter. This reporter prints a one-line-summary of the formatting status per file. ([#621](https://github.com/pinterest/ktlint/issue/621)). @@ -147,8 +153,10 @@ The utility functions provided via `com.pinterest.ktlint.core.internal.CurrentBa * Print an error message and return with non-zero exit code when no files are found that match with the globs ([#629](https://github.com/pinterest/ktlint/issue/629)). * Invoke callback on `format` function for all errors including errors that are autocorrected ([#1491](https://github.com/pinterest/ktlint/issues/1491)) +* Rename `.editorconfig` property `disabled_rules` to `ktlint_disabled_rules` ([#701](https://github.com/pinterest/ktlint/issues/701)) ### Removed +* Remove support to generate IntelliJ IDEA configuration files as this no longer fits the scope of the ktlint project ([#701](https://github.com/pinterest/ktlint/issues/701)) ## [0.46.1] - 2022-06-21 diff --git a/docs/install/cli.md b/docs/install/cli.md index 03bea8b958..1c3b5b09e3 100644 --- a/docs/install/cli.md +++ b/docs/install/cli.md @@ -177,10 +177,6 @@ ktlint installGitPrePushHook `-a` or `--android`: Turn on Android Kotlin Style Guide compatibility. This flag is most likely to be removed in a future version. Use `.editorconfig ktlint_code_style`(https://pinterest.github.io/ktlint/rules/configuration/#code-style). -`applyToIDEA` or `--apply-to-idea`: Update Intellij IDEA Kotlin codestyle settings (global) - -`applyToIDEAProject` or `--apply-to-idea-project`: Update Intellij IDEA project settings - `--color` and `--color-name=`: Make output colorful and optionally set the color name to use. `--disabled_rules=`: A comma-separated list of rules to globally disable. To disable the standard ktlint rule-set use `--disabled_rules=standard`. This flag is most likely to be removed in a future version. Use `.editorconfig disabled_rules`(https://pinterest.github.io/ktlint/rules/configuration/#disabled-rules). diff --git a/docs/install/integrations.md b/docs/install/integrations.md index c876fe3fb5..315b0edddc 100644 --- a/docs/install/integrations.md +++ b/docs/install/integrations.md @@ -188,95 +188,6 @@ val ktlintFormat by tasks.creating(JavaExec::class) { } ``` -## [IntelliJ IDEA](https://www.jetbrains.com/idea/) integration - -!!! Warning - `ktlint` strives to prevent code formatting conflicts with IntelliJ IDEA / Android Studio. We recommend using either IDE formatting or `ktlint` formatting. However, if you persist on using both, then please ensure that the formatting settings are aligned as described below. This reduces the chance that code which is formatted by ktlint conflicts with formatting by the IntelliJ IDEA built-in formatter. - -Choose any of options below to align the formatting settings of IntelliJ IDEA. - -### Update code style of single project via ktlint (recommended) - -Use [ktlint cli](../cli) to change the code style settings of a single project with any of the commands below. -```sh -# Run command below from root directory of project -ktlint applyToIDEAProject -``` - -Or if you want to use android specific code style: - -```sh -# Run command below from root directory of project -ktlint --android applyToIDEAProject -``` - -### Update global code style for all projects via ktlint - -Use [ktlint cli](../cli) to change the code style settings of all projects with any of the commands below. -```sh -ktlint applyToIDEA -``` - -Or if you want to use android specific code style: -```sh -ktlint --android applyToIDEA -``` - -### Manually update `.editorconfig` - -Create or update the code style config with `.editorconfig` by setting properties below: -```ini -[{*.kt,*.kts}] -ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL - -ij_kotlin_line_comment_at_first_column = false -ij_kotlin_line_comment_add_space = true - -# These options can keep to use single name import -ij_kotlin_name_count_to_use_star_import = 2147483647 -ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 - -ij_kotlin_keep_blank_lines_in_declarations = 1 -ij_kotlin_keep_blank_lines_in_code = 1 -ij_kotlin_keep_blank_lines_before_right_brace = 0 - -# optional but recommended -ij_kotlin_align_multiline_parameters = false - -# optional but recommended -ij_continuation_indent_size = 4 - -# Android specific rules -ij_kotlin_import_nested_classes = false -ij_kotlin_imports_layout = *,^ -``` - -### Manually update the IntelliJ IDEA preferences - -!!! Warning - `ktlint` is *not* reading the IntelliJ IDEA preferences. If your settings are out of sync with the configuration in `.editorconfig` or the default values of `ktlint`, this results in formatting differences. - -Go to File -> Settings... -> Editor - -- General -> Auto Import - - check `Kotlin` / `Optimize imports on the fly (for current project)`. -- Code Style -> Kotlin - - Set from... on the right -> (Predefined style) -> Kotlin style guide (Kotlin plugin 1.2.20+). - - open Code Generation tab - - uncheck `Line comment at first column`; - - select `Add a space at comment start`. - - open Imports tab - - select `Use single name import` (all of them); - - remove `import java.util.*` from `Packages to Use Import with '*'`. - - open Blank Lines tab - - change `Keep Maximum Blank Lines` / `In declarations` & `In code` to 1 and `Before '}'` to 0. - - (optional but recommended) open Wrapping and Braces tab - - uncheck `Function declaration parameters` (OR `Methods declartion parameters` for older version) / `Align when multiline`. - - (optional but recommended) open Tabs and Indents tab - - change `Continuation indent` to the same value as `Indent` (4 by default). -- Inspections - - change `Severity` level of `Unused import directive` and `Redundant semicolon` under `Kotlin` -> `Redundant constructs` to `ERROR`. - ## [GNU Emacs](https://www.gnu.org/software/emacs/) integration See [whirm/flycheck-kotlin](https://github.com/whirm/flycheck-kotlin). diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml deleted file mode 100644 index 6984192ecd..0000000000 --- a/docs/mkdocs.yml +++ /dev/null @@ -1,77 +0,0 @@ -site_name: Ktlint -site_url: https://ktlint.github.io - -theme: - name: material - favicon: assets/images/favicon.ico - palette: - # Palette toggle for light mode - - media: "(prefers-color-scheme: light)" - scheme: default - primary: pink - toggle: - icon: material/brightness-7 - name: Switch to dark mode - # Palette toggle for dark mode - - media: "(prefers-color-scheme: dark)" - scheme: slate - primary: pink - toggle: - icon: material/brightness-4 - name: Switch to light mode - icon: - repo: material/github - features: - - navigation.tabs - - navigation.tabs.sticky - - navigation.sections - - navigation.expand - - search.suggest - - search.share - -nav: - - Home: index.md - - Installation: - - Overview: install/overview.md - - Command line: install/cli.md - - Integrations: install/integrations.md -# - API: install/api.md TODO: properly document - - Snapshot build: install/snapshot-build.md - - Rules: - - Standard rules: rules/standard.md - - Experimental rules: rules/experimental.md - - Configuration: rules/configuration.md - - Extensions: - - Custom rule set: extensions/custom-rule-set.md - - Custom reporter: extensions/custom-reporter.md - - Badge: extensions/badge.md - - FAQ: faq.md - - Contributing: - - Overview: contributing/overview.md - - Guidelines: contributing/guidelines.md - - Code of conduct: contributing/code-of-conduct.md - -plugins: - - search - -repo_url: https://github.com/pinterest/ktlint -repo_name: pinterest/ktlint - -markdown_extensions: - - toc: - permalink: true - - admonition - - pymdownx.emoji: - emoji_generator: !!python/name:materialx.emoji.to_svg - emoji_index: !!python/name:materialx.emoji.twemoji - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.mark - - pymdownx.smartsymbols - - pymdownx.superfences: - custom_fences: - - name: mermaid - class: mermaid - format: !!python/name:pymdownx.superfences.fence_code_format - - pymdownx.tabbed: - alternate_style: true diff --git a/docs/rules/configuration-intellij-idea.md b/docs/rules/configuration-intellij-idea.md new file mode 100644 index 0000000000..a7c570cfa4 --- /dev/null +++ b/docs/rules/configuration-intellij-idea.md @@ -0,0 +1,36 @@ +!!! Warning + `ktlint` strives to prevent code formatting conflicts with IntelliJ IDEA / Android Studio. We recommend using either IDE formatting or `ktlint` formatting. However, if you persist on using both, then please ensure that the formatting settings are aligned as described below. This reduces the chance that code which is formatted by ktlint conflicts with formatting by the IntelliJ IDEA built-in formatter. + +!!! Note + IntelliJ IDEA supports the [kotlin coding conventions](https://kotlinlang.org/docs/coding-conventions.html). As of version 0.47.x of ktlint, the support to overwrite some configuration files of IntelliJ IDEA has been dropped as it no longer fits the scope of the project. + + +Steps: + +1. Go to your project directory +2. Create or replace file `.idea/codeStyles/codeStyleConfig.xml` with content below: + ```xml + + + + + ``` +3. Create or replace file `.idea/codeStyles/Project.xml` with content below: + ```xml + + + + + + + + + + ``` diff --git a/docs/rules/configuration.md b/docs/rules/configuration-ktlint.md similarity index 100% rename from docs/rules/configuration.md rename to docs/rules/configuration-ktlint.md diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt index 8440757d73..f32ff7a891 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt @@ -85,6 +85,10 @@ public interface UsesEditorConfigProperties { editorConfigProperty: EditorConfigProperty, codeStyleValue: CodeStyleValue ): T { + if (editorConfigProperty.deprecationWarning != null) { + logger.warn { "Property '${editorConfigProperty.type.name}' is deprecated: ${editorConfigProperty.deprecationWarning}" } + } + val property = get(editorConfigProperty.type.name) // If the property value is remapped to a non-null value then return it immediately. @@ -178,7 +182,12 @@ public interface UsesEditorConfigProperties { * value of the property. The */ public val propertyMapper: ((Property?, CodeStyleValue) -> T?)? = null, - public val propertyWriter: (T) -> String = { it.toString() } + public val propertyWriter: (T) -> String = { it.toString() }, + + /** + * Optional message to be displayed whenever the value of the property is being retrieved. + */ + internal val deprecationWarning: String? = null ) } @@ -207,6 +216,10 @@ public object DefaultEditorConfigProperties : UsesEditorConfigProperties { defaultAndroidValue = android ) + @Deprecated( + message = "Marked for removal in KtLint 0.48", + replaceWith = ReplaceWith("ktlintDisabledRulesProperty") + ) public val disabledRulesProperty: UsesEditorConfigProperties.EditorConfigProperty = UsesEditorConfigProperties.EditorConfigProperty( type = PropertyType.LowerCasingPropertyType( @@ -216,6 +229,30 @@ public object DefaultEditorConfigProperties : UsesEditorConfigProperties { emptySet() ), defaultValue = "", + propertyMapper = { property, _ -> + when { + property?.isUnset == true -> "" + property?.getValueAs() != null -> { + // Remove spaces (most likely they occur only around the comma) as they otherwise will be seen + // as part of the rule-id which is to be disabled. But as the space is not allowed in the id's + // of rule sets and rule ids, they are just removed all. + property.getValueAs().replace(" ", "") + } + else -> property?.getValueAs() + } + }, + deprecationWarning = "Rename property 'disabled_rules' to 'ktlint_disabled_rules' in all '.editorconfig' files." + ) + + public val ktlintDisabledRulesProperty: UsesEditorConfigProperties.EditorConfigProperty = + UsesEditorConfigProperties.EditorConfigProperty( + type = PropertyType.LowerCasingPropertyType( + "ktlint_disabled_rules", + "A comma separated list of rule ids which should not be run. For rules not defined in the 'standard' ruleset, the qualified rule-id should be used.", + PropertyType.PropertyValueParser.IDENTITY_VALUE_PARSER, + emptySet() + ), + defaultValue = "", propertyMapper = { property, _ -> when { property?.isUnset == true -> "" diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/VisitorProvider.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/VisitorProvider.kt index c0be58966c..7e1f458ab4 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/VisitorProvider.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/VisitorProvider.kt @@ -4,6 +4,7 @@ import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleRunner import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.disabledRulesProperty +import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.ktlintDisabledRulesProperty import com.pinterest.ktlint.core.api.EditorConfigProperties import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.core.api.UsesEditorConfigProperties.EditorConfigProperty @@ -22,7 +23,10 @@ internal class VisitorProvider( */ recreateRuleSorter: Boolean = false ) : UsesEditorConfigProperties { - override val editorConfigProperties: List> = listOf(disabledRulesProperty) + override val editorConfigProperties: List> = listOf( + ktlintDisabledRulesProperty, + disabledRulesProperty + ) /** * The list of [ruleRunners] is sorted based on the [Rule.VisitorModifier] of the rules. @@ -80,12 +84,21 @@ internal class VisitorProvider( } } - private fun isNotDisabled(editorConfigProperties: EditorConfigProperties, qualifiedRuleId: String): Boolean = - editorConfigProperties - .getEditorConfigValue(disabledRulesProperty) + private fun isNotDisabled(editorConfigProperties: EditorConfigProperties, qualifiedRuleId: String): Boolean { + val ktlintDisabledRulesProperty = + if (editorConfigProperties.containsKey(ktlintDisabledRulesProperty.type.name) || + !editorConfigProperties.containsKey(disabledRulesProperty.type.name) + ) { + // New property takes precedence when defined, or, when both old and new property are not defined. + editorConfigProperties.getEditorConfigValue(ktlintDisabledRulesProperty) + } else { + editorConfigProperties.getEditorConfigValue(disabledRulesProperty) + } + return ktlintDisabledRulesProperty .split(",") .none { // The rule set id in the disabled_rules setting may be omitted for rules in the standard rule set it.toQualifiedRuleId() == qualifiedRuleId } + } } diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/DisabledRulesTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/DisabledRulesTest.kt index b179905141..013d9a6d2c 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/DisabledRulesTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/DisabledRulesTest.kt @@ -1,10 +1,12 @@ package com.pinterest.ktlint.core import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.disabledRulesProperty +import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.ktlintDisabledRulesProperty import com.pinterest.ktlint.core.api.EditorConfigOverride import com.pinterest.ktlint.core.ast.ElementType import org.assertj.core.api.Assertions.assertThat import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource @@ -31,71 +33,145 @@ class DisabledRulesTest { ) } - @ParameterizedTest(name = "RuleId: {0}, Disabled ruleId: {1}") - @CsvSource( - value = [ - "no-var,no-var", - "no-var,standard:no-var", - "standard:no-var,no-var", - "standard:no-var,standard:no-var", - "experimental:no-var,experimental:no-var", - "custom:no-var,custom:no-var" - ] - ) - fun `Given some code and a disabled standard rule then no violation is reported`( - ruleId: String, - disabledRuleId: String - ) { - assertThat( - ArrayList().apply { - KtLint.lint( - KtLint.ExperimentalParams( - text = "var foo", - ruleProviders = setOf( - RuleProvider { NoVarRule(ruleId) } - ), - cb = { e, _ -> add(e) }, - editorConfigOverride = EditorConfigOverride.from(disabledRulesProperty to disabledRuleId) + @Nested + @Deprecated("To be removed when deprecated disabledRulesProperty is removed") + inner class DisabledRulesProperty { + @ParameterizedTest(name = "RuleId: {0}, Disabled ruleId: {1}") + @CsvSource( + value = [ + "no-var,no-var", + "no-var,standard:no-var", + "standard:no-var,no-var", + "standard:no-var,standard:no-var", + "experimental:no-var,experimental:no-var", + "custom:no-var,custom:no-var" + ] + ) + fun `Given some code and a disabled standard rule then no violation is reported`( + ruleId: String, + disabledRuleId: String + ) { + assertThat( + ArrayList().apply { + KtLint.lint( + KtLint.ExperimentalParams( + text = "var foo", + ruleProviders = setOf( + RuleProvider { NoVarRule(ruleId) } + ), + cb = { e, _ -> add(e) }, + editorConfigOverride = EditorConfigOverride.from(disabledRulesProperty to disabledRuleId) + ) ) - ) - } - ).isEmpty() - } + } + ).isEmpty() + } - @Test - fun `Given some code and a disabled standard rule then no violation is reported`() { - assertThat( - ArrayList().apply { - KtLint.lint( - KtLint.ExperimentalParams( - text = "var foo", - ruleProviders = setOf( - RuleProvider { NoVarRule("no-var") } - ), - cb = { e, _ -> add(e) }, - editorConfigOverride = EditorConfigOverride.from(disabledRulesProperty to "no-var") + @Test + fun `Given some code and a disabled standard rule then no violation is reported`() { + assertThat( + ArrayList().apply { + KtLint.lint( + KtLint.ExperimentalParams( + text = "var foo", + ruleProviders = setOf( + RuleProvider { NoVarRule("no-var") } + ), + cb = { e, _ -> add(e) }, + editorConfigOverride = EditorConfigOverride.from(disabledRulesProperty to "no-var") + ) ) - ) - } - ).isEmpty() + } + ).isEmpty() + } + + @Test + fun `Given some code and a disabled experimental rule then no violation is reported`() { + assertThat( + ArrayList().apply { + KtLint.lint( + KtLint.ExperimentalParams( + text = "var foo", + ruleProviders = setOf( + RuleProvider { NoVarRule("experimental:no-var") } + ), + cb = { e, _ -> add(e) }, + editorConfigOverride = EditorConfigOverride.from(disabledRulesProperty to "experimental:no-var") + ) + ) + } + ).isEmpty() + } } - @Test - fun `Given some code and a disabled experimental rule then no violation is reported`() { - assertThat( - ArrayList().apply { - KtLint.lint( - KtLint.ExperimentalParams( - text = "var foo", - ruleProviders = setOf( - RuleProvider { NoVarRule("experimental:no-var") } - ), - cb = { e, _ -> add(e) }, - editorConfigOverride = EditorConfigOverride.from(disabledRulesProperty to "experimental:no-var") + @Nested + inner class KtlintDisabledRulesProperty { + @ParameterizedTest(name = "RuleId: {0}, Disabled ruleId: {1}") + @CsvSource( + value = [ + "no-var,no-var", + "no-var,standard:no-var", + "standard:no-var,no-var", + "standard:no-var,standard:no-var", + "experimental:no-var,experimental:no-var", + "custom:no-var,custom:no-var" + ] + ) + fun `Given some code and a disabled standard rule then no violation is reported`( + ruleId: String, + disabledRuleId: String + ) { + assertThat( + ArrayList().apply { + KtLint.lint( + KtLint.ExperimentalParams( + text = "var foo", + ruleProviders = setOf( + RuleProvider { NoVarRule(ruleId) } + ), + cb = { e, _ -> add(e) }, + editorConfigOverride = EditorConfigOverride.from(ktlintDisabledRulesProperty to disabledRuleId) + ) ) - ) - } - ).isEmpty() + } + ).isEmpty() + } + + @Test + fun `Given some code and a disabled standard rule then no violation is reported`() { + assertThat( + ArrayList().apply { + KtLint.lint( + KtLint.ExperimentalParams( + text = "var foo", + ruleProviders = setOf( + RuleProvider { NoVarRule("no-var") } + ), + cb = { e, _ -> add(e) }, + editorConfigOverride = EditorConfigOverride.from(ktlintDisabledRulesProperty to "no-var") + ) + ) + } + ).isEmpty() + } + + @Test + fun `Given some code and a disabled experimental rule then no violation is reported`() { + assertThat( + ArrayList().apply { + KtLint.lint( + KtLint.ExperimentalParams( + text = "var foo", + ruleProviders = setOf( + RuleProvider { NoVarRule("experimental:no-var") } + ), + cb = { e, _ -> add(e) }, + editorConfigOverride = EditorConfigOverride.from(ktlintDisabledRulesProperty to "experimental:no-var") + ) + ) + } + ).isEmpty() + } } class NoVarRule(id: String) : Rule(id) { diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt index bba9498f0d..dfe5aa8381 100644 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt +++ b/ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt @@ -2,8 +2,6 @@ package com.pinterest.ktlint -import com.pinterest.ktlint.internal.ApplyToIDEAGloballySubCommand -import com.pinterest.ktlint.internal.ApplyToIDEAProjectSubCommand import com.pinterest.ktlint.internal.GenerateEditorConfigSubCommand import com.pinterest.ktlint.internal.GitPreCommitHookSubCommand import com.pinterest.ktlint.internal.GitPrePushHookSubCommand @@ -18,8 +16,6 @@ fun main(args: Array) { .addSubcommand(GitPreCommitHookSubCommand.COMMAND_NAME, GitPreCommitHookSubCommand()) .addSubcommand(GitPrePushHookSubCommand.COMMAND_NAME, GitPrePushHookSubCommand()) .addSubcommand(PrintASTSubCommand.COMMAND_NAME, PrintASTSubCommand()) - .addSubcommand(ApplyToIDEAGloballySubCommand.COMMAND_NAME, ApplyToIDEAGloballySubCommand()) - .addSubcommand(ApplyToIDEAProjectSubCommand.COMMAND_NAME, ApplyToIDEAProjectSubCommand()) .addSubcommand(GenerateEditorConfigSubCommand.COMMAND_NAME, GenerateEditorConfigSubCommand()) val parseResult = commandLine.parseArgs(*args) @@ -40,8 +36,6 @@ private fun handleSubCommand( is GitPreCommitHookSubCommand -> subCommand.run() is GitPrePushHookSubCommand -> subCommand.run() is PrintASTSubCommand -> subCommand.run() - is ApplyToIDEAGloballySubCommand -> subCommand.run() - is ApplyToIDEAProjectSubCommand -> subCommand.run() is GenerateEditorConfigSubCommand -> subCommand.run() else -> commandLine.usage(System.out, CommandLine.Help.Ansi.OFF) } diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEACommandHelper.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEACommandHelper.kt deleted file mode 100644 index 59e9d389be..0000000000 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEACommandHelper.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.pinterest.ktlint.internal - -import com.pinterest.ktlint.core.initKtLintKLogger -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.system.exitProcess -import mu.KotlinLogging - -private val logger = KotlinLogging.logger {}.initKtLintKLogger() - -class ApplyToIDEACommandHelper( - private val applyToProject: Boolean, - private val forceApply: Boolean, - private val isAndroidCodeStyle: Boolean -) { - fun apply() { - try { - val workDir = Paths.get(".") - - if (!forceApply && !getUserAcceptanceToUpdateFiles(workDir)) { - logger.error { "Update canceled." } - exitProcess(1) - } - - IntellijIDEAIntegration.apply( - workDir, - false, - isAndroidCodeStyle, - applyToProject - ) - } catch (e: IntellijIDEAIntegration.ProjectNotFoundException) { - logger.error { ".idea directory not found. Are you sure you are inside project root directory?" } - exitProcess(1) - } - - logger.info { - """ - |Updated. - |Please restart your IDE. - |If you experience any issues please report them at https://github.com/pinterest/ktlint/issues. - """.trimMargin() - } - } - - private fun getUserAcceptanceToUpdateFiles(workDir: Path): Boolean { - val fileList = IntellijIDEAIntegration.apply( - workDir, - true, - isAndroidCodeStyle, - applyToProject - ) - logger.info { - """ - |The following files are going to be updated: - |${fileList.joinToString(prefix = "\t", separator = "\n\t")} - | - |Do you wish to proceed? [y/n] - |(in future, use -y flag if you wish to skip confirmation) - """.trimMargin() - } - - val userInput = generateSequence { readLine() } - .filter { it.trim().isNotBlank() } - .first() - - return "y".equals(userInput, ignoreCase = true) - } -} diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAGloballySubCommand.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAGloballySubCommand.kt deleted file mode 100644 index c46f5c5567..0000000000 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAGloballySubCommand.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.pinterest.ktlint.internal - -import picocli.CommandLine - -@CommandLine.Command( - description = [ - "Update Intellij IDEA Kotlin codestyle settings (global)" - ], - aliases = ["--apply-to-idea"], - mixinStandardHelpOptions = true, - versionProvider = KtlintVersionProvider::class -) -class ApplyToIDEAGloballySubCommand : Runnable { - @CommandLine.ParentCommand - private lateinit var ktlintCommand: KtlintCommandLine - - @CommandLine.Spec - private lateinit var commandSpec: CommandLine.Model.CommandSpec - - @CommandLine.Option( - names = ["-y"], - description = ["Overwrite existing Kotlin codestyle settings without asking"] - ) - private var forceApply: Boolean = false - - override fun run() { - commandSpec.commandLine().printCommandLineHelpOrVersionUsage() - - ApplyToIDEACommandHelper( - false, - forceApply, - ktlintCommand.android - ).apply() - } - - companion object { - const val COMMAND_NAME = "applyToIDEA" - } -} diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAProjectSubCommand.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAProjectSubCommand.kt deleted file mode 100644 index ea54c72959..0000000000 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ApplyToIDEAProjectSubCommand.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.pinterest.ktlint.internal - -import picocli.CommandLine - -@CommandLine.Command( - description = [ - "Update Intellij IDEA project settings" - ], - aliases = ["--apply-to-idea-project"], - mixinStandardHelpOptions = true, - versionProvider = KtlintVersionProvider::class -) -class ApplyToIDEAProjectSubCommand : Runnable { - @CommandLine.ParentCommand - private lateinit var ktlintCommand: KtlintCommandLine - - @CommandLine.Spec - private lateinit var commandSpec: CommandLine.Model.CommandSpec - - @CommandLine.Option( - names = ["-y"], - description = ["Overwrite existing Kotlin codestyle settings without asking"] - ) - private var forceApply: Boolean = false - - override fun run() { - commandSpec.commandLine().printCommandLineHelpOrVersionUsage() - - ApplyToIDEACommandHelper( - true, - forceApply, - ktlintCommand.android - ).apply() - } - - companion object { - const val COMMAND_NAME = "applyToIDEAProject" - } -} diff --git a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt b/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt deleted file mode 100644 index d864bb0157..0000000000 --- a/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/IntellijIDEAIntegration.kt +++ /dev/null @@ -1,260 +0,0 @@ -package com.pinterest.ktlint.internal - -import com.github.shyiko.klob.Glob -import com.pinterest.ktlint.core.internal.EditorConfigLoader -import com.pinterest.ktlint.core.internal.EditorConfigLoader.Companion.convertToRawValues -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.nio.charset.Charset -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.NoSuchFileException -import java.nio.file.Path -import java.nio.file.Paths -import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.transform.OutputKeys -import javax.xml.transform.TransformerFactory -import javax.xml.transform.dom.DOMSource -import javax.xml.transform.stream.StreamResult -import javax.xml.xpath.XPathConstants -import javax.xml.xpath.XPathFactory -import org.w3c.dom.Element - -object IntellijIDEAIntegration { - - @Suppress("UNUSED_PARAMETER") - @Throws(IOException::class) - fun apply(workDir: Path, dryRun: Boolean, android: Boolean = false, local: Boolean = false): Array { - val editorConfigProperties = EditorConfigLoader(FileSystems.getDefault()) - .loadPropertiesForFile(null, isStdIn = true, rules = emptySet()) - val editorConfig: Map = editorConfigProperties.convertToRawValues() - val indentSize = editorConfig["indent_size"]?.toIntOrNull() ?: 4 - val continuationIndentSize = editorConfig["continuation_indent_size"]?.toIntOrNull() ?: 4 - val updates = if (local) { - if (!Files.isDirectory(workDir.resolve(".idea"))) { - throw ProjectNotFoundException() - } - listOf( - Paths.get(workDir.toString(), ".idea", "codeStyles", "codeStyleConfig.xml") to - overwriteWithResource("/project-config/.idea/codeStyles/codeStyleConfig.xml"), - Paths.get(workDir.toString(), ".idea", "codeStyles", "Project.xml") to - overwriteWithResource("/project-config/.idea/codeStyles/Project.xml") { resource -> - resource - .replace( - "option name=\"INDENT_SIZE\" value=\"4\"", - "option name=\"INDENT_SIZE\" value=\"$indentSize\"" - ) - .replace( - "option name=\"CONTINUATION_INDENT_SIZE\" value=\"8\"", - "option name=\"CONTINUATION_INDENT_SIZE\" value=\"$continuationIndentSize\"" - ) - }, - Paths.get(workDir.toString(), ".idea", "inspectionProfiles", "profiles_settings.xml") to - overwriteWithResource("/project-config/.idea/inspectionProfiles/profiles_settings.xml"), - Paths.get(workDir.toString(), ".idea", "inspectionProfiles", "ktlint.xml") to - overwriteWithResource("/project-config/.idea/inspectionProfiles/ktlint.xml"), - Paths.get(workDir.toString(), ".idea", "workspace.xml").let { src -> - src to { - var arr = "".toByteArray() - try { - arr = Files.readAllBytes(Paths.get(workDir.toString(), ".idea", "workspace.xml")) - } catch (e: IOException) { - if (e !is NoSuchFileException) { - throw e - } - } - try { - enableOptimizeImportsOnTheFly(arr) - } catch (e: Exception) { - throw IOException("Failed to enable \"Optimize imports on the fly\" ($src)", e) - } - } - } - ) - } else { - val home = System.getProperty("user.home") - val codeStyleName = "ktlint${ - if (continuationIndentSize == 4) "" else "-cis$continuationIndentSize" - }${ - if (indentSize == 4) "" else "-is$indentSize" - }" - val paths = - // macOS for 2019.3.x and below - Glob.from("IntelliJIdea*", "IdeaIC*", "AndroidStudio*") - .iterate( - Paths.get(home, "Library", "Preferences"), - Glob.IterationOption.SKIP_CHILDREN, - Glob.IterationOption.DIRECTORY - ).asSequence() + - // macOS for 2020.1.x and above - Glob.from("IntelliJIdea*", "IdeaIC*", "AndroidStudio*") - .iterate( - Paths.get(home, "Library", "Application Support", "JetBrains"), - Glob.IterationOption.SKIP_CHILDREN, - Glob.IterationOption.DIRECTORY - ).asSequence() + - // linux/windows - Glob.from(".IntelliJIdea*/config", ".IdeaIC*/config", ".AndroidStudio*/config") - .iterate( - Paths.get(home), - Glob.IterationOption.SKIP_CHILDREN, - Glob.IterationOption.DIRECTORY - ).asSequence() - ( - paths.flatMap { dir -> - sequenceOf( - Paths.get(dir.toString(), "codestyles", "$codeStyleName.xml") to - overwriteWithResource("/config/codestyles/ktlint.xml") { resource -> - resource - .replace( - "code_scheme name=\"ktlint\"", - "code_scheme name=\"$codeStyleName\"" - ) - .replace( - "option name=\"INDENT_SIZE\" value=\"4\"", - "option name=\"INDENT_SIZE\" value=\"$indentSize\"" - ) - .replace( - "option name=\"CONTINUATION_INDENT_SIZE\" value=\"8\"", - "option name=\"CONTINUATION_INDENT_SIZE\" value=\"$continuationIndentSize\"" - ) - }, - Paths.get(dir.toString(), "options", "code.style.schemes.xml") to - overwriteWithResource("/config/options/code.style.schemes.xml") { content -> - content - .replace( - "option name=\"CURRENT_SCHEME_NAME\" value=\"ktlint\"", - "option name=\"CURRENT_SCHEME_NAME\" value=\"$codeStyleName\"" - ) - }, - Paths.get(dir.toString(), "inspection", "ktlint.xml") to - overwriteWithResource("/config/inspection/ktlint.xml"), - Paths.get(dir.toString(), "options", "editor.codeinsight.xml").let { src -> - src to { - var arr = "".toByteArray() - try { - arr = Files.readAllBytes(src) - } catch (e: IOException) { - if (e !is NoSuchFileException) { - throw e - } - } - try { - enableOptimizeImportsOnTheFlyInsideWorkspace(arr) - } catch (e: Exception) { - throw IOException("Failed to enable \"Optimize imports on the fly\" ($src)", e) - } - } - } - ) - } + sequenceOf( - Paths.get(workDir.toString(), ".idea", "codeStyleSettings.xml") to - overwriteWithResource("/config/.idea/codeStyleSettings.xml") { content -> - content.replace( - "option name=\"PREFERRED_PROJECT_CODE_STYLE\" value=\"ktlint\"", - "option name=\"PREFERRED_PROJECT_CODE_STYLE\" value=\"$codeStyleName\"" - ) - }, - Paths.get(workDir.toString(), ".idea", "inspectionProfiles", "profiles_settings.xml") to - overwriteWithResource("/config/.idea/inspectionProfiles/profiles_settings.xml") - ) - ).toList() - } - if (!dryRun) { - updates.forEach { (path, contentSupplier) -> - Files.createDirectories(path.parent) - Files.write(path, contentSupplier()) - } - } - return updates.map { (path) -> path }.toTypedArray() - } - - private fun overwriteWithResource(resource: String, transformer: ((String) -> String) = { it }): () -> ByteArray = { - transformer(getResourceText(resource)).toByteArray(charset("UTF-8")) - } - - private fun enableOptimizeImportsOnTheFly(arr: ByteArray): ByteArray { - /* - * - * - * - * ... - * - */ - val doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(ByteArrayInputStream(arr)) - val xpath = XPathFactory.newInstance().newXPath() - var cis = xpath.evaluate( - "//component[@name='CodeInsightSettings']", - doc, - XPathConstants.NODE - ) as Element? - if (cis == null) { - cis = doc.createElement("component") - cis.setAttribute("name", "CodeInsightSettings") - cis = doc.documentElement.appendChild(cis) as Element - } - var oiotf = xpath.evaluate( - "//option[@name='OPTIMIZE_IMPORTS_ON_THE_FLY']", - cis, - XPathConstants.NODE - ) as Element? - if (oiotf == null) { - oiotf = doc.createElement("option") - oiotf.setAttribute("name", "OPTIMIZE_IMPORTS_ON_THE_FLY") - oiotf = cis.appendChild(oiotf) as Element - } - oiotf.setAttribute("value", "true") - val transformer = TransformerFactory.newInstance().newTransformer() - val out = ByteArrayOutputStream() - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes") - transformer.transform(DOMSource(doc), StreamResult(out)) - return out.toByteArray() - } - - private fun enableOptimizeImportsOnTheFlyInsideWorkspace(arr: ByteArray): ByteArray { - /* - * - * - * - * ... - * - */ - val doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(ByteArrayInputStream(arr)) - val xpath = XPathFactory.newInstance().newXPath() - var cis = xpath.evaluate( - "//component[@name='CodeInsightWorkspaceSettings']", - doc, - XPathConstants.NODE - ) as Element? - if (cis == null) { - cis = doc.createElement("component") - cis.setAttribute("name", "CodeInsightWorkspaceSettings") - cis = doc.documentElement.appendChild(cis) as Element - } - var oiotf = xpath.evaluate( - "//option[@name='optimizeImportsOnTheFly']", - cis, - XPathConstants.NODE - ) as Element? - if (oiotf == null) { - oiotf = doc.createElement("option") - oiotf.setAttribute("name", "optimizeImportsOnTheFly") - oiotf = cis.appendChild(oiotf) as Element - } - oiotf.setAttribute("value", "true") - val transformer = TransformerFactory.newInstance().newTransformer() - val out = ByteArrayOutputStream() - transformer.transform(DOMSource(doc), StreamResult(out)) - return out.toByteArray() - } - - private fun getResourceText(name: String) = - this::class.java.getResourceAsStream(name).readBytes().toString(Charset.forName("UTF-8")) - - class ProjectNotFoundException : RuntimeException() -} diff --git a/mkdocs.yml b/mkdocs.yml index 6984192ecd..e0b58c29d8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,7 +40,8 @@ nav: - Rules: - Standard rules: rules/standard.md - Experimental rules: rules/experimental.md - - Configuration: rules/configuration.md + - KtLint configuration: rules/configuration-ktlint.md + - IntelliJ IDEA configuration: rules/configuration-intellij-idea.md - Extensions: - Custom rule set: extensions/custom-rule-set.md - Custom reporter: extensions/custom-reporter.md