Skip to content

Commit

Permalink
Merge branch 'main' into 1162-facing-could-not-resolve-parser-when-us…
Browse files Browse the repository at this point in the history
…ing-multiple-prettier-formats-with-different-plugins
  • Loading branch information
nedtwigg committed Feb 10, 2023
2 parents 92eb869 + fc3f6ff commit 3333519
Show file tree
Hide file tree
Showing 24 changed files with 680 additions and 6 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (

## [Unreleased]
### Added
* CleanThat Java Refactorer. ([#1560](https://github.com/diffplug/spotless/pull/1560))
* Introduce `LazyArgLogger` to allow for lazy evaluation of log messages in slf4j logging. ([#1565](https://github.com/diffplug/spotless/pull/1565))
### Fixed
* Allow multiple instances of the same npm-based formatter to be used by separating their `node_modules` directories. ([#1565](https://github.com/diffplug/spotless/pull/1565))
* `ktfmt` default style uses correct continuation indent. ([#1562](https://github.com/diffplug/spotless/pull/1562))

## [2.34.1] - 2023-02-05
### Changes
* **POTENTIALLY BREAKING** Bump bytecode from Java 8 to 11 ([#1530](https://github.com/diffplug/spotless/pull/1530) part 2 of [#1337](https://github.com/diffplug/spotless/issues/1337))
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ lib('java.PalantirJavaFormatStep') +'{{yes}} | {{yes}}
lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
extra('java.EclipseJdtFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.FormatAnnotationsStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('java.CleanthatJavaStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('json.gson.GsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('json.JacksonJsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('json.JsonSimpleStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
Expand Down Expand Up @@ -131,6 +132,7 @@ lib('yaml.JacksonYamlStep') +'{{yes}} | {{yes}}
| [`java.RemoveUnusedImportsStep`](lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.EclipseJdtFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.FormatAnnotationsStep`](lib/src/main/java/com/diffplug/spotless/java/FormatAnnotationsStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`java.CleanthatJavaStep`](lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`json.gson.GsonStep`](lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`json.JacksonJsonStep`](lib/src/main/java/com/diffplug/spotless/json/JacksonJsonStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`json.JsonSimpleStep`](lib/src/main/java/com/diffplug/spotless/json/JsonSimpleStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
Expand Down
12 changes: 11 additions & 1 deletion lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def NEEDS_GLUE = [
'diktat',
'scalafmt',
'jackson',
'gson'
'gson',
'cleanthat'
]
for (glue in NEEDS_GLUE) {
sourceSets.register(glue) {
Expand All @@ -39,6 +40,12 @@ versionCompatibility {
]
targetSourceSetName = 'ktlint'
}
namespaces.register('Cleanthat') {
versions = [
'2.1',
]
targetSourceSetName = 'cleanthat'
}
}
}

Expand Down Expand Up @@ -100,6 +107,9 @@ dependencies {
flexmarkCompileOnly 'com.vladsch.flexmark:flexmark-all:0.62.2'

gsonCompileOnly 'com.google.code.gson:gson:2.10.1'

cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.1'
compatCleanthat2Dot1CompileAndTestOnly 'io.github.solven-eu.cleanthat:java:2.1'
}

// we'll hold the core lib to a high standard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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.java;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

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

import com.diffplug.spotless.FormatterFunc;

import eu.solven.cleanthat.config.pojo.CleanthatEngineProperties;
import eu.solven.cleanthat.config.pojo.SourceCodeProperties;
import eu.solven.cleanthat.engine.java.IJdkVersionConstants;
import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer;
import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorerProperties;
import eu.solven.cleanthat.formatter.LineEnding;

/**
* The glue for CleanThat: it is build over the version in build.gradle, but at runtime it will be executed over
* the version loaded in JarState, which is by default defined in com.diffplug.spotless.java.CleanthatJavaStep#JVM_SUPPORT
*/
public class JavaCleanthatRefactorerFunc implements FormatterFunc {
private static final Logger LOGGER = LoggerFactory.getLogger(JavaCleanthatRefactorerFunc.class);

private String jdkVersion;
private List<String> included;
private List<String> excluded;

public JavaCleanthatRefactorerFunc(String jdkVersion, List<String> included, List<String> excluded) {
this.jdkVersion = jdkVersion == null ? IJdkVersionConstants.JDK_8 : jdkVersion;
this.included = included == null ? Collections.emptyList() : included;
this.excluded = excluded == null ? Collections.emptyList() : excluded;
}

public JavaCleanthatRefactorerFunc() {
this(IJdkVersionConstants.JDK_8, Arrays.asList(JavaRefactorerProperties.WILDCARD), Arrays.asList());
}

@Override
public String apply(String input) throws Exception {
// https://stackoverflow.com/questions/1771679/difference-between-threads-context-class-loader-and-normal-classloader
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
// Ensure CleanThat main Thread has its custom classLoader while executing its refactoring
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
return doApply(input);
} finally {
// Restore the originalClassLoader
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}

private String doApply(String input) throws InterruptedException, IOException {
// call some API that uses reflection without taking ClassLoader param
CleanthatEngineProperties engineProperties = CleanthatEngineProperties.builder().engineVersion(jdkVersion).build();

// Spotless will push us LF content
engineProperties.setSourceCode(SourceCodeProperties.builder().lineEnding(LineEnding.LF).build());

JavaRefactorerProperties refactorerProperties = new JavaRefactorerProperties();

refactorerProperties.setIncluded(included);
refactorerProperties.setExcluded(excluded);

JavaRefactorer refactorer = new JavaRefactorer(engineProperties, refactorerProperties);

LOGGER.debug("Processing sourceJdk={} included={} excluded={}", jdkVersion, included, excluded);
LOGGER.debug("Available mutators: {}", JavaRefactorer.getAllIncluded());

// Spotless calls steps always with LF eol.
return refactorer.doFormat(input, LineEnding.LF);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 DiffPlug
* Copyright 2022-2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -78,7 +78,7 @@ private FormattingOptions createFormattingOptions() {
formattingOptions.getStyle(),
ktfmtFormattingOptions.getMaxWidth().orElse(formattingOptions.getMaxWidth()),
ktfmtFormattingOptions.getBlockIndent().orElse(formattingOptions.getBlockIndent()),
ktfmtFormattingOptions.getContinuationIndent().orElse(formattingOptions.getBlockIndent()),
ktfmtFormattingOptions.getContinuationIndent().orElse(formattingOptions.getContinuationIndent()),
ktfmtFormattingOptions.getRemoveUnusedImport().orElse(formattingOptions.getRemoveUnusedImports()),
formattingOptions.getDebuggingPrintOpsAfterFormatting());
}
Expand Down
159 changes: 159 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* 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.java;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.JarState;
import com.diffplug.spotless.Jvm;
import com.diffplug.spotless.Provisioner;

/**
* Enables CleanThat as a SpotLess step.
*
* @author Benoit Lacelle
*/
// https://github.com/diffplug/spotless/blob/main/CONTRIBUTING.md#how-to-add-a-new-formatterstep
public final class CleanthatJavaStep {

private static final String NAME = "cleanthat";
private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java";

// CleanThat changelog is available at https://github.com/solven-eu/cleanthat/blob/master/CHANGES.MD
private static final Jvm.Support<String> JVM_SUPPORT = Jvm.<String> support(NAME).add(11, "2.1");

// prevent direct instantiation
private CleanthatJavaStep() {}

/** Creates a step which apply default CleanThat mutators. */
public static FormatterStep create(Provisioner provisioner) {
return create(defaultVersion(), provisioner);
}

/** Creates a step which apply default CleanThat mutators. */
public static FormatterStep create(String version, Provisioner provisioner) {
return create(MAVEN_COORDINATE, version, defaultSourceJdk(), defaultExcludedMutators(), defaultMutators(), provisioner);
}

public static String defaultSourceJdk() {
// see IJdkVersionConstants.JDK_7
// https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source
// 1.7 is the default for 'maven-compiler-plugin' since 3.9.0
return "1.7";
}

public static List<String> defaultExcludedMutators() {
return List.of();
}

/**
* By default, we include all available rules
* @return
*/
public static List<String> defaultMutators() {
// see JavaRefactorerProperties.WILDCARD
return List.of("*");
}

/** Creates a step which apply selected CleanThat mutators. */
public static FormatterStep create(String groupArtifact,
String version,
String sourceJdkVersion,
List<String> excluded,
List<String> included,
Provisioner provisioner) {
Objects.requireNonNull(groupArtifact, "groupArtifact");
if (groupArtifact.chars().filter(ch -> ch == ':').count() != 1) {
throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'. it was: " + groupArtifact);
}
Objects.requireNonNull(version, "version");
Objects.requireNonNull(provisioner, "provisioner");
return FormatterStep.createLazy(NAME,
() -> new JavaRefactorerState(NAME, groupArtifact, version, sourceJdkVersion, excluded, included, provisioner),
JavaRefactorerState::createFormat);
}

/** Get default formatter version */
public static String defaultVersion() {
return JVM_SUPPORT.getRecommendedFormatterVersion();
}

public static String defaultGroupArtifact() {
return MAVEN_COORDINATE;
}

static final class JavaRefactorerState implements Serializable {
private static final long serialVersionUID = 1L;

final JarState jarState;
final String stepName;
final String version;

final String sourceJdkVersion;
final List<String> included;
final List<String> excluded;

JavaRefactorerState(String stepName, String version, Provisioner provisioner) throws IOException {
this(stepName, MAVEN_COORDINATE, version, defaultSourceJdk(), defaultExcludedMutators(), defaultMutators(), provisioner);
}

JavaRefactorerState(String stepName,
String groupArtifact,
String version,
String sourceJdkVersion,
List<String> included,
List<String> excluded,
Provisioner provisioner) throws IOException {
JVM_SUPPORT.assertFormatterSupported(version);
ModuleHelper.doOpenInternalPackagesIfRequired();
this.jarState = JarState.from(groupArtifact + ":" + version, provisioner);
this.stepName = stepName;
this.version = version;

this.sourceJdkVersion = sourceJdkVersion;
this.included = included;
this.excluded = excluded;
}

@SuppressWarnings("PMD.UseProperClassLoader")
FormatterFunc createFormat() {
ClassLoader classLoader = jarState.getClassLoader();

Object formatter;
Method formatterMethod;
try {
Class<?> formatterClazz = classLoader.loadClass("com.diffplug.spotless.glue.java.JavaCleanthatRefactorerFunc");
Constructor<?> formatterConstructor = formatterClazz.getConstructor(String.class, List.class, List.class);

formatter = formatterConstructor.newInstance(sourceJdkVersion, included, excluded);
formatterMethod = formatterClazz.getMethod("apply", String.class);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Issue executing the formatter", e);
}
return JVM_SUPPORT.suggestLaterVersionOnError(version, input -> {
return (String) formatterMethod.invoke(formatter, input);
});
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.java;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer;

public class JavaCleanthatRefactorerFuncTest {
@Test
public void testMutatorsDetection() {
Assertions.assertThat(JavaRefactorer.getAllIncluded()).isNotEmpty();
}
}
3 changes: 3 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).

## [Unreleased]
### Added
* CleanThat Java Refactorer. ([#1560](https://github.com/diffplug/spotless/pull/1560))
### Fixed
* Allow multiple instances of the same npm-based formatter to be used simultaneously. E.g. use prettier for typescript
*and* Java (using the community prettier-plugin-java) without messing up their respective `node_module` dependencies. ([#1565](https://github.com/diffplug/spotless/pull/1565))
* `ktfmt` default style uses correct continuation indent. ([#1562](https://github.com/diffplug/spotless/pull/1562))

## [6.14.1] - 2023-02-05
### Fixed
Expand Down
Loading

0 comments on commit 3333519

Please sign in to comment.