Skip to content

Commit

Permalink
Support KtLint 0.49.0, drop KtLint 0.46.0 (#1706 fixes #1696)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg authored May 22, 2023
2 parents 8d4f402 + 87efad8 commit d107957
Show file tree
Hide file tree
Showing 13 changed files with 326 additions and 149 deletions.
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 @@ -19,6 +19,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);
}
}

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

0 comments on commit d107957

Please sign in to comment.