Skip to content

Commit d771a9c

Browse files
committed
chore: provide custom idea.properties with custom config/system paths
1 parent 599ad51 commit d771a9c

File tree

4 files changed

+96
-26
lines changed

4 files changed

+96
-26
lines changed

lib/src/main/java/com/diffplug/spotless/generic/IdeaStep.java

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@
1616
package com.diffplug.spotless.generic;
1717

1818
import java.io.File;
19+
import java.io.FileOutputStream;
1920
import java.io.IOException;
2021
import java.io.Serializable;
2122
import java.nio.charset.StandardCharsets;
2223
import java.nio.file.Files;
24+
import java.nio.file.Path;
25+
import java.util.HashMap;
2326
import java.util.List;
2427
import java.util.Map;
2528
import java.util.Objects;
29+
import java.util.Properties;
30+
import java.util.TreeMap;
31+
import java.util.UUID;
2632
import java.util.regex.Pattern;
2733
import java.util.stream.Collectors;
2834
import java.util.stream.Stream;
@@ -44,12 +50,16 @@ public final class IdeaStep {
4450

4551
public static final String NAME = "IDEA";
4652

53+
public static final String IDEA_EXECUTABLE_DEFAULT = "idea";
54+
4755
private static final Logger LOGGER = LoggerFactory.getLogger(IdeaStep.class);
56+
public static final String IDEA_CONFIG_PATH_PROPERTY = "idea.config.path";
57+
public static final String IDEA_SYSTEM_PATH_PROPERTY = "idea.system.path";
4858

4959
private IdeaStep() {}
5060

51-
public static IdeaStepBuilder create() {
52-
return new IdeaStepBuilder();
61+
public static IdeaStepBuilder create(@Nonnull File buildDir) {
62+
return new IdeaStepBuilder(Objects.requireNonNull(buildDir));
5363
}
5464

5565
private static FormatterStep create(@Nonnull IdeaStepBuilder builder) {
@@ -64,13 +74,20 @@ private static State createState(@Nonnull IdeaStepBuilder builder) {
6474
}
6575

6676
public static final class IdeaStepBuilder {
67-
private static final String DEFAULT_IDEA = "idea";
6877

6978
private boolean useDefaults = true;
7079
@Nonnull
71-
private String binaryPath = DEFAULT_IDEA;
80+
private String binaryPath = IDEA_EXECUTABLE_DEFAULT;
7281
@Nullable
7382
private String codeStyleSettingsPath;
83+
private final Map<String, String> ideaProperties = new HashMap<>();
84+
85+
@Nonnull
86+
private final File buildDir;
87+
88+
private IdeaStepBuilder(@Nonnull File buildDir) {
89+
this.buildDir = Objects.requireNonNull(buildDir);
90+
}
7491

7592
public IdeaStepBuilder setUseDefaults(boolean useDefaults) {
7693
this.useDefaults = useDefaults;
@@ -87,6 +104,14 @@ public IdeaStepBuilder setCodeStyleSettingsPath(@Nullable String codeStyleSettin
87104
return this;
88105
}
89106

107+
public IdeaStepBuilder setIdeaProperties(@Nonnull Map<String, String> ideaProperties) {
108+
if (ideaProperties.containsKey(IDEA_CONFIG_PATH_PROPERTY) || ideaProperties.containsKey(IDEA_SYSTEM_PATH_PROPERTY)) {
109+
throw new IllegalArgumentException("Cannot override IDEA config or system path");
110+
}
111+
this.ideaProperties.putAll(ideaProperties);
112+
return this;
113+
}
114+
90115
public FormatterStep build() {
91116
return create(this);
92117
}
@@ -97,30 +122,35 @@ private static class State
97122

98123
private static final long serialVersionUID = -1825662355363926318L;
99124

100-
private String binaryPath;
125+
private final String buildDir;
126+
private final String uniquePath;
127+
private final String binaryPath;
101128
@Nullable
102-
private String codeStyleSettingsPath;
103-
private boolean withDefaults;
129+
private final String codeStyleSettingsPath; // TODO make sure to save content in state
130+
private final boolean withDefaults;
131+
private final TreeMap<String, String> ideaProperties;
104132

105133
private State(@Nonnull IdeaStepBuilder builder) {
134+
this.buildDir = ThrowingEx.get(builder.buildDir::getCanonicalPath);
135+
this.uniquePath = UUID.randomUUID().toString();
106136
this.withDefaults = builder.useDefaults;
107137
this.codeStyleSettingsPath = builder.codeStyleSettingsPath;
108-
this.binaryPath = builder.binaryPath;
109-
resolveFullBinaryPathAndCheckVersion();
138+
this.ideaProperties = new TreeMap<>(builder.ideaProperties);
139+
this.binaryPath = resolveFullBinaryPathAndCheckVersion(builder.binaryPath);
110140
}
111141

112-
private void resolveFullBinaryPathAndCheckVersion() {
142+
private static String resolveFullBinaryPathAndCheckVersion(String binaryPath) {
113143
var exe = ForeignExe
114-
.nameAndVersion(this.binaryPath, "IntelliJ IDEA")
115-
.pathToExe(pathToExe())
144+
.nameAndVersion(binaryPath, "IntelliJ IDEA")
145+
.pathToExe(pathToExe(binaryPath))
116146
.versionRegex(Pattern.compile("(IntelliJ IDEA) .*"))
117147
.fixCantFind(
118148
"IDEA executable cannot be found on your machine, "
119149
+ "please install it and put idea binary to PATH, provide a valid path to the executable or report the problem")
120150
.fixWrongVersion("Provided binary is not IDEA, "
121151
+ "please check it and fix the problem; or report the problem");
122152
try {
123-
this.binaryPath = exe.confirmVersionAndGetAbsolutePath();
153+
return exe.confirmVersionAndGetAbsolutePath();
124154
} catch (IOException e) {
125155
throw new IllegalArgumentException("binary cannot be found", e);
126156
} catch (InterruptedException e) {
@@ -130,10 +160,7 @@ private void resolveFullBinaryPathAndCheckVersion() {
130160
}
131161

132162
@CheckForNull
133-
private String pathToExe() {
134-
if (binaryPath == null) {
135-
throw new IllegalStateException("binaryPath is not set");
136-
}
163+
private static String pathToExe(String binaryPath) {
137164
return macOsFix(binaryPath);
138165
}
139166

@@ -177,7 +204,8 @@ public String applyWithFile(String unix, File file) throws Exception {
177204
List<String> params = getParams(tempFile);
178205

179206
try (ProcessRunner runner = new ProcessRunner()) {
180-
var result = runner.exec(params);
207+
Map<String, String> env = createEnv();
208+
var result = runner.exec(null, env, null, params);
181209
LOGGER.debug("command finished with stdout: {}",
182210
result.assertExitZero(StandardCharsets.UTF_8));
183211

@@ -188,6 +216,38 @@ public String applyWithFile(String unix, File file) throws Exception {
188216
}
189217
}
190218

219+
private Map<String, String> createEnv() {
220+
File ideaProps = createIdeaPropertiesFile();
221+
Map<String, String> env = Map.ofEntries(
222+
Map.entry("IDEA_PROPERTIES", ThrowingEx.get(ideaProps::getCanonicalPath)));
223+
return env;
224+
}
225+
226+
private File createIdeaPropertiesFile() {
227+
Path ideaProps = new File(buildDir).toPath().resolve(uniquePath).resolve("idea.properties");
228+
229+
if (Files.exists(ideaProps)) {
230+
return ideaProps.toFile(); // only create if it does not exist
231+
}
232+
233+
ThrowingEx.run(() -> Files.createDirectories(ideaProps.getParent()));
234+
235+
Path configPath = ideaProps.getParent().resolve("config");
236+
Path systemPath = ideaProps.getParent().resolve("system");
237+
238+
Properties properties = new Properties();
239+
properties.putAll(ideaProperties);
240+
properties.put(IDEA_CONFIG_PATH_PROPERTY, ThrowingEx.get(configPath.toFile()::getCanonicalPath));
241+
properties.put(IDEA_SYSTEM_PATH_PROPERTY, ThrowingEx.get(systemPath.toFile()::getCanonicalPath));
242+
243+
try (FileOutputStream out = new FileOutputStream(ideaProps.toFile())) {
244+
properties.store(out, "Generated by spotless");
245+
} catch (IOException e) {
246+
throw new IllegalStateException("Failed to create IDEA properties file", e);
247+
}
248+
return ideaProps.toFile();
249+
}
250+
191251
private List<String> getParams(File file) {
192252
/* https://www.jetbrains.com/help/idea/command-line-formatter.html */
193253
var builder = Stream.<String> builder();

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ public class IdeaConfig {
969969
private final IdeaStep.IdeaStepBuilder builder;
970970

971971
IdeaConfig() {
972-
this.builder = IdeaStep.create();
972+
this.builder = IdeaStep.create(getProject().getLayout().getBuildDirectory().getAsFile().get());
973973
addStep(createStep());
974974
}
975975

plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Idea.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class Idea implements FormatterStepFactory {
3535

3636
@Override
3737
public FormatterStep newFormatterStep(FormatterStepConfig config) {
38-
return IdeaStep.create()
38+
return IdeaStep.create(config.getFileLocator().getBuildDir())
3939
.setUseDefaults(withDefaults)
4040
.setCodeStyleSettingsPath(configPath)
4141
.setBinaryPath(binaryPath)

testlib/src/test/java/com/diffplug/spotless/generic/IdeaStepTest.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
import com.diffplug.common.io.Files;
2525
import com.diffplug.spotless.FormatterStep;
2626
import com.diffplug.spotless.ResourceHarness;
27+
import com.diffplug.spotless.ThrowingEx;
2728
import com.diffplug.spotless.tag.IdeaTest;
2829

2930
@IdeaTest
3031
class IdeaStepTest extends ResourceHarness {
3132

3233
@Test
3334
void name() throws Exception {
34-
FormatterStep step = IdeaStep.create().setUseDefaults(true).setBinaryPath("idea").build();
35+
FormatterStep step = IdeaStep.create(buildDir()).setUseDefaults(true).build();
3536

3637
String name = step.getName();
3738

@@ -43,7 +44,7 @@ void notFormattings() throws Exception {
4344
File cleanFile = newFile("clean.java");
4445
String cleanJava = ResourceHarness.getTestResource("java/idea/full.clean.java");
4546
Files.write(cleanJava, cleanFile, StandardCharsets.UTF_8);
46-
FormatterStep step = IdeaStep.create().setUseDefaults(true).setBinaryPath("/Users/simschla/Applications/IntelliJ IDEA Ultimate.app/Contents/MacOS/idea").build();
47+
FormatterStep step = IdeaStep.create(buildDir()).setUseDefaults(true).build();
4748

4849
var result = step.format(cleanJava, cleanFile);
4950

@@ -56,7 +57,7 @@ void formattings() throws Exception {
5657
File dirtyFile = newFile("dirty.java");
5758
String dirtyJava = ResourceHarness.getTestResource("java/idea/full.dirty.java");
5859
Files.write(dirtyJava, dirtyFile, StandardCharsets.UTF_8);
59-
FormatterStep step = IdeaStep.create().setUseDefaults(true).setBinaryPath("/Users/simschla/Applications/IntelliJ IDEA Ultimate.app/Contents/MacOS/idea").build();
60+
FormatterStep step = IdeaStep.create(buildDir()).setUseDefaults(true).build();
6061

6162
var result = step.format(dirtyJava, dirtyFile);
6263

@@ -69,7 +70,7 @@ void formattingsWorkWithDefaultParameters() throws Exception {
6970
File dirtyFile = newFile("dirty.java");
7071
String dirtyJava = ResourceHarness.getTestResource("java/idea/full.dirty.java");
7172
Files.write(dirtyJava, dirtyFile, StandardCharsets.UTF_8);
72-
FormatterStep step = IdeaStep.create().build();
73+
FormatterStep step = IdeaStep.create(buildDir()).build();
7374

7475
var result = step.format(dirtyJava, dirtyFile);
7576

@@ -82,7 +83,7 @@ void formattingsWithOutDefaultDoesNothing() throws Exception {
8283
File dirtyFile = newFile("dirty.java");
8384
String dirtyJava = ResourceHarness.getTestResource("java/idea/full.dirty.java");
8485
Files.write(dirtyJava, dirtyFile, StandardCharsets.UTF_8);
85-
FormatterStep step = IdeaStep.create().setUseDefaults(false).setBinaryPath("idea").build();
86+
FormatterStep step = IdeaStep.create(buildDir()).setUseDefaults(false).build();
8687

8788
var result = step.format(dirtyJava, dirtyFile);
8889

@@ -95,11 +96,20 @@ void configureFile() throws Exception {
9596
File cleanFile = newFile("clean.java");
9697
String cleanJava = ResourceHarness.getTestResource("java/idea/full.clean.java");
9798
Files.write(cleanJava, cleanFile, StandardCharsets.UTF_8);
98-
FormatterStep step = IdeaStep.create().setUseDefaults(true).setBinaryPath("idea").build();
99+
FormatterStep step = IdeaStep.create(buildDir()).setUseDefaults(true).build();
99100

100101
var result = step.format(cleanJava, cleanFile);
101102

102103
Assertions.assertEquals(cleanJava, result,
103104
"formatting was applied to clean file");
104105
}
106+
107+
private File buildDir = null;
108+
109+
protected File buildDir() {
110+
if (this.buildDir == null) {
111+
this.buildDir = ThrowingEx.get(() -> newFolder("build-dir"));
112+
}
113+
return this.buildDir;
114+
}
105115
}

0 commit comments

Comments
 (0)