Skip to content

Support KtLint 0.49.0, drop KtLint 0.46.0 #1706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ indent_size = 2

[*.java]
# Doc: https://youtrack.jetbrains.com/issue/IDEA-170643#focus=streamItem-27-3708697.0-0
ij_java_imports_layout = java.**,|,javax.**,|,org.**,|,com.**,|,com.diffplug.**,|,*
ij_java_imports_layout = $*,|,java.**,|,javax.**,|,org.**,|,com.**,|,com.diffplug.**,|,*
ij_java_use_single_class_imports = true
ij_java_class_count_to_use_import_on_demand = 999
ij_java_names_count_to_use_import_on_demand = 999
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Fixed a regression which changed the import sorting order in `googleJavaFormat` introduced in `2.38.0`. ([#1680](https://github.com/diffplug/spotless/pull/1680))
### Changes
* Bump default sortpom version to latest `3.0.0` -> `3.2.1`. ([#1675](https://github.com/diffplug/spotless/pull/1675))
* Bump default `ktlint` version to latest `0.48.2` -> `0.49.1`.([#1696](https://github.com/diffplug/spotless/issues/1696))
* Dropped support for `ktlint 0.46.x` following our policy of supporting two breaking changes at a time.

## [2.38.0] - 2023-04-06
### Added
Expand Down
13 changes: 4 additions & 9 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ versionCompatibility {
// we will support no more than 2 breaking changes at a time = 3 incompatible versions
// we will try to drop down to only one version if a stable API can be maintained for a full year
versions = [
'0.46.0',
'0.47.0',
'0.48.0',
'0.49.0',
]
targetSourceSetName = 'ktlint'
}
Expand Down Expand Up @@ -96,20 +96,15 @@ dependencies {
}
}
// ktlint
String VER_KTLINT='0.46.1'
ktlintCompileOnly "com.pinterest:ktlint:$VER_KTLINT"
ktlintCompileOnly "com.pinterest.ktlint:ktlint-core:$VER_KTLINT"
ktlintCompileOnly "com.pinterest.ktlint:ktlint-ruleset-experimental:$VER_KTLINT"
ktlintCompileOnly "com.pinterest.ktlint:ktlint-ruleset-standard:$VER_KTLINT"
compatKtLint0Dot46Dot0CompileOnly 'com.pinterest.ktlint:ktlint-core:0.46.0'
compatKtLint0Dot46Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.46.0'
compatKtLint0Dot46Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.46.0'
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-core:0.47.0'
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.47.0'
compatKtLint0Dot47Dot0CompileOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.47.0'
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-core:0.48.0'
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-experimental:0.48.0'
compatKtLint0Dot48Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.48.0'
compatKtLint0Dot49Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-rule-engine:0.49.0'
compatKtLint0Dot49Dot0CompileAndTestOnly 'com.pinterest.ktlint:ktlint-ruleset-standard:0.49.0'
compatKtLint0Dot49Dot0CompileAndTestOnly 'org.slf4j:slf4j-api:2.0.0'
// palantirJavaFormat
palantirJavaFormatCompileOnly 'com.palantir.javaformat:palantir-java-format:1.1.0' // this version needs to stay compilable against Java 8 for CI Job testNpm
// scalafmt
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
* Copyright 2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.glue.ktlint.compat;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pinterest.ktlint.rule.engine.api.Code;
import com.pinterest.ktlint.rule.engine.api.EditorConfigDefaults;
import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride;
import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine;
import com.pinterest.ktlint.rule.engine.api.LintError;
import com.pinterest.ktlint.rule.engine.core.api.Rule;
import com.pinterest.ktlint.rule.engine.core.api.RuleProvider;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleEditorConfigPropertyKt;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EndOfLinePropertyKt;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.IndentSizeEditorConfigPropertyKt;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.IndentStyleEditorConfigPropertyKt;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.InsertFinalNewLineEditorConfigPropertyKt;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MaxLineLengthEditorConfigPropertyKt;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecution;
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecutionEditorConfigPropertyKt;
import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider;

import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function2;

public class KtLintCompat0Dot49Dot0Adapter implements KtLintCompatAdapter {

private static final Logger logger = LoggerFactory.getLogger(KtLintCompat0Dot49Dot0Adapter.class);

private static final List<EditorConfigProperty<?>> DEFAULT_EDITOR_CONFIG_PROPERTIES;

static {
List<EditorConfigProperty<?>> list = new ArrayList<>();
list.add(CodeStyleEditorConfigPropertyKt.getCODE_STYLE_PROPERTY());
list.add(EndOfLinePropertyKt.getEND_OF_LINE_PROPERTY());
list.add(IndentSizeEditorConfigPropertyKt.getINDENT_SIZE_PROPERTY());
list.add(IndentStyleEditorConfigPropertyKt.getINDENT_STYLE_PROPERTY());
list.add(InsertFinalNewLineEditorConfigPropertyKt.getINSERT_FINAL_NEWLINE_PROPERTY());
list.add(MaxLineLengthEditorConfigPropertyKt.getMAX_LINE_LENGTH_PROPERTY());
list.add(RuleExecutionEditorConfigPropertyKt.getEXPERIMENTAL_RULES_EXECUTION_PROPERTY());
DEFAULT_EDITOR_CONFIG_PROPERTIES = Collections.unmodifiableList(list);
}

private static final Method RULEID_METHOD;
private static final Method CREATE_RULESET_EXECUTION_METHOD;
private static final Method CREATE_RULE_EXECUTION_METHOD;

static {
try {
RULEID_METHOD = LintError.class.getMethod("getRuleId-6XN97os");
CREATE_RULESET_EXECUTION_METHOD = RuleExecutionEditorConfigPropertyKt.class.getMethod("createRuleSetExecutionEditorConfigProperty-fqiwTpU", String.class, RuleExecution.class);
CREATE_RULE_EXECUTION_METHOD = RuleExecutionEditorConfigPropertyKt.class.getMethod("createRuleExecutionEditorConfigProperty-U7AdEiY", String.class, RuleExecution.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
Comment on lines +80 to +86
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the mangled method names for 0.49.X. They're unrepresentable in Java source because they contain a hyphen, and they're mangled in the first place because they deal with Kotlin value classes (and Valhalla isn't ready, so they implemented it like this first).
More info here: pinterest/ktlint#2041

}

private static String getRuleId(LintError lint) {
try {
return (String) RULEID_METHOD.invoke(lint);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}

private static EditorConfigProperty<?> createRuleSetExecution(String id, RuleExecution execution) {
try {
return (EditorConfigProperty<?>) CREATE_RULESET_EXECUTION_METHOD.invoke(null, id, execution);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}

private static EditorConfigProperty<?> createRuleExecution(String id, RuleExecution execution) {
try {
return (EditorConfigProperty<?>) CREATE_RULE_EXECUTION_METHOD.invoke(null, id, execution);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}

static class FormatterCallback implements Function2<LintError, Boolean, Unit> {

@Override
public Unit invoke(LintError lint, Boolean corrected) {
if (!corrected) {
KtLintCompatReporting.report(lint.getLine(), lint.getCol(), getRuleId(lint), lint.getDetail());
}
return Unit.INSTANCE;
}
}

@Override
public String format(final String text, Path path, final boolean isScript,
final boolean useExperimental,
Path editorConfigPath, final Map<String, String> userData,
final Map<String, Object> editorConfigOverrideMap) {
final FormatterCallback formatterCallback = new FormatterCallback();

Set<RuleProvider> allRuleProviders = new LinkedHashSet<>(
new StandardRuleSetProvider().getRuleProviders());

// TODO: Should we keep `useExperimental` now that ktlint uses an EditorConfig property for this purpose?
if (useExperimental) {
String experimentalRulesPropertyName = RuleExecutionEditorConfigPropertyKt.getEXPERIMENTAL_RULES_EXECUTION_PROPERTY().getName();
Object experimentalOverride = editorConfigOverrideMap.get(experimentalRulesPropertyName);
if (experimentalOverride != null) {
logger.warn("`useExperimental` parameter is `true` and `ktlint_experimental` property is set, `useExperimental` will take priority!");
editorConfigOverrideMap.put(experimentalRulesPropertyName, "enabled");
}
}

EditorConfigOverride editorConfigOverride;
if (editorConfigOverrideMap.isEmpty()) {
editorConfigOverride = EditorConfigOverride.Companion.getEMPTY_EDITOR_CONFIG_OVERRIDE();
} else {
editorConfigOverride = createEditorConfigOverride(allRuleProviders.stream().map(
RuleProvider::createNewRuleInstance).collect(Collectors.toList()),
editorConfigOverrideMap);
}
EditorConfigDefaults editorConfig;
if (editorConfigPath == null || !Files.exists(editorConfigPath)) {
editorConfig = EditorConfigDefaults.Companion.getEMPTY_EDITOR_CONFIG_DEFAULTS();
} else {
editorConfig = EditorConfigDefaults.Companion.load(editorConfigPath);
}

return new KtLintRuleEngine(
allRuleProviders,
editorConfig,
editorConfigOverride,
false,
path.getFileSystem())
.format(Code.Companion.fromPath(path), formatterCallback);
}

/**
* Create EditorConfigOverride from user provided parameters.
*/
private static EditorConfigOverride createEditorConfigOverride(final List<Rule> rules, Map<String, Object> editorConfigOverrideMap) {
// Get properties from rules in the rule sets
Stream<EditorConfigProperty<?>> ruleProperties = rules.stream()
.flatMap(rule -> rule.getUsesEditorConfigProperties().stream());

// Create a mapping of properties to their names based on rule properties and default properties
Map<String, EditorConfigProperty<?>> supportedProperties = Stream
.concat(ruleProperties, DEFAULT_EDITOR_CONFIG_PROPERTIES.stream())
.distinct()
.collect(Collectors.toMap(EditorConfigProperty::getName, property -> property));

// Create config properties based on provided property names and values
@SuppressWarnings("unchecked")
Pair<EditorConfigProperty<?>, ?>[] properties = editorConfigOverrideMap.entrySet().stream()
.map(entry -> {
EditorConfigProperty<?> property = supportedProperties.get(entry.getKey());
if (property != null) {
return new Pair<>(property, entry.getValue());
} else if (entry.getKey().startsWith("ktlint_")) {
String[] parts = entry.getKey().substring(7).split("_", 2);
if (parts.length == 1) {
// convert ktlint_{ruleset} to {ruleset}
String qualifiedRuleId = parts[0] + ":";
property = createRuleSetExecution(qualifiedRuleId, RuleExecution.disabled);
} else {
// convert ktlint_{ruleset}_{rulename} to {ruleset}:{rulename}
String qualifiedRuleId = parts[0] + ":" + parts[1];
property = createRuleExecution(qualifiedRuleId, RuleExecution.disabled);
}
return new Pair<>(property, entry.getValue());
} else {
return null;
}
})
.filter(Objects::nonNull)
.toArray(Pair[]::new);

return EditorConfigOverride.Companion.from(properties);
}
}
Loading