Skip to content

Commit 5d9f3b4

Browse files
authored
Maven IDE hook (#1782)
2 parents c4e5be7 + 5ddf6e0 commit 5d9f3b4

File tree

5 files changed

+190
-0
lines changed

5 files changed

+190
-0
lines changed

plugin-maven/CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
44

55
## [Unreleased]
66
### Added
7+
* Add `-DspotlessIdeHook` that provides the ability to apply Spotless exclusively to a specified file. It accepts the absolute path of the file. ([#1782](https://github.com/diffplug/spotless/pull/1782))
8+
* BETA, subject to change until we have proven compatibility with some IDE plugins.
79
* Added support for `google-java-format`'s `skip-javadoc-formatting` option ([#1793](https://github.com/diffplug/spotless/pull/1793))
810
* Added support for biome. The Rome project [was renamed to Biome](https://biomejs.dev/blog/annoucing-biome/).
911
The configuration is still the same, but you should switch to the new `<biome>` tag and adjust

plugin-maven/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies {
3939
compileOnly "org.eclipse.aether:aether-api:${VER_ECLIPSE_AETHER}"
4040

4141
implementation "com.diffplug.durian:durian-core:${VER_DURIAN}"
42+
implementation "com.diffplug.durian:durian-io:${VER_DURIAN}"
4243
implementation "com.diffplug.durian:durian-collect:${VER_DURIAN}"
4344
implementation("org.codehaus.plexus:plexus-resources:${VER_PLEXUS_RESOURCES}")
4445
implementation "org.eclipse.jgit:org.eclipse.jgit:${VER_JGIT}"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2016-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.maven;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.nio.file.Files;
21+
22+
import com.diffplug.common.base.Errors;
23+
import com.diffplug.common.io.ByteStreams;
24+
import com.diffplug.spotless.Formatter;
25+
import com.diffplug.spotless.PaddedCell;
26+
27+
class IdeHook {
28+
29+
private static void dumpIsClean() {
30+
System.err.println("IS CLEAN");
31+
}
32+
33+
//No need to check ratchet (using isClean()) as it is performed in Gradle's IDE hook, since we have already gathered the available git files from ratchet.
34+
static void performHook(Iterable<File> projectFiles, Formatter formatter, String path, boolean spotlessIdeHookUseStdIn, boolean spotlessIdeHookUseStdOut) {
35+
File file = new File(path);
36+
if (!file.isAbsolute()) {
37+
System.err.println("Argument passed to spotlessIdeHook must be an absolute path");
38+
return;
39+
}
40+
41+
if (!projectContainsFile(projectFiles, file)) {
42+
return;
43+
}
44+
45+
try {
46+
byte[] bytes;
47+
if (spotlessIdeHookUseStdIn) {
48+
bytes = ByteStreams.toByteArray(System.in);
49+
} else {
50+
bytes = Files.readAllBytes(file.toPath());
51+
}
52+
PaddedCell.DirtyState dirty = PaddedCell.calculateDirtyState(formatter, file, bytes);
53+
if (dirty.isClean()) {
54+
dumpIsClean();
55+
} else if (dirty.didNotConverge()) {
56+
System.err.println("DID NOT CONVERGE");
57+
System.err.println("See details https://github.com/diffplug/spotless/blob/main/PADDEDCELL.md");
58+
} else {
59+
System.err.println("IS DIRTY");
60+
if (spotlessIdeHookUseStdOut) {
61+
dirty.writeCanonicalTo(System.out);
62+
} else {
63+
dirty.writeCanonicalTo(file);
64+
}
65+
}
66+
} catch (IOException e) {
67+
e.printStackTrace(System.err);
68+
throw Errors.asRuntime(e);
69+
} finally {
70+
System.err.close();
71+
System.out.close();
72+
}
73+
}
74+
75+
private static boolean projectContainsFile(Iterable<File> projectFiles, File file) {
76+
for (File projectFile : projectFiles) {
77+
if (projectFile.equals(file)) {
78+
return true;
79+
}
80+
}
81+
return false;
82+
}
83+
}

plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.java

+19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.apache.maven.plugin.MojoExecutionException;
2222
import org.apache.maven.plugins.annotations.Mojo;
23+
import org.apache.maven.plugins.annotations.Parameter;
2324

2425
import com.diffplug.spotless.Formatter;
2526
import com.diffplug.spotless.PaddedCell;
@@ -31,8 +32,22 @@
3132
@Mojo(name = AbstractSpotlessMojo.GOAL_APPLY, threadSafe = true)
3233
public class SpotlessApplyMojo extends AbstractSpotlessMojo {
3334

35+
@Parameter(property = "spotlessIdeHook")
36+
private String spotlessIdeHook;
37+
38+
@Parameter(property = "spotlessIdeHookUseStdIn")
39+
private boolean spotlessIdeHookUseStdIn;
40+
41+
@Parameter(property = "spotlessIdeHookUseStdOut")
42+
private boolean spotlessIdeHookUseStdOut;
43+
3444
@Override
3545
protected void process(Iterable<File> files, Formatter formatter, UpToDateChecker upToDateChecker) throws MojoExecutionException {
46+
if (isIdeHook()) {
47+
IdeHook.performHook(files, formatter, spotlessIdeHook, spotlessIdeHookUseStdIn, spotlessIdeHookUseStdOut);
48+
return;
49+
}
50+
3651
ImpactedFilesTracker counter = new ImpactedFilesTracker();
3752

3853
for (File file : files) {
@@ -69,4 +84,8 @@ protected void process(Iterable<File> files, Formatter formatter, UpToDateChecke
6984
getLog().debug(String.format("Spotless.%s has no target files. Examine your `<includes>`: https://github.com/diffplug/spotless/tree/main/plugin-maven#quickstart", formatter.getName()));
7085
}
7186
}
87+
88+
private boolean isIdeHook() {
89+
return !(spotlessIdeHook == null || spotlessIdeHook.isEmpty());
90+
}
7291
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2016-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.maven;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
21+
import org.assertj.core.api.Assertions;
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
25+
import com.diffplug.spotless.ProcessRunner;
26+
27+
class IdeHookTest extends MavenIntegrationHarness {
28+
private String output, error;
29+
private File dirty, clean, diverge, outofbounds;
30+
31+
@BeforeEach
32+
void before() throws IOException {
33+
writePomWithFormatSteps("<includes>\n" +
34+
" <include>DIRTY.md</include>\n" +
35+
" <include>CLEAN.md</include>\n" +
36+
" </includes>\n" +
37+
" <replace>\n" +
38+
" <name>Greetings to Mars</name>\n" +
39+
" <search>World</search>\n" +
40+
" <replacement>Mars</replacement>\n" +
41+
" </replace>");
42+
43+
dirty = setFile("DIRTY.md").toContent("World");
44+
clean = setFile("CLEAN.md").toContent("Mars");
45+
outofbounds = setFile("OUTOFBOUNDS.md").toContent("Mars");
46+
;
47+
}
48+
49+
private void runWith(String... arguments) throws IOException, InterruptedException {
50+
ProcessRunner.Result result = mavenRunner()
51+
.withArguments(arguments)
52+
.runNoError();
53+
54+
this.output = result.stdOutUtf8();
55+
this.error = result.stdErrUtf8();
56+
}
57+
58+
@Test
59+
void dirty() throws IOException, InterruptedException {
60+
runWith("spotless:apply", "--quiet", "-DspotlessIdeHook=\"" + dirty.getAbsolutePath() + "\"", "-DspotlessIdeHookUseStdOut=true");
61+
Assertions.assertThat(output).isEqualTo("Mars");
62+
Assertions.assertThat(error).startsWith("IS DIRTY");
63+
}
64+
65+
@Test
66+
void clean() throws IOException, InterruptedException {
67+
runWith("spotless:apply", "--quiet", "-DspotlessIdeHook=" + clean.getAbsolutePath(), "-DspotlessIdeHookUseStdOut=true");
68+
Assertions.assertThat(output).isEmpty();
69+
Assertions.assertThat(error).startsWith("IS CLEAN");
70+
}
71+
72+
@Test
73+
void outofbounds() throws IOException, InterruptedException {
74+
runWith("spotless:apply", "--quiet", "-DspotlessIdeHook=" + outofbounds.getAbsolutePath(), "-DspotlessIdeHookUseStdOut=true");
75+
Assertions.assertThat(output).isEmpty();
76+
Assertions.assertThat(error).isEmpty();
77+
}
78+
79+
@Test
80+
void notAbsolute() throws IOException, InterruptedException {
81+
runWith("spotless:apply", "--quiet", "-DspotlessIdeHook=\"pom.xml\"", "-DspotlessIdeHookUseStdOut=true");
82+
Assertions.assertThat(output).isEmpty();
83+
Assertions.assertThat(error).contains("Argument passed to spotlessIdeHook must be an absolute path");
84+
}
85+
}

0 commit comments

Comments
 (0)