Skip to content

Commit 5cf7a7e

Browse files
committed
Support KtLint 0.49.0, drop KtLint 0.46.0
Currently, we work around name-mangling issues with special Kotlin classes. The root issue is tracked here: pinterest/ktlint#2041 Also fixes the IntelliJ .editorconfig import order property to better match Eclipse formatting
1 parent c2ec7e1 commit 5cf7a7e

File tree

10 files changed

+321
-142
lines changed

10 files changed

+321
-142
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ indent_size = 2
1414

1515
[*.java]
1616
# Doc: https://youtrack.jetbrains.com/issue/IDEA-170643#focus=streamItem-27-3708697.0-0
17-
ij_java_imports_layout = java.**,|,javax.**,|,org.**,|,com.**,|,com.diffplug.**,|,*
17+
ij_java_imports_layout = $*,|,java.**,|,javax.**,|,org.**,|,com.**,|,com.diffplug.**,|,*
1818
ij_java_use_single_class_imports = true
1919
ij_java_class_count_to_use_import_on_demand = 999
2020
ij_java_names_count_to_use_import_on_demand = 999

lib/build.gradle

Lines changed: 5 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.46.0',
4847
'0.47.0',
4948
'0.48.0',
49+
'0.49.0',
5050
]
5151
targetSourceSetName = 'ktlint'
5252
}
@@ -96,20 +96,20 @@ dependencies {
9696
}
9797
}
9898
// ktlint
99-
String VER_KTLINT='0.46.1'
99+
String VER_KTLINT='0.47.1'
100100
ktlintCompileOnly "com.pinterest:ktlint:$VER_KTLINT"
101101
ktlintCompileOnly "com.pinterest.ktlint:ktlint-core:$VER_KTLINT"
102102
ktlintCompileOnly "com.pinterest.ktlint:ktlint-ruleset-experimental:$VER_KTLINT"
103103
ktlintCompileOnly "com.pinterest.ktlint:ktlint-ruleset-standard:$VER_KTLINT"
104-
compatKtLint0Dot46Dot0CompileOnly 'com.pinterest.ktlint:ktlint-core:0.46.0'
105-
compatKtLint0Dot46Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.46.0'
106-
compatKtLint0Dot46Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.46.0'
107104
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-core:0.47.0'
108105
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.47.0'
109106
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.47.0'
110107
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-core:0.48.0'
111108
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.48.0'
112109
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.48.0'
110+
compatKtLint0Dot49Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-rule-engine:0.49.0'
111+
compatKtLint0Dot49Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.49.0'
112+
compatKtLint0Dot49Dot0CompileAndTestOnly 'org.slf4j:slf4j-api:2.0.0'
113113
// palantirJavaFormat
114114
palantirJavaFormatCompileOnly 'com.palantir.javaformat:palantir-java-format:1.1.0' // this version needs to stay compilable against Java 8 for CI Job testNpm
115115
// scalafmt

lib/src/compatKtLint0Dot46Dot0/java/com/diffplug/spotless/glue/ktlint/compat/KtLintCompat0Dot46Dot0Adapter.java

Lines changed: 0 additions & 119 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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.lang.reflect.InvocationTargetException;
19+
import java.lang.reflect.Method;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
22+
import java.util.ArrayList;
23+
import java.util.Collections;
24+
import java.util.LinkedHashSet;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.Objects;
28+
import java.util.Set;
29+
import java.util.stream.Collectors;
30+
import java.util.stream.Stream;
31+
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
34+
35+
import com.pinterest.ktlint.rule.engine.api.Code;
36+
import com.pinterest.ktlint.rule.engine.api.EditorConfigDefaults;
37+
import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride;
38+
import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine;
39+
import com.pinterest.ktlint.rule.engine.api.LintError;
40+
import com.pinterest.ktlint.rule.engine.core.api.Rule;
41+
import com.pinterest.ktlint.rule.engine.core.api.RuleProvider;
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 KtLintCompat0Dot49Dot0Adapter implements KtLintCompatAdapter {
58+
59+
private static final Logger logger = LoggerFactory.getLogger(KtLintCompat0Dot49Dot0Adapter.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+
private static final Method RULEID_METHOD;
76+
private static final Method CREATE_RULESET_EXECUTION_METHOD;
77+
private static final Method CREATE_RULE_EXECUTION_METHOD;
78+
79+
static {
80+
try {
81+
RULEID_METHOD = LintError.class.getMethod("getRuleId-6XN97os");
82+
CREATE_RULESET_EXECUTION_METHOD = RuleExecutionEditorConfigPropertyKt.class.getMethod("createRuleSetExecutionEditorConfigProperty-fqiwTpU", String.class, RuleExecution.class);
83+
CREATE_RULE_EXECUTION_METHOD = RuleExecutionEditorConfigPropertyKt.class.getMethod("createRuleExecutionEditorConfigProperty-U7AdEiY", String.class, RuleExecution.class);
84+
} catch (NoSuchMethodException e) {
85+
throw new RuntimeException(e);
86+
}
87+
}
88+
89+
private static String getRuleId(LintError lint) {
90+
try {
91+
return (String) RULEID_METHOD.invoke(lint);
92+
} catch (IllegalAccessException | InvocationTargetException e) {
93+
throw new RuntimeException(e);
94+
}
95+
}
96+
97+
private static EditorConfigProperty<?> createRuleSetExecution(String id, RuleExecution execution) {
98+
try {
99+
return (EditorConfigProperty<?>) CREATE_RULESET_EXECUTION_METHOD.invoke(null, id, execution);
100+
} catch (IllegalAccessException | InvocationTargetException e) {
101+
throw new RuntimeException(e);
102+
}
103+
}
104+
105+
private static EditorConfigProperty<?> createRuleExecution(String id, RuleExecution execution) {
106+
try {
107+
return (EditorConfigProperty<?>) CREATE_RULE_EXECUTION_METHOD.invoke(null, id, execution);
108+
} catch (IllegalAccessException | InvocationTargetException e) {
109+
throw new RuntimeException(e);
110+
}
111+
}
112+
113+
static class FormatterCallback implements Function2<LintError, Boolean, Unit> {
114+
115+
@Override
116+
public Unit invoke(LintError lint, Boolean corrected) {
117+
if (!corrected) {
118+
KtLintCompatReporting.report(lint.getLine(), lint.getCol(), getRuleId(lint), lint.getDetail());
119+
}
120+
return Unit.INSTANCE;
121+
}
122+
}
123+
124+
@Override
125+
public String format(final String text, Path path, final boolean isScript,
126+
final boolean useExperimental,
127+
Path editorConfigPath, final Map<String, String> userData,
128+
final Map<String, Object> editorConfigOverrideMap) {
129+
final FormatterCallback formatterCallback = new FormatterCallback();
130+
131+
Set<RuleProvider> allRuleProviders = new LinkedHashSet<>(
132+
new StandardRuleSetProvider().getRuleProviders());
133+
134+
// TODO: Should we keep `useExperimental` now that ktlint uses an EditorConfig property for this purpose?
135+
if (useExperimental) {
136+
String experimentalRulesPropertyName = RuleExecutionEditorConfigPropertyKt.getEXPERIMENTAL_RULES_EXECUTION_PROPERTY().getName();
137+
Object experimentalOverride = editorConfigOverrideMap.get(experimentalRulesPropertyName);
138+
if (experimentalOverride != null) {
139+
logger.warn("`useExperimental` parameter is `true` and `ktlint_experimental` property is set, `useExperimental` will take priority!");
140+
editorConfigOverrideMap.put(experimentalRulesPropertyName, "enabled");
141+
}
142+
}
143+
144+
EditorConfigOverride editorConfigOverride;
145+
if (editorConfigOverrideMap.isEmpty()) {
146+
editorConfigOverride = EditorConfigOverride.Companion.getEMPTY_EDITOR_CONFIG_OVERRIDE();
147+
} else {
148+
editorConfigOverride = createEditorConfigOverride(allRuleProviders.stream().map(
149+
RuleProvider::createNewRuleInstance).collect(Collectors.toList()),
150+
editorConfigOverrideMap);
151+
}
152+
EditorConfigDefaults editorConfig;
153+
if (editorConfigPath == null || !Files.exists(editorConfigPath)) {
154+
editorConfig = EditorConfigDefaults.Companion.getEMPTY_EDITOR_CONFIG_DEFAULTS();
155+
} else {
156+
editorConfig = EditorConfigDefaults.Companion.load(editorConfigPath);
157+
}
158+
159+
return new KtLintRuleEngine(
160+
allRuleProviders,
161+
editorConfig,
162+
editorConfigOverride,
163+
false,
164+
path.getFileSystem())
165+
.format(Code.Companion.fromPath(path), formatterCallback);
166+
}
167+
168+
/**
169+
* Create EditorConfigOverride from user provided parameters.
170+
*/
171+
private static EditorConfigOverride createEditorConfigOverride(final List<Rule> rules, Map<String, Object> editorConfigOverrideMap) {
172+
// Get properties from rules in the rule sets
173+
Stream<EditorConfigProperty<?>> ruleProperties = rules.stream()
174+
.flatMap(rule -> rule.getUsesEditorConfigProperties().stream());
175+
176+
// Create a mapping of properties to their names based on rule properties and default properties
177+
Map<String, EditorConfigProperty<?>> supportedProperties = Stream
178+
.concat(ruleProperties, DEFAULT_EDITOR_CONFIG_PROPERTIES.stream())
179+
.distinct()
180+
.collect(Collectors.toMap(EditorConfigProperty::getName, property -> property));
181+
182+
// Create config properties based on provided property names and values
183+
@SuppressWarnings("unchecked")
184+
Pair<EditorConfigProperty<?>, ?>[] properties = editorConfigOverrideMap.entrySet().stream()
185+
.map(entry -> {
186+
EditorConfigProperty<?> property = supportedProperties.get(entry.getKey());
187+
if (property != null) {
188+
return new Pair<>(property, entry.getValue());
189+
} else if (entry.getKey().startsWith("ktlint_")) {
190+
String[] parts = entry.getKey().substring(7).split("_", 2);
191+
if (parts.length == 1) {
192+
// convert ktlint_{ruleset} to {ruleset}
193+
String qualifiedRuleId = parts[0] + ":";
194+
property = createRuleSetExecution(qualifiedRuleId, RuleExecution.disabled);
195+
} else {
196+
// convert ktlint_{ruleset}_{rulename} to {ruleset}:{rulename}
197+
String qualifiedRuleId = parts[0] + ":" + parts[1];
198+
property = createRuleExecution(qualifiedRuleId, RuleExecution.disabled);
199+
}
200+
return new Pair<>(property, entry.getValue());
201+
} else {
202+
return null;
203+
}
204+
})
205+
.filter(Objects::nonNull)
206+
.toArray(Pair[]::new);
207+
208+
return EditorConfigOverride.Companion.from(properties);
209+
}
210+
}

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
@@ -38,15 +38,17 @@ public class KtlintFormatterFunc implements FormatterFunc.NeedsFile {
3838
public KtlintFormatterFunc(String version, boolean isScript, boolean useExperimental, FileSignature editorConfigPath, Map<String, String> userData,
3939
Map<String, Object> editorConfigOverrideMap) {
4040
int minorVersion = Integer.parseInt(version.split("\\.")[1]);
41-
if (minorVersion >= 48) {
41+
if (minorVersion >= 49) {
42+
// Packages and modules moved around (`ktlint-core` -> `ktlint-rule-engine`)
43+
// Experimental ruleset was replaced by implementing `Rule.Experimental` and checking the `ktlint_experimental` `.editorconfig` property
44+
// `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
45+
this.adapter = new KtLintCompat0Dot49Dot0Adapter();
46+
} else if (minorVersion == 48) {
4247
// ExperimentalParams lost two constructor arguments, EditorConfigProperty moved to its own class
4348
this.adapter = new KtLintCompat0Dot48Dot0Adapter();
44-
} else if (minorVersion == 47) {
49+
} else {
4550
// rename RuleSet to RuleProvider
4651
this.adapter = new KtLintCompat0Dot47Dot0Adapter();
47-
} else {
48-
// DefaultEditorConfigProperties.INSTANCE.getDefaultEditorConfigProperties() renamed to .getEditorConfigProperties()
49-
this.adapter = new KtLintCompat0Dot46Dot0Adapter();
5052
}
5153
this.editorConfigPath = editorConfigPath;
5254
this.useExperimental = useExperimental;

0 commit comments

Comments
 (0)