diff --git a/lib/src/main/java/com/diffplug/spotless/ForeignExe.java b/lib/src/main/java/com/diffplug/spotless/ForeignExe.java index 4cb15d1a8a..43846a2c9e 100644 --- a/lib/src/main/java/com/diffplug/spotless/ForeignExe.java +++ b/lib/src/main/java/com/diffplug/spotless/ForeignExe.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 DiffPlug + * Copyright 2020-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package com.diffplug.spotless; import java.io.IOException; +import java.io.Serializable; import java.nio.charset.Charset; import java.util.Objects; import java.util.regex.Matcher; @@ -31,7 +32,8 @@ * Usage: {@code ForeignExe.nameAndVersion("grep", "2.5.7").confirmVersionAndGetAbsolutePath()} * will find grep, confirm that it is version 2.5.7, and then return. */ -public class ForeignExe { +public class ForeignExe implements Serializable { + private static final long serialVersionUID = 1L; private @Nullable String pathToExe; private String versionFlag = "--version"; private Pattern versionRegex = Pattern.compile("version (\\S*)"); diff --git a/lib/src/main/java/com/diffplug/spotless/cpp/ClangFormatStep.java b/lib/src/main/java/com/diffplug/spotless/cpp/ClangFormatStep.java index 5591045505..345ac723c3 100644 --- a/lib/src/main/java/com/diffplug/spotless/cpp/ClangFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/cpp/ClangFormatStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 DiffPlug + * Copyright 2020-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,10 +64,10 @@ public ClangFormatStep withPathToExe(String pathToExe) { } public FormatterStep create() { - return FormatterStep.createLazy(name(), this::createState, State::toFunc); + return FormatterStep.createLazy(name(), this::createState, RoundtripState::state, State::toFunc); } - private State createState() throws IOException, InterruptedException { + private RoundtripState createState() throws IOException, InterruptedException { String howToInstall = "" + "You can download clang-format from https://releases.llvm.org and " + "then point Spotless to it with {@code pathToExe('/path/to/clang-format')} " + @@ -82,7 +82,25 @@ private State createState() throws IOException, InterruptedException { .fixWrongVersion( "You can tell Spotless to use the version you already have with {@code clangFormat('{versionFound}')}" + "or you can download the currently specified version, {version}.\n" + howToInstall); - return new State(this, exe); + return new RoundtripState(this, exe); + } + + static class RoundtripState implements Serializable { + private static final long serialVersionUID = 1L; + + final String version; + final @Nullable String style; + final ForeignExe exe; + + RoundtripState(ClangFormatStep step, ForeignExe exe) { + this.version = step.version; + this.style = step.style; + this.exe = exe; + } + + private State state() { + return new State(version, style, exe); + } } @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") @@ -95,9 +113,9 @@ static class State implements Serializable { // used for executing private transient @Nullable List args; - State(ClangFormatStep step, ForeignExe pathToExe) { - this.version = step.version; - this.style = step.style; + State(String version, @Nullable String style, ForeignExe pathToExe) { + this.version = version; + this.style = style; this.exe = Objects.requireNonNull(pathToExe); } diff --git a/lib/src/main/java/com/diffplug/spotless/protobuf/BufStep.java b/lib/src/main/java/com/diffplug/spotless/protobuf/BufStep.java index ac39eac935..4fc087ad7a 100644 --- a/lib/src/main/java/com/diffplug/spotless/protobuf/BufStep.java +++ b/lib/src/main/java/com/diffplug/spotless/protobuf/BufStep.java @@ -58,16 +58,32 @@ public BufStep withPathToExe(String pathToExe) { } public FormatterStep create() { - return FormatterStep.createLazy(name(), this::createState, State::toFunc); + return FormatterStep.createLazy(name(), this::createRoundtrip, RoundtripState::state, State::toFunc); } - private State createState() { + private RoundtripState createRoundtrip() { String instructions = "https://docs.buf.build/installation"; ForeignExe exe = ForeignExe.nameAndVersion("buf", version) .pathToExe(pathToExe) .versionRegex(Pattern.compile("(\\S*)")) .fixCantFind("Try following the instructions at " + instructions + ", or else tell Spotless where it is with {@code buf().pathToExe('path/to/executable')}"); - return new State(this, exe); + return new RoundtripState(version, exe); + } + + private static class RoundtripState implements Serializable { + private static final long serialVersionUID = 1L; + + final String version; + final ForeignExe exe; + + RoundtripState(String version, ForeignExe exe) { + this.version = version; + this.exe = exe; + } + + private State state() { + return new State(version, exe); + } } @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") @@ -80,9 +96,9 @@ static class State implements Serializable { private final transient ForeignExe exe; private transient String exeAbsPath; - State(BufStep step, ForeignExe exe) { - this.version = step.version; - this.exe = Objects.requireNonNull(exe); + State(String version, ForeignExe exeAbsPath) { + this.version = version; + this.exe = Objects.requireNonNull(exeAbsPath); } String format(ProcessRunner runner, String input, File file) throws IOException, InterruptedException { diff --git a/lib/src/main/java/com/diffplug/spotless/python/BlackStep.java b/lib/src/main/java/com/diffplug/spotless/python/BlackStep.java index 9bce2241dd..9fe26cdae8 100644 --- a/lib/src/main/java/com/diffplug/spotless/python/BlackStep.java +++ b/lib/src/main/java/com/diffplug/spotless/python/BlackStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 DiffPlug + * Copyright 2020-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,17 +56,33 @@ public BlackStep withPathToExe(String pathToExe) { } public FormatterStep create() { - return FormatterStep.createLazy(name(), this::createState, State::toFunc); + return FormatterStep.createLazy(name(), this::createState, RoundtripState::state, State::toFunc); } - private State createState() throws IOException, InterruptedException { + private RoundtripState createState() { String trackingIssue = "\n github issue to handle this better: https://github.com/diffplug/spotless/issues/674"; ForeignExe exeAbsPath = ForeignExe.nameAndVersion("black", version) .pathToExe(pathToExe) .versionRegex(Pattern.compile("(?:black, version|black,|version) (\\S*)")) .fixCantFind("Try running {@code pip install black=={version}}, or else tell Spotless where it is with {@code black().pathToExe('path/to/executable')}" + trackingIssue) .fixWrongVersion("Try running {@code pip install --force-reinstall black=={version}}, or else specify {@code black('{versionFound}')} to Spotless" + trackingIssue); - return new State(this, exeAbsPath); + return new RoundtripState(version, exeAbsPath); + } + + static class RoundtripState implements Serializable { + private static final long serialVersionUID = 1L; + + final String version; + final ForeignExe exe; + + RoundtripState(String version, ForeignExe exe) { + this.version = version; + this.exe = exe; + } + + private State state() { + return new State(version, exe); + } } @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") @@ -78,8 +94,8 @@ static class State implements Serializable { // used for executing private transient @Nullable String[] args; - State(BlackStep step, ForeignExe exeAbsPath) { - this.version = step.version; + State(String version, ForeignExe exeAbsPath) { + this.version = version; this.exe = Objects.requireNonNull(exeAbsPath); } diff --git a/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java b/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java index c82de128a1..f9b5864d7f 100644 --- a/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java +++ b/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java @@ -17,6 +17,7 @@ import java.util.Locale; import java.util.Objects; +import java.util.Set; import org.assertj.core.api.Assertions; @@ -48,6 +49,8 @@ protected StepHarnessBase(Formatter formatter) { supportsRoundTrip = true; } else if (onlyStepName.equals("fence")) { supportsRoundTrip = true; + } else if (Set.of("black", "buf", "clang").contains(onlyStepName)) { + supportsRoundTrip = true; } } }