Skip to content

Commit 3bafa6a

Browse files
committed
Support ktlint 0.50.0 and drop support for 0.47.0
Closes #1741
1 parent d245b60 commit 3bafa6a

File tree

14 files changed

+299
-181
lines changed

14 files changed

+299
-181
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ This document is intended for Spotless developers.
1010
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
1111

1212
## [Unreleased]
13+
### Added
14+
* Bump default `ktlint` version to latest `0.49.1` -> `0.50.0`.([#1741](https://github.com/diffplug/spotless/issues/1741))
15+
* Dropped support for `ktlint 0.47.x` following our policy of supporting two breaking changes at a time.
1316
### Changes
1417
* Bump default `cleanthat` version to latest `2.13` -> `2.16`. ([#1725](https://github.com/diffplug/spotless/pull/1725))
1518
### Fixed

lib/build.gradle

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ versionCompatibility {
4444
// we will support no more than 2 breaking changes at a time = 3 incompatible versions
4545
// we will try to drop down to only one version if a stable API can be maintained for a full year
4646
versions = [
47-
'0.47.0',
4847
'0.48.0',
4948
'0.49.0',
49+
'0.50.0',
5050
]
5151
targetSourceSetName = 'ktlint'
5252
}
@@ -95,16 +95,18 @@ dependencies {
9595
strictly '1.7' // for JDK 8 compatibility
9696
}
9797
}
98-
// ktlint
99-
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-core:0.47.0'
100-
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.47.0'
101-
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.47.0'
98+
// ktlint oldest supported version
10299
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-core:0.48.0'
103100
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.48.0'
104101
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.48.0'
102+
// ktlint previous supported version
105103
compatKtLint0Dot49Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-rule-engine:0.49.0'
106104
compatKtLint0Dot49Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.49.0'
107105
compatKtLint0Dot49Dot0CompileAndTestOnly 'org.slf4j:slf4j-api:2.0.0'
106+
// ktlint latest supported version
107+
compatKtLint0Dot50Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-rule-engine:0.50.0'
108+
compatKtLint0Dot50Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.50.0'
109+
compatKtLint0Dot50Dot0CompileAndTestOnly 'org.slf4j:slf4j-api:2.0.0'
108110
// palantirJavaFormat
109111
palantirJavaFormatCompileOnly 'com.palantir.javaformat:palantir-java-format:1.1.0' // this version needs to stay compilable against Java 8 for CI Job testNpm
110112
// scalafmt

lib/src/compatKtLint0Dot47Dot0/java/com/diffplug/spotless/glue/ktlint/compat/KtLintCompat0Dot47Dot0Adapter.java

Lines changed: 0 additions & 139 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* Copyright 2023 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.glue.ktlint.compat;
17+
18+
import java.nio.file.Files;
19+
import java.nio.file.Path;
20+
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.LinkedHashSet;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Objects;
26+
import java.util.Set;
27+
import java.util.stream.Collectors;
28+
import java.util.stream.Stream;
29+
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
33+
import com.pinterest.ktlint.rule.engine.api.Code;
34+
import com.pinterest.ktlint.rule.engine.api.EditorConfigDefaults;
35+
import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride;
36+
import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine;
37+
import com.pinterest.ktlint.rule.engine.api.LintError;
38+
import com.pinterest.ktlint.rule.engine.core.api.Rule;
39+
import com.pinterest.ktlint.rule.engine.core.api.RuleId;
40+
import com.pinterest.ktlint.rule.engine.core.api.RuleProvider;
41+
import com.pinterest.ktlint.rule.engine.core.api.RuleSetId;
42+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleEditorConfigPropertyKt;
43+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty;
44+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EndOfLinePropertyKt;
45+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.IndentSizeEditorConfigPropertyKt;
46+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.IndentStyleEditorConfigPropertyKt;
47+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.InsertFinalNewLineEditorConfigPropertyKt;
48+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MaxLineLengthEditorConfigPropertyKt;
49+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecution;
50+
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecutionEditorConfigPropertyKt;
51+
import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider;
52+
53+
import kotlin.Pair;
54+
import kotlin.Unit;
55+
import kotlin.jvm.functions.Function2;
56+
57+
public class KtLintCompat0Dot50Dot0Adapter implements KtLintCompatAdapter {
58+
59+
private static final Logger logger = LoggerFactory.getLogger(KtLintCompat0Dot50Dot0Adapter.class);
60+
61+
private static final List<EditorConfigProperty<?>> DEFAULT_EDITOR_CONFIG_PROPERTIES;
62+
63+
static {
64+
List<EditorConfigProperty<?>> list = new ArrayList<>();
65+
list.add(CodeStyleEditorConfigPropertyKt.getCODE_STYLE_PROPERTY());
66+
list.add(EndOfLinePropertyKt.getEND_OF_LINE_PROPERTY());
67+
list.add(IndentSizeEditorConfigPropertyKt.getINDENT_SIZE_PROPERTY());
68+
list.add(IndentStyleEditorConfigPropertyKt.getINDENT_STYLE_PROPERTY());
69+
list.add(InsertFinalNewLineEditorConfigPropertyKt.getINSERT_FINAL_NEWLINE_PROPERTY());
70+
list.add(MaxLineLengthEditorConfigPropertyKt.getMAX_LINE_LENGTH_PROPERTY());
71+
list.add(RuleExecutionEditorConfigPropertyKt.getEXPERIMENTAL_RULES_EXECUTION_PROPERTY());
72+
DEFAULT_EDITOR_CONFIG_PROPERTIES = Collections.unmodifiableList(list);
73+
}
74+
75+
static class FormatterCallback implements Function2<LintError, Boolean, Unit> {
76+
77+
@Override
78+
public Unit invoke(LintError lint, Boolean corrected) {
79+
if (!corrected) {
80+
KtLintCompatReporting.report(lint.getLine(), lint.getCol(), lint.getRuleId().getValue(), lint.getDetail());
81+
}
82+
return Unit.INSTANCE;
83+
}
84+
}
85+
86+
@Override
87+
public String format(final String text, Path path, final boolean isScript,
88+
final boolean useExperimental,
89+
Path editorConfigPath, final Map<String, String> userData,
90+
final Map<String, Object> editorConfigOverrideMap) {
91+
final FormatterCallback formatterCallback = new FormatterCallback();
92+
93+
Set<RuleProvider> allRuleProviders = new LinkedHashSet<>(
94+
new StandardRuleSetProvider().getRuleProviders());
95+
96+
// TODO: Should we keep `useExperimental` now that ktlint uses an EditorConfig property for this purpose?
97+
if (useExperimental) {
98+
String experimentalRulesPropertyName = RuleExecutionEditorConfigPropertyKt.getEXPERIMENTAL_RULES_EXECUTION_PROPERTY().getName();
99+
Object experimentalOverride = editorConfigOverrideMap.get(experimentalRulesPropertyName);
100+
if (experimentalOverride != null) {
101+
logger.warn("`useExperimental` parameter is `true` and `ktlint_experimental` property is set, `useExperimental` will take priority!");
102+
editorConfigOverrideMap.put(experimentalRulesPropertyName, "enabled");
103+
}
104+
}
105+
106+
EditorConfigOverride editorConfigOverride;
107+
if (editorConfigOverrideMap.isEmpty()) {
108+
editorConfigOverride = EditorConfigOverride.Companion.getEMPTY_EDITOR_CONFIG_OVERRIDE();
109+
} else {
110+
editorConfigOverride = createEditorConfigOverride(allRuleProviders.stream().map(
111+
RuleProvider::createNewRuleInstance).collect(Collectors.toList()),
112+
editorConfigOverrideMap);
113+
}
114+
EditorConfigDefaults editorConfig;
115+
if (editorConfigPath == null || !Files.exists(editorConfigPath)) {
116+
editorConfig = EditorConfigDefaults.Companion.getEMPTY_EDITOR_CONFIG_DEFAULTS();
117+
} else {
118+
editorConfig = EditorConfigDefaults.Companion.load(editorConfigPath, Collections.emptySet());
119+
}
120+
121+
return new KtLintRuleEngine(
122+
allRuleProviders,
123+
editorConfig,
124+
editorConfigOverride,
125+
true,
126+
false,
127+
path.getFileSystem())
128+
.format(Code.Companion.fromPath(path), formatterCallback);
129+
}
130+
131+
/**
132+
* Create EditorConfigOverride from user provided parameters.
133+
*/
134+
private static EditorConfigOverride createEditorConfigOverride(final List<Rule> rules, Map<String, Object> editorConfigOverrideMap) {
135+
// Get properties from rules in the rule sets
136+
Stream<EditorConfigProperty<?>> ruleProperties = rules.stream()
137+
.flatMap(rule -> rule.getUsesEditorConfigProperties().stream());
138+
139+
// Create a mapping of properties to their names based on rule properties and default properties
140+
Map<String, EditorConfigProperty<?>> supportedProperties = Stream
141+
.concat(ruleProperties, DEFAULT_EDITOR_CONFIG_PROPERTIES.stream())
142+
.distinct()
143+
.collect(Collectors.toMap(EditorConfigProperty::getName, property -> property));
144+
145+
// Create config properties based on provided property names and values
146+
@SuppressWarnings("unchecked")
147+
Pair<EditorConfigProperty<?>, ?>[] properties = editorConfigOverrideMap.entrySet().stream()
148+
.map(entry -> {
149+
EditorConfigProperty<?> property = supportedProperties.get(entry.getKey());
150+
if (property != null) {
151+
return new Pair<>(property, entry.getValue());
152+
} else if (entry.getKey().startsWith("ktlint_")) {
153+
String[] parts = entry.getKey().substring(7).split("_", 2);
154+
if (parts.length == 1) {
155+
// convert ktlint_{ruleset} to RuleSetId
156+
RuleSetId id = new RuleSetId(parts[0]);
157+
property = RuleExecutionEditorConfigPropertyKt.createRuleSetExecutionEditorConfigProperty(id, RuleExecution.disabled);
158+
} else {
159+
// convert ktlint_{ruleset}_{rulename} to RuleId
160+
RuleId id = new RuleId(parts[0] + ":" + parts[1]);
161+
property = RuleExecutionEditorConfigPropertyKt.createRuleExecutionEditorConfigProperty(id, RuleExecution.disabled);
162+
}
163+
return new Pair<>(property, entry.getValue());
164+
} else {
165+
return null;
166+
}
167+
})
168+
.filter(Objects::nonNull)
169+
.toArray(Pair[]::new);
170+
171+
return EditorConfigOverride.Companion.from(properties);
172+
}
173+
}

lib/src/ktlint/java/com/diffplug/spotless/glue/ktlint/KtlintFormatterFunc.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,19 @@ public class KtlintFormatterFunc implements FormatterFunc.NeedsFile {
3535
public KtlintFormatterFunc(String version, boolean isScript, boolean useExperimental, FileSignature editorConfigPath, Map<String, String> userData,
3636
Map<String, Object> editorConfigOverrideMap) {
3737
int minorVersion = Integer.parseInt(version.split("\\.")[1]);
38-
if (minorVersion >= 49) {
38+
if (minorVersion >= 50) {
39+
// Fixed `RuleId` and `RuleSetId` issues
40+
// New argument to `EditorConfigDefaults.Companion.load(...)` for custom property type parsing
41+
// New argument to `new KtLintRuleEngine(...)` to fail on usage of `treeCopyHandler` extension point
42+
this.adapter = new KtLintCompat0Dot50Dot0Adapter();
43+
} else if (minorVersion == 49) {
3944
// Packages and modules moved around (`ktlint-core` -> `ktlint-rule-engine`)
4045
// Experimental ruleset was replaced by implementing `Rule.Experimental` and checking the `ktlint_experimental` `.editorconfig` property
4146
// `RuleId` and `RuleSetId` became inline classes (mangled to be unrepresentable in Java source code, so reflection is needed), tracked here: https://github.com/pinterest/ktlint/issues/2041
4247
this.adapter = new KtLintCompat0Dot49Dot0Adapter();
43-
} else if (minorVersion == 48) {
48+
} else {
4449
// ExperimentalParams lost two constructor arguments, EditorConfigProperty moved to its own class
4550
this.adapter = new KtLintCompat0Dot48Dot0Adapter();
46-
} else {
47-
// rename RuleSet to RuleProvider
48-
this.adapter = new KtLintCompat0Dot47Dot0Adapter();
4951
}
5052
this.editorConfigPath = editorConfigPath;
5153
this.useExperimental = useExperimental;

lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class KtLintStep {
3636
// prevent direct instantiation
3737
private KtLintStep() {}
3838

39-
private static final String DEFAULT_VERSION = "0.49.1";
39+
private static final String DEFAULT_VERSION = "0.50.0";
4040
static final String NAME = "ktlint";
4141
static final String PACKAGE = "com.pinterest";
4242
static final String MAVEN_COORDINATE = PACKAGE + ":ktlint:";

0 commit comments

Comments
 (0)