From 8afb866e7c5f26846c475c0cf6d7e314861b072c Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 21 Nov 2022 14:38:29 -0800 Subject: [PATCH 01/23] Create a build rule for compiling deps at the Java STS language level This allows building the Java > 11 logic in google-java-format into the jar regardless of what language level is configured for the entire build, and then dynamically enabling it when running on a new enough JDK, while remaining compatible with earlier JDK versions. PiperOrigin-RevId: 490074589 --- .../com/google/googlejavaformat/java/testdata/B20844369.input | 2 +- .../com/google/googlejavaformat/java/testdata/B20844369.output | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input index 7317f17b..86e46d50 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input @@ -1,6 +1,6 @@ public class B20844369 { private static final String ID_PATTERN = - // TODO(user): add min/max lengths for the numbers here, e.g. android ID + // TODO(daw): add min/max lengths for the numbers here, e.g. android ID "(?:(?\\d+)\\+)?" // optional Android ID + "(?\\d+)" // type + ":" diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output index 62f9721b..982dc2b5 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output @@ -1,6 +1,6 @@ public class B20844369 { private static final String ID_PATTERN = - // TODO(user): add min/max lengths for the numbers here, e.g. android ID + // TODO(daw): add min/max lengths for the numbers here, e.g. android ID "(?:(?\\d+)\\+)?" // optional Android ID + "(?\\d+)" // type + ":" From d9ddb94db1d90a240f901949514f35df4e4c3fc4 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Thu, 15 Dec 2022 14:44:45 -0800 Subject: [PATCH 02/23] Recognize new JSpecify package name So that its `@Nullable` annotation is treated as a type-use annotation and formatted accordingly. See jspecify/jspecify#260. Fixes #869 COPYBARA_INTEGRATE_REVIEW=https://github.com/google/google-java-format/pull/869 from PicnicSupermarket:improvement/support-new-jpecify-package-name 7a0dc20b5cddc4d560b2be25441b1ab26c3229c2 PiperOrigin-RevId: 495701635 --- .../com/google/googlejavaformat/java/JavaInputAstVisitor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java index 75f36873..9b6283ec 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -285,6 +285,7 @@ private static ImmutableSetMultimap typeAnnotations() { ImmutableSetMultimap.Builder result = ImmutableSetMultimap.builder(); for (String annotation : ImmutableList.of( + "org.jspecify.annotations.Nullable", "org.jspecify.nullness.Nullable", "org.checkerframework.checker.nullness.qual.Nullable")) { String simpleName = annotation.substring(annotation.lastIndexOf('.') + 1); From d24deb9d982eaae896a57313f7486aaf130bf5aa Mon Sep 17 00:00:00 2001 From: cpovirk Date: Fri, 6 Jan 2023 08:20:43 -0800 Subject: [PATCH 03/23] Recognize a couple `@NonNull` annotations as type-use annotations. PiperOrigin-RevId: 500180559 --- .../com/google/googlejavaformat/java/JavaInputAstVisitor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java index 9b6283ec..394d396d 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -285,8 +285,10 @@ private static ImmutableSetMultimap typeAnnotations() { ImmutableSetMultimap.Builder result = ImmutableSetMultimap.builder(); for (String annotation : ImmutableList.of( + "org.jspecify.annotations.NonNull", "org.jspecify.annotations.Nullable", "org.jspecify.nullness.Nullable", + "org.checkerframework.checker.nullness.qual.NonNull", "org.checkerframework.checker.nullness.qual.Nullable")) { String simpleName = annotation.substring(annotation.lastIndexOf('.') + 1); result.put(simpleName, annotation); From 90f9e5aeff9dde129cd79db386470b54bdec99e3 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 13 Jan 2023 21:53:10 -0500 Subject: [PATCH 04/23] Delete dependabot.yml --- .github/dependabot.yml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index daec3189..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "maven" - directory: "/" - schedule: - interval: "daily" From 4a22aab7b19a41d6267ea70c76f137a6fd49bc76 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 16 Jan 2023 09:48:24 -0800 Subject: [PATCH 05/23] Return a non-zero exit code upon error This brings us in line with the Unix standard of returning non-zero for a failed command: https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html Also, this will allow any callers (like google-java-format-diff.py) to respond appropriately. [A change](http://r.android.com/2257232) needed to be reverted because `google-java-format-diff.py` was failing silently, so the author wasn't aware that their change caused a pre-upload hook to stop working. Fixes #848 COPYBARA_INTEGRATE_REVIEW=https://github.com/google/google-java-format/pull/848 from abstractlyZach:return_nonzero 06b2d3671d77c406be9b6d982321b17b920826df PiperOrigin-RevId: 502399096 --- core/src/main/java/com/google/googlejavaformat/java/Main.java | 3 ++- .../java/GoogleJavaFormatToolProviderTest.java | 2 +- .../test/java/com/google/googlejavaformat/java/MainTest.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/Main.java b/core/src/main/java/com/google/googlejavaformat/java/Main.java index 953ca586..11c01923 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Main.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java @@ -79,7 +79,8 @@ static int main(PrintWriter out, PrintWriter err, String... args) { return formatter.format(args); } catch (UsageException e) { err.print(e.getMessage()); - return 0; + // We return exit code 2 to differentiate usage issues from code formatting issues. + return 2; } finally { err.flush(); out.flush(); diff --git a/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java index 15e45229..d060fef1 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java @@ -46,7 +46,7 @@ public void testUsageOutputAfterLoadingViaToolName() { int result = format.run(new PrintWriter(out, true), new PrintWriter(err, true), "--help"); - assertThat(result).isEqualTo(0); + assertThat(result).isEqualTo(2); String usage = err.toString(); diff --git a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java index ac3eb396..d4a41090 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java @@ -132,7 +132,7 @@ public void testMain() throws Exception { process.waitFor(); String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8); assertThat(err).contains("Usage: google-java-format"); - assertThat(process.exitValue()).isEqualTo(0); + assertThat(process.exitValue()).isEqualTo(2); } // end to end javadoc formatting test From 91223aa642bd64f973e5b147a7e7d93a2b454fe5 Mon Sep 17 00:00:00 2001 From: Kyle J Stiemann Date: Fri, 3 Feb 2023 16:58:42 -0800 Subject: [PATCH 06/23] Fix #846 Formatter leaks threads and memory I've signed the CLA. Fixes #847 COPYBARA_INTEGRATE_REVIEW=https://github.com/google/google-java-format/pull/847 from stiemannkj1:fix-846-mem-thread-leak 0ca1e9bf9c8cf877509ac39cc50707d0ade7d71c PiperOrigin-RevId: 507026969 --- .../main/java/com/google/googlejavaformat/java/Main.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/com/google/googlejavaformat/java/Main.java b/core/src/main/java/com/google/googlejavaformat/java/Main.java index 11c01923..628c8bb2 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Main.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java @@ -18,6 +18,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.io.ByteStreams; +import com.google.common.util.concurrent.MoreExecutors; import com.google.googlejavaformat.FormatterDiagnostic; import com.google.googlejavaformat.java.JavaFormatterOptions.Style; import java.io.IOError; @@ -28,6 +29,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; @@ -187,6 +189,10 @@ private int formatFiles(CommandLineOptions parameters, JavaFormatterOptions opti outWriter.write(formatted); } } + if (!MoreExecutors.shutdownAndAwaitTermination(executorService, Duration.ofSeconds(5))) { + errWriter.println("Failed to shut down ExecutorService"); + allOk = false; + } return allOk ? 0 : 1; } From 0a6a85e37f634fa870c920997d9270dca319cc03 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 14 Feb 2023 12:33:08 -0800 Subject: [PATCH 07/23] Refactor formatter's main to use `ExecutorCompletionService` PiperOrigin-RevId: 509606924 --- .../java/FormatFileCallable.java | 57 +++++++++++---- .../google/googlejavaformat/java/Main.java | 69 ++++++++++--------- 2 files changed, 83 insertions(+), 43 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java index 9d8ae41c..3d68a23f 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java +++ b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java @@ -14,40 +14,73 @@ package com.google.googlejavaformat.java; +import com.google.auto.value.AutoValue; import com.google.common.collect.Range; import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet; +import java.nio.file.Path; import java.util.concurrent.Callable; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Encapsulates information about a file to be formatted, including which parts of the file to * format. */ -class FormatFileCallable implements Callable { +class FormatFileCallable implements Callable { + + @AutoValue + abstract static class Result { + abstract @Nullable Path path(); + + abstract String input(); + + abstract @Nullable String output(); + + boolean changed() { + return !input().equals(output()); + } + + abstract @Nullable FormatterException exception(); + + static Result create( + @Nullable Path path, + String input, + @Nullable String output, + @Nullable FormatterException exception) { + return new AutoValue_FormatFileCallable_Result(path, input, output, exception); + } + } + + private final Path path; private final String input; private final CommandLineOptions parameters; private final JavaFormatterOptions options; public FormatFileCallable( - CommandLineOptions parameters, String input, JavaFormatterOptions options) { + CommandLineOptions parameters, Path path, String input, JavaFormatterOptions options) { + this.path = path; this.input = input; this.parameters = parameters; this.options = options; } @Override - public String call() throws FormatterException { - if (parameters.fixImportsOnly()) { - return fixImports(input); - } + public Result call() { + try { + if (parameters.fixImportsOnly()) { + return Result.create(path, input, fixImports(input), /* exception= */ null); + } - Formatter formatter = new Formatter(options); - String formatted = formatter.formatSource(input, characterRanges(input).asRanges()); - formatted = fixImports(formatted); - if (parameters.reflowLongStrings()) { - formatted = StringWrapper.wrap(Formatter.MAX_LINE_LENGTH, formatted, formatter); + Formatter formatter = new Formatter(options); + String formatted = formatter.formatSource(input, characterRanges(input).asRanges()); + formatted = fixImports(formatted); + if (parameters.reflowLongStrings()) { + formatted = StringWrapper.wrap(Formatter.MAX_LINE_LENGTH, formatted, formatter); + } + return Result.create(path, input, formatted, /* exception= */ null); + } catch (FormatterException e) { + return Result.create(path, input, /* output= */ null, e); } - return formatted; } private String fixImports(String input) throws FormatterException { diff --git a/core/src/main/java/com/google/googlejavaformat/java/Main.java b/core/src/main/java/com/google/googlejavaformat/java/Main.java index 628c8bb2..9c60fcb1 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Main.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java @@ -16,6 +16,7 @@ import static java.lang.Math.min; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Comparator.comparing; import com.google.common.io.ByteStreams; import com.google.common.util.concurrent.MoreExecutors; @@ -30,13 +31,14 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.Collections; +import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; /** The main class for the Java formatter CLI. */ public final class Main { @@ -123,50 +125,54 @@ private int formatFiles(CommandLineOptions parameters, JavaFormatterOptions opti int numThreads = min(MAX_THREADS, parameters.files().size()); ExecutorService executorService = Executors.newFixedThreadPool(numThreads); - Map inputs = new LinkedHashMap<>(); - Map> results = new LinkedHashMap<>(); + ExecutorCompletionService cs = + new ExecutorCompletionService<>(executorService); boolean allOk = true; + int files = 0; for (String fileName : parameters.files()) { if (!fileName.endsWith(".java")) { errWriter.println("Skipping non-Java file: " + fileName); continue; } Path path = Paths.get(fileName); - String input; try { - input = new String(Files.readAllBytes(path), UTF_8); - inputs.put(path, input); - results.put( - path, executorService.submit(new FormatFileCallable(parameters, input, options))); + cs.submit(new FormatFileCallable(parameters, path, Files.readString(path), options)); + files++; } catch (IOException e) { errWriter.println(fileName + ": could not read file: " + e.getMessage()); allOk = false; } } - for (Map.Entry> result : results.entrySet()) { - Path path = result.getKey(); - String formatted; + List results = new ArrayList<>(); + while (files > 0) { try { - formatted = result.getValue().get(); + files--; + results.add(cs.take().get()); } catch (InterruptedException e) { errWriter.println(e.getMessage()); allOk = false; continue; } catch (ExecutionException e) { - if (e.getCause() instanceof FormatterException) { - for (FormatterDiagnostic diagnostic : ((FormatterException) e.getCause()).diagnostics()) { - errWriter.println(path + ":" + diagnostic); - } - } else { - errWriter.println(path + ": error: " + e.getCause().getMessage()); - e.getCause().printStackTrace(errWriter); + errWriter.println("error: " + e.getCause().getMessage()); + e.getCause().printStackTrace(errWriter); + allOk = false; + continue; + } + } + Collections.sort(results, comparing(FormatFileCallable.Result::path)); + for (FormatFileCallable.Result result : results) { + Path path = result.path(); + if (result.exception() != null) { + for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) { + errWriter.println(path + ":" + diagnostic); } allOk = false; continue; } - boolean changed = !formatted.equals(inputs.get(path)); + String formatted = result.output(); + boolean changed = result.changed(); if (changed && parameters.setExitIfChanged()) { allOk = false; } @@ -205,9 +211,16 @@ private int formatStdin(CommandLineOptions parameters, JavaFormatterOptions opti } String stdinFilename = parameters.assumeFilename().orElse(STDIN_FILENAME); boolean ok = true; - try { - String output = new FormatFileCallable(parameters, input, options).call(); - boolean changed = !input.equals(output); + FormatFileCallable.Result result = + new FormatFileCallable(parameters, null, input, options).call(); + if (result.exception() != null) { + for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) { + errWriter.println(stdinFilename + ":" + diagnostic); + } + ok = false; + } else { + String output = result.output(); + boolean changed = result.changed(); if (changed && parameters.setExitIfChanged()) { ok = false; } @@ -218,12 +231,6 @@ private int formatStdin(CommandLineOptions parameters, JavaFormatterOptions opti } else { outWriter.write(output); } - } catch (FormatterException e) { - for (FormatterDiagnostic diagnostic : e.diagnostics()) { - errWriter.println(stdinFilename + ":" + diagnostic); - } - ok = false; - // TODO(cpovirk): Catch other types of exception (as we do in the formatFiles case). } return ok ? 0 : 1; } From 8c85ac1e3a794cc42362b92fd4fd0de21e23790c Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 14 Feb 2023 15:00:23 -0800 Subject: [PATCH 08/23] Follow-up to https://github.com/google/google-java-format/commit/0a6a85e37f634fa870c920997d9270dca319cc03 See investigation in b/269345183 PiperOrigin-RevId: 509647966 --- core/src/main/java/com/google/googlejavaformat/java/Main.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/Main.java b/core/src/main/java/com/google/googlejavaformat/java/Main.java index 9c60fcb1..62423f22 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Main.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java @@ -137,7 +137,8 @@ private int formatFiles(CommandLineOptions parameters, JavaFormatterOptions opti } Path path = Paths.get(fileName); try { - cs.submit(new FormatFileCallable(parameters, path, Files.readString(path), options)); + String input = new String(Files.readAllBytes(path), UTF_8); + cs.submit(new FormatFileCallable(parameters, path, input, options)); files++; } catch (IOException e) { errWriter.println(fileName + ": could not read file: " + e.getMessage()); From 973cbff100aa1277ea97f26ffdbc2afd9cb0cb07 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 24 Feb 2023 13:44:10 -0800 Subject: [PATCH 09/23] Allow modifier reordering to be disabled This is currently the only non-whitespace change made as part of the core formatting logic. This is a requirement for updating the IntelliJ plugin to use the new supported plugin API. PiperOrigin-RevId: 512153706 --- .../googlejavaformat/java/Formatter.java | 4 +- .../java/JavaFormatterOptions.java | 50 +++++++------------ .../googlejavaformat/java/MainTest.java | 23 +++++++++ .../GoogleJavaFormatCodeStyleManager.java | 3 +- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java index 841e88a8..9ff702d5 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java @@ -262,7 +262,9 @@ public ImmutableList getFormatReplacements( // TODO(cushon): this is only safe because the modifier ordering doesn't affect whitespace, // and doesn't change the replacements that are output. This is not true in general for // 'de-linting' changes (e.g. import ordering). - javaInput = ModifierOrderer.reorderModifiers(javaInput, characterRanges); + if (options.reorderModifiers()) { + javaInput = ModifierOrderer.reorderModifiers(javaInput, characterRanges); + } String lineSeparator = Newlines.guessLineSeparator(input); JavaOutput javaOutput = diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java b/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java index fbb6fe7e..67c13d0b 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java @@ -14,6 +14,7 @@ package com.google.googlejavaformat.java; +import com.google.auto.value.AutoValue; import com.google.errorprone.annotations.Immutable; /** @@ -27,7 +28,8 @@ * preferences, and in fact it would work directly against our primary goals. */ @Immutable -public class JavaFormatterOptions { +@AutoValue +public abstract class JavaFormatterOptions { public enum Style { /** The default Google Java Style configuration. */ @@ -47,27 +49,17 @@ int indentationMultiplier() { } } - private final Style style; - private final boolean formatJavadoc; - - private JavaFormatterOptions(Style style, boolean formatJavadoc) { - this.style = style; - this.formatJavadoc = formatJavadoc; - } - /** Returns the multiplier for the unit of indent. */ public int indentationMultiplier() { - return style.indentationMultiplier(); + return style().indentationMultiplier(); } - public boolean formatJavadoc() { - return formatJavadoc; - } + public abstract boolean formatJavadoc(); + + public abstract boolean reorderModifiers(); /** Returns the code style. */ - public Style style() { - return style; - } + public abstract Style style(); /** Returns the default formatting options. */ public static JavaFormatterOptions defaultOptions() { @@ -76,28 +68,22 @@ public static JavaFormatterOptions defaultOptions() { /** Returns a builder for {@link JavaFormatterOptions}. */ public static Builder builder() { - return new Builder(); + return new AutoValue_JavaFormatterOptions.Builder() + .style(Style.GOOGLE) + .formatJavadoc(true) + .reorderModifiers(true); } /** A builder for {@link JavaFormatterOptions}. */ - public static class Builder { - private Style style = Style.GOOGLE; - private boolean formatJavadoc = true; + @AutoValue.Builder + public abstract static class Builder { - private Builder() {} + public abstract Builder style(Style style); - public Builder style(Style style) { - this.style = style; - return this; - } + public abstract Builder formatJavadoc(boolean formatJavadoc); - public Builder formatJavadoc(boolean formatJavadoc) { - this.formatJavadoc = formatJavadoc; - return this; - } + public abstract Builder reorderModifiers(boolean reorderModifiers); - public JavaFormatterOptions build() { - return new JavaFormatterOptions(style, formatJavadoc); - } + public abstract JavaFormatterOptions build(); } } diff --git a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java index d4a41090..ad91bfa2 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java @@ -613,4 +613,27 @@ public void noFormatJavadoc() throws Exception { assertThat(main.format("--skip-javadoc-formatting", "-")).isEqualTo(0); assertThat(out.toString()).isEqualTo(joiner.join(input)); } + + @Test + public void reorderModifiersOptionTest() throws Exception { + String[] input = { + "class Test {", // + " static public void main(String... args) {}", + "}", + "", + }; + String[] fixed = { + "class Test {", // + " public static void main(String... args) {}", + "}", + "", + }; + String source = joiner.join(input); + assertThat(new Formatter(JavaFormatterOptions.builder().build()).formatSource(source)) + .isEqualTo(joiner.join(fixed)); + assertThat( + new Formatter(JavaFormatterOptions.builder().reorderModifiers(false).build()) + .formatSource(source)) + .isEqualTo(source); + } } diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java b/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java index 3d56743e..c9aa64a6 100644 --- a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java +++ b/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java @@ -145,7 +145,8 @@ private void formatInternal(PsiFile file, Collection ranges */ private void format(Document document, Collection ranges) { Style style = GoogleJavaFormatSettings.getInstance(getProject()).getStyle(); - Formatter formatter = new Formatter(JavaFormatterOptions.builder().style(style).build()); + Formatter formatter = + new Formatter(JavaFormatterOptions.builder().style(style).reorderModifiers(false).build()); performReplacements( document, FormatterUtil.getReplacements(formatter, document.getText(), ranges)); } From eb7edafefc0cd2a2b2aaa35dff9ccdbf92d76eb4 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 24 Feb 2023 13:51:57 -0800 Subject: [PATCH 10/23] Update release.yml --- .github/workflows/release.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3f066df..6e72789c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,16 +12,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write - steps: - - name: Setup Signing Key - run: | - gpg-agent --daemon --default-cache-ttl 7200 - echo -e "${{ secrets.GPG_SIGNING_KEY }}" | gpg --batch --import --no-tty - echo "hello world" > temp.txt - gpg --detach-sig --yes -v --output=/dev/null --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" temp.txt - rm temp.txt - gpg --list-secret-keys --keyid-format LONG - + steps: - name: Checkout uses: actions/checkout@v2.4.0 @@ -34,6 +25,8 @@ jobs: server-id: sonatype-nexus-staging server-username: CI_DEPLOY_USERNAME server-password: CI_DEPLOY_PASSWORD + gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE - name: Bump Version Number run: | @@ -51,6 +44,7 @@ jobs: env: CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: mvn --no-transfer-progress -pl '!eclipse_plugin' -P sonatype-oss-release clean deploy -Dgpg.passphrase="${{ secrets.GPG_PASSPHRASE }}" From 64f98edf6aeef20a890f09122bb8a5729bc7585a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 27 Feb 2023 11:32:35 -0800 Subject: [PATCH 11/23] Update ci.yml --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79a260ea..716abcc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,9 @@ jobs: - os: ubuntu-latest java: 20-ea experimental: true + - os: ubuntu-latest + java: 21-ea + experimental: true runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} steps: From a7583fbb7776ee2f9398c280d78a68e161b929b0 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 27 Feb 2023 16:12:44 -0800 Subject: [PATCH 12/23] Prepare for a change to `JCTree.getQualifiedIdentifier ` https://github.com/openjdk/jdk/commit/a917fb3fcf0fe1a4c4de86c08ae4041462848b82 Fixes: https://github.com/google/google-java-format/issues/898 PiperOrigin-RevId: 512759236 --- .../java/RemoveUnusedImports.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java index ecb30eed..a0fc2f54 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java +++ b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java @@ -49,7 +49,6 @@ import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; -import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Log; @@ -293,9 +292,7 @@ private static RangeMap buildReplacements( } private static String getSimpleName(JCImport importTree) { - return importTree.getQualifiedIdentifier() instanceof JCIdent - ? ((JCIdent) importTree.getQualifiedIdentifier()).getName().toString() - : ((JCFieldAccess) importTree.getQualifiedIdentifier()).getIdentifier().toString(); + return getQualifiedIdentifier(importTree).getIdentifier().toString(); } private static boolean isUnused( @@ -304,18 +301,15 @@ private static boolean isUnused( Multimap> usedInJavadoc, JCImport importTree, String simpleName) { - String qualifier = - ((JCFieldAccess) importTree.getQualifiedIdentifier()).getExpression().toString(); + JCFieldAccess qualifiedIdentifier = getQualifiedIdentifier(importTree); + String qualifier = qualifiedIdentifier.getExpression().toString(); if (qualifier.equals("java.lang")) { return true; } if (unit.getPackageName() != null && unit.getPackageName().toString().equals(qualifier)) { return true; } - if (importTree.getQualifiedIdentifier() instanceof JCFieldAccess - && ((JCFieldAccess) importTree.getQualifiedIdentifier()) - .getIdentifier() - .contentEquals("*")) { + if (qualifiedIdentifier.getIdentifier().contentEquals("*")) { return false; } @@ -328,6 +322,15 @@ private static boolean isUnused( return true; } + private static JCFieldAccess getQualifiedIdentifier(JCImport importTree) { + // Use reflection because the return type is JCTree in some versions and JCFieldAccess in others + try { + return (JCFieldAccess) JCImport.class.getMethod("getQualifiedIdentifier").invoke(importTree); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } + /** Applies the replacements to the given source, and re-format any edited javadoc. */ private static String applyReplacements(String source, RangeMap replacements) { // save non-empty fixed ranges for reformatting after fixes are applied From 9412083f3b42bcafece6d784f5c34da895525b66 Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Tue, 28 Feb 2023 06:21:05 -0800 Subject: [PATCH 13/23] Use a standard gradle project structure. I'm going to add tests next, but with this weird file structure I've been using, there's no good place to put them. The only content changes here are in build.gradle. PiperOrigin-RevId: 512914783 --- idea_plugin/build.gradle | 7 ------- .../intellij/CodeStyleManagerDecorator.java | 0 .../google/googlejavaformat/intellij/FormatterUtil.java | 0 .../intellij/GoogleJavaFormatCodeStyleManager.java | 0 .../intellij/GoogleJavaFormatConfigurable.form | 0 .../intellij/GoogleJavaFormatConfigurable.java | 0 .../intellij/GoogleJavaFormatInstaller.java | 0 .../intellij/GoogleJavaFormatSettings.java | 0 .../InitialConfigurationProjectManagerListener.java | 0 .../google/googlejavaformat/intellij/UiFormatterStyle.java | 0 idea_plugin/{ => src/main}/resources/META-INF/plugin.xml | 0 11 files changed, 7 deletions(-) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/FormatterUtil.java (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java (100%) rename idea_plugin/src/{ => main/java}/com/google/googlejavaformat/intellij/UiFormatterStyle.java (100%) rename idea_plugin/{ => src/main}/resources/META-INF/plugin.xml (100%) diff --git a/idea_plugin/build.gradle b/idea_plugin/build.gradle index 294d77e7..6c9695d4 100644 --- a/idea_plugin/build.gradle +++ b/idea_plugin/build.gradle @@ -50,13 +50,6 @@ publishPlugin { token = project.ext.properties.jetbrainsPluginRepoToken } -sourceSets { - main { - java.srcDir "src" - resources.srcDir "resources" - } -} - dependencies { implementation "com.google.googlejavaformat:google-java-format:${googleJavaFormatVersion}" } diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/FormatterUtil.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/FormatterUtil.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/FormatterUtil.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/FormatterUtil.java diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/UiFormatterStyle.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java similarity index 100% rename from idea_plugin/src/com/google/googlejavaformat/intellij/UiFormatterStyle.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java diff --git a/idea_plugin/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml similarity index 100% rename from idea_plugin/resources/META-INF/plugin.xml rename to idea_plugin/src/main/resources/META-INF/plugin.xml From 84b2c9a2ba382ee7a968518b46afb0d6000ada8d Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Thu, 2 Mar 2023 09:40:06 -0800 Subject: [PATCH 14/23] Move the plugin to the FormattingService API. Also support optimizing imports, using the ImportOptimizer API. PiperOrigin-RevId: 513562756 --- idea_plugin/build.gradle | 18 +- .../intellij/CodeStyleManagerDecorator.java | 246 ----------------- .../intellij/FormatterUtil.java | 64 ----- .../GoogleJavaFormatCodeStyleManager.java | 173 ------------ .../GoogleJavaFormatFormattingService.java | 136 ++++++++++ .../GoogleJavaFormatImportOptimizer.java | 65 +++++ .../intellij/GoogleJavaFormatInstaller.java | 57 ---- .../intellij/Notifications.java | 38 +++ .../src/main/resources/META-INF/plugin.xml | 19 +- ...GoogleJavaFormatFormattingServiceTest.java | 250 ++++++++++++++++++ .../GoogleJavaFormatImportOptimizerTest.java | 168 ++++++++++++ 11 files changed, 683 insertions(+), 551 deletions(-) delete mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java delete mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/FormatterUtil.java delete mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java create mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java create mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java delete mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java create mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/Notifications.java create mode 100644 idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java create mode 100644 idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizerTest.java diff --git a/idea_plugin/build.gradle b/idea_plugin/build.gradle index 6c9695d4..73214867 100644 --- a/idea_plugin/build.gradle +++ b/idea_plugin/build.gradle @@ -15,7 +15,7 @@ */ plugins { - id "org.jetbrains.intellij" version "1.4.0" + id "org.jetbrains.intellij" version "1.13.0" } repositories { @@ -23,7 +23,7 @@ repositories { } ext { - googleJavaFormatVersion = "1.15.0" + googleJavaFormatVersion = "1.16.0" } apply plugin: "org.jetbrains.intellij" @@ -35,14 +35,14 @@ targetCompatibility = JavaVersion.VERSION_11 intellij { pluginName = "google-java-format" plugins = ["java"] - version = "221.3427-EAP-CANDIDATE-SNAPSHOT" + version = "2021.3" } patchPluginXml { pluginDescription = "Formats source code using the google-java-format tool. This version of " + "the plugin uses version ${googleJavaFormatVersion} of the tool." version.set("${googleJavaFormatVersion}.0") - sinceBuild = "203" + sinceBuild = "213" untilBuild = "" } @@ -50,6 +50,16 @@ publishPlugin { token = project.ext.properties.jetbrainsPluginRepoToken } +tasks.withType(Test).configureEach { + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" +} + dependencies { implementation "com.google.googlejavaformat:google-java-format:${googleJavaFormatVersion}" + testImplementation "junit:junit:4.13.2" + testImplementation "com.google.truth:truth:1.1.3" + testImplementation "com.google.truth.extensions:truth-java8-extension:1.1.3" } diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java deleted file mode 100644 index af5da957..00000000 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * 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.google.googlejavaformat.intellij; - -import com.intellij.formatting.FormattingMode; -import com.intellij.lang.ASTNode; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.ChangedRangesInfo; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.codeStyle.DocCommentSettings; -import com.intellij.psi.codeStyle.FormattingModeAwareIndentAdjuster; -import com.intellij.psi.codeStyle.Indent; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.ThrowableRunnable; -import java.util.Collection; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.NotNull; - -/** - * Decorates the {@link CodeStyleManager} abstract class by delegating to a concrete implementation - * instance (likely IntelliJ's default instance). - */ -@SuppressWarnings("deprecation") -class CodeStyleManagerDecorator extends CodeStyleManager - implements FormattingModeAwareIndentAdjuster { - - private final CodeStyleManager delegate; - - CodeStyleManagerDecorator(CodeStyleManager delegate) { - this.delegate = delegate; - } - - CodeStyleManager getDelegate() { - return delegate; - } - - @Override - public @NotNull Project getProject() { - return delegate.getProject(); - } - - @Override - public @NotNull PsiElement reformat(@NotNull PsiElement element) - throws IncorrectOperationException { - return delegate.reformat(element); - } - - @Override - public @NotNull PsiElement reformat(@NotNull PsiElement element, boolean canChangeWhiteSpacesOnly) - throws IncorrectOperationException { - return delegate.reformat(element, canChangeWhiteSpacesOnly); - } - - @Override - public PsiElement reformatRange(@NotNull PsiElement element, int startOffset, int endOffset) - throws IncorrectOperationException { - return delegate.reformatRange(element, startOffset, endOffset); - } - - @Override - public PsiElement reformatRange( - @NotNull PsiElement element, int startOffset, int endOffset, boolean canChangeWhiteSpacesOnly) - throws IncorrectOperationException { - return delegate.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly); - } - - @Override - public void reformatText(@NotNull PsiFile file, int startOffset, int endOffset) - throws IncorrectOperationException { - delegate.reformatText(file, startOffset, endOffset); - } - - @Override - public void reformatText(@NotNull PsiFile file, @NotNull Collection ranges) - throws IncorrectOperationException { - delegate.reformatText(file, ranges); - } - - @Override - public void reformatTextWithContext( - @NotNull PsiFile psiFile, @NotNull ChangedRangesInfo changedRangesInfo) - throws IncorrectOperationException { - delegate.reformatTextWithContext(psiFile, changedRangesInfo); - } - - @Override - public void reformatTextWithContext( - @NotNull PsiFile file, @NotNull Collection ranges) - throws IncorrectOperationException { - delegate.reformatTextWithContext(file, ranges); - } - - @Override - public void adjustLineIndent(@NotNull PsiFile file, TextRange rangeToAdjust) - throws IncorrectOperationException { - delegate.adjustLineIndent(file, rangeToAdjust); - } - - @Override - public int adjustLineIndent(@NotNull PsiFile file, int offset) - throws IncorrectOperationException { - return delegate.adjustLineIndent(file, offset); - } - - @Override - public int adjustLineIndent(@NotNull Document document, int offset) { - return delegate.adjustLineIndent(document, offset); - } - - public void scheduleIndentAdjustment(@NotNull Document document, int offset) { - delegate.scheduleIndentAdjustment(document, offset); - } - - @Override - public boolean isLineToBeIndented(@NotNull PsiFile file, int offset) { - return delegate.isLineToBeIndented(file, offset); - } - - @Override - @Nullable - public String getLineIndent(@NotNull PsiFile file, int offset) { - return delegate.getLineIndent(file, offset); - } - - @Override - @Nullable - public String getLineIndent(@NotNull PsiFile file, int offset, FormattingMode mode) { - return delegate.getLineIndent(file, offset, mode); - } - - @Override - @Nullable - public String getLineIndent(@NotNull Document document, int offset) { - return delegate.getLineIndent(document, offset); - } - - @Override - public Indent getIndent(String text, FileType fileType) { - return delegate.getIndent(text, fileType); - } - - @Override - public String fillIndent(Indent indent, FileType fileType) { - return delegate.fillIndent(indent, fileType); - } - - @Override - public Indent zeroIndent() { - return delegate.zeroIndent(); - } - - @Override - public void reformatNewlyAddedElement(@NotNull ASTNode block, @NotNull ASTNode addedElement) - throws IncorrectOperationException { - delegate.reformatNewlyAddedElement(block, addedElement); - } - - @Override - public boolean isSequentialProcessingAllowed() { - return delegate.isSequentialProcessingAllowed(); - } - - @Override - public void performActionWithFormatterDisabled(Runnable r) { - delegate.performActionWithFormatterDisabled(r); - } - - @Override - public void performActionWithFormatterDisabled(ThrowableRunnable r) - throws T { - delegate.performActionWithFormatterDisabled(r); - } - - @Override - public T performActionWithFormatterDisabled(Computable r) { - return delegate.performActionWithFormatterDisabled(r); - } - - @Override - public int getSpacing(@NotNull PsiFile file, int offset) { - return delegate.getSpacing(file, offset); - } - - @Override - public int getMinLineFeeds(@NotNull PsiFile file, int offset) { - return delegate.getMinLineFeeds(file, offset); - } - - @Override - public void runWithDocCommentFormattingDisabled( - @NotNull PsiFile file, @NotNull Runnable runnable) { - delegate.runWithDocCommentFormattingDisabled(file, runnable); - } - - @Override - public @NotNull DocCommentSettings getDocCommentSettings(@NotNull PsiFile file) { - return delegate.getDocCommentSettings(file); - } - - // From FormattingModeAwareIndentAdjuster - - /** Uses same fallback as {@link CodeStyleManager#getCurrentFormattingMode}. */ - @Override - public FormattingMode getCurrentFormattingMode() { - if (delegate instanceof FormattingModeAwareIndentAdjuster) { - return ((FormattingModeAwareIndentAdjuster) delegate).getCurrentFormattingMode(); - } - return FormattingMode.REFORMAT; - } - - @Override - public int adjustLineIndent( - final @NotNull Document document, final int offset, FormattingMode mode) - throws IncorrectOperationException { - if (delegate instanceof FormattingModeAwareIndentAdjuster) { - return ((FormattingModeAwareIndentAdjuster) delegate) - .adjustLineIndent(document, offset, mode); - } - return offset; - } - - @Override - public void scheduleReformatWhenSettingsComputed(@NotNull PsiFile file) { - delegate.scheduleReformatWhenSettingsComputed(file); - } -} diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/FormatterUtil.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/FormatterUtil.java deleted file mode 100644 index a5e69c93..00000000 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/FormatterUtil.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * 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.google.googlejavaformat.intellij; - -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.collect.BoundType; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Range; -import com.google.googlejavaformat.java.Formatter; -import com.google.googlejavaformat.java.FormatterException; -import com.intellij.openapi.util.TextRange; -import java.util.Collection; -import java.util.Map; -import java.util.stream.Collectors; - -final class FormatterUtil { - - private FormatterUtil() {} - - static Map getReplacements( - Formatter formatter, String text, Collection ranges) { - try { - ImmutableMap.Builder replacements = ImmutableMap.builder(); - formatter - .getFormatReplacements(text, toRanges(ranges)) - .forEach( - replacement -> - replacements.put( - toTextRange(replacement.getReplaceRange()), - replacement.getReplacementString())); - return replacements.build(); - } catch (FormatterException e) { - return ImmutableMap.of(); - } - } - - private static Collection> toRanges(Collection textRanges) { - return textRanges.stream() - .map(textRange -> Range.closedOpen(textRange.getStartOffset(), textRange.getEndOffset())) - .collect(Collectors.toList()); - } - - private static TextRange toTextRange(Range range) { - checkState( - range.lowerBoundType().equals(BoundType.CLOSED) - && range.upperBoundType().equals(BoundType.OPEN)); - return new TextRange(range.lowerEndpoint(), range.upperEndpoint()); - } -} diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java deleted file mode 100644 index c9aa64a6..00000000 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * 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.google.googlejavaformat.intellij; - -import static java.util.Comparator.comparing; - -import com.google.common.collect.ImmutableList; -import com.google.googlejavaformat.java.Formatter; -import com.google.googlejavaformat.java.JavaFormatterOptions; -import com.google.googlejavaformat.java.JavaFormatterOptions.Style; -import com.intellij.ide.highlighter.JavaFileType; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.ChangedRangesInfo; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.impl.CheckUtil; -import com.intellij.util.IncorrectOperationException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; -import org.jetbrains.annotations.NotNull; - -/** - * A {@link CodeStyleManager} implementation which formats .java files with google-java-format. - * Formatting of all other types of files is delegated to IntelliJ's default implementation. - */ -class GoogleJavaFormatCodeStyleManager extends CodeStyleManagerDecorator { - - public GoogleJavaFormatCodeStyleManager(@NotNull CodeStyleManager original) { - super(original); - } - - @Override - public void reformatText(@NotNull PsiFile file, int startOffset, int endOffset) - throws IncorrectOperationException { - if (overrideFormatterForFile(file)) { - formatInternal(file, ImmutableList.of(new TextRange(startOffset, endOffset))); - } else { - super.reformatText(file, startOffset, endOffset); - } - } - - @Override - public void reformatText(@NotNull PsiFile file, @NotNull Collection ranges) - throws IncorrectOperationException { - if (overrideFormatterForFile(file)) { - formatInternal(file, ranges); - } else { - super.reformatText(file, ranges); - } - } - - @Override - public void reformatTextWithContext(@NotNull PsiFile file, @NotNull ChangedRangesInfo info) - throws IncorrectOperationException { - List ranges = new ArrayList<>(); - if (info.insertedRanges != null) { - ranges.addAll(info.insertedRanges); - } - ranges.addAll(info.allChangedRanges); - reformatTextWithContext(file, ranges); - } - - @Override - public void reformatTextWithContext( - @NotNull PsiFile file, @NotNull Collection ranges) { - if (overrideFormatterForFile(file)) { - formatInternal(file, ranges); - } else { - super.reformatTextWithContext(file, ranges); - } - } - - @Override - public PsiElement reformatRange( - @NotNull PsiElement element, - int startOffset, - int endOffset, - boolean canChangeWhiteSpacesOnly) { - // Only handle elements that are PsiFile for now -- otherwise we need to search for some - // element within the file at new locations given the original startOffset and endOffsets - // to serve as the return value. - PsiFile file = element instanceof PsiFile ? (PsiFile) element : null; - if (file != null && canChangeWhiteSpacesOnly && overrideFormatterForFile(file)) { - formatInternal(file, ImmutableList.of(new TextRange(startOffset, endOffset))); - return file; - } else { - return super.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly); - } - } - - /** Return whether this formatter can handle formatting the given file. */ - private boolean overrideFormatterForFile(PsiFile file) { - return JavaFileType.INSTANCE.equals(file.getFileType()) - && GoogleJavaFormatSettings.getInstance(getProject()).isEnabled(); - } - - private void formatInternal(PsiFile file, Collection ranges) { - ApplicationManager.getApplication().assertWriteAccessAllowed(); - PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); - documentManager.commitAllDocuments(); - CheckUtil.checkWritable(file); - - Document document = documentManager.getDocument(file); - - if (document == null) { - return; - } - // If there are postponed PSI changes (e.g., during a refactoring), just abort. - // If we apply them now, then the incoming text ranges may no longer be valid. - if (documentManager.isDocumentBlockedByPsi(document)) { - return; - } - - format(document, ranges); - } - - /** - * Format the ranges of the given document. - * - *

Overriding methods will need to modify the document with the result of the external - * formatter (usually using {@link #performReplacements(Document, Map)}). - */ - private void format(Document document, Collection ranges) { - Style style = GoogleJavaFormatSettings.getInstance(getProject()).getStyle(); - Formatter formatter = - new Formatter(JavaFormatterOptions.builder().style(style).reorderModifiers(false).build()); - performReplacements( - document, FormatterUtil.getReplacements(formatter, document.getText(), ranges)); - } - - private void performReplacements( - final Document document, final Map replacements) { - - if (replacements.isEmpty()) { - return; - } - - TreeMap sorted = new TreeMap<>(comparing(TextRange::getStartOffset)); - sorted.putAll(replacements); - WriteCommandAction.runWriteCommandAction( - getProject(), - () -> { - for (Entry entry : sorted.descendingMap().entrySet()) { - document.replaceString( - entry.getKey().getStartOffset(), entry.getKey().getEndOffset(), entry.getValue()); - } - PsiDocumentManager.getInstance(getProject()).commitDocument(document); - }); - } -} diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java new file mode 100644 index 00000000..1e7ca1a6 --- /dev/null +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java @@ -0,0 +1,136 @@ +/* + * Copyright 2023 Google Inc. All Rights Reserved. + * + * 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.google.googlejavaformat.intellij; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Range; +import com.google.googlejavaformat.java.Formatter; +import com.google.googlejavaformat.java.FormatterException; +import com.google.googlejavaformat.java.JavaFormatterOptions; +import com.google.googlejavaformat.java.JavaFormatterOptions.Style; +import com.intellij.formatting.service.AsyncDocumentFormattingService; +import com.intellij.formatting.service.AsyncFormattingRequest; +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.lang.ImportOptimizer; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiFile; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import org.jetbrains.annotations.NotNull; + +/** Uses {@code google-java-format} to reformat code. */ +public class GoogleJavaFormatFormattingService extends AsyncDocumentFormattingService { + + public static final ImmutableSet IMPORT_OPTIMIZERS = + ImmutableSet.of(new GoogleJavaFormatImportOptimizer()); + + @Override + protected FormattingTask createFormattingTask(AsyncFormattingRequest request) { + Project project = request.getContext().getProject(); + Style style = GoogleJavaFormatSettings.getInstance(project).getStyle(); + Formatter formatter = createFormatter(style, request.canChangeWhitespaceOnly()); + return new GoogleJavaFormatFormattingTask(formatter, request); + } + + @Override + protected String getNotificationGroupId() { + return Notifications.PARSING_ERROR_NOTIFICATION_GROUP; + } + + @Override + protected String getName() { + return "google-java-format"; + } + + private static Formatter createFormatter(Style style, boolean canChangeWhiteSpaceOnly) { + JavaFormatterOptions.Builder optBuilder = JavaFormatterOptions.builder().style(style); + if (canChangeWhiteSpaceOnly) { + optBuilder.formatJavadoc(false).reorderModifiers(false); + } + return new Formatter(optBuilder.build()); + } + + @Override + public @NotNull Set getFeatures() { + return Set.of(Feature.AD_HOC_FORMATTING, Feature.FORMAT_FRAGMENTS, Feature.OPTIMIZE_IMPORTS); + } + + @Override + public boolean canFormat(@NotNull PsiFile file) { + return JavaFileType.INSTANCE.equals(file.getFileType()) + && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled(); + } + + @Override + public @NotNull Set getImportOptimizers(@NotNull PsiFile file) { + return IMPORT_OPTIMIZERS; + } + + private static final class GoogleJavaFormatFormattingTask implements FormattingTask { + private final Formatter formatter; + private final AsyncFormattingRequest request; + + private GoogleJavaFormatFormattingTask(Formatter formatter, AsyncFormattingRequest request) { + this.formatter = formatter; + this.request = request; + } + + @Override + public void run() { + try { + String formattedText = formatter.formatSource(request.getDocumentText(), toRanges(request)); + request.onTextReady(formattedText); + } catch (FormatterException e) { + request.onError( + Notifications.PARSING_ERROR_TITLE, + Notifications.parsingErrorMessage(request.getContext().getContainingFile().getName())); + } + } + + private static Collection> toRanges(AsyncFormattingRequest request) { + if (isWholeFile(request)) { + // The IDE sometimes passes invalid ranges when the file is unsaved before invoking the + // formatter. So this is a workaround for that issue. + return ImmutableList.of(Range.closedOpen(0, request.getDocumentText().length())); + } + return request.getFormattingRanges().stream() + .map(textRange -> Range.closedOpen(textRange.getStartOffset(), textRange.getEndOffset())) + .collect(ImmutableList.toImmutableList()); + } + + private static boolean isWholeFile(AsyncFormattingRequest request) { + List ranges = request.getFormattingRanges(); + return ranges.size() == 1 + && ranges.get(0).getStartOffset() == 0 + // using greater than or equal because ranges are sometimes passed inaccurately + && ranges.get(0).getEndOffset() >= request.getDocumentText().length(); + } + + @Override + public boolean isRunUnderProgress() { + return true; + } + + @Override + public boolean cancel() { + return false; + } + } +} diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java new file mode 100644 index 00000000..3a9a30f4 --- /dev/null +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Google Inc. All Rights Reserved. + * + * 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.google.googlejavaformat.intellij; + +import com.google.common.util.concurrent.Runnables; +import com.google.googlejavaformat.java.FormatterException; +import com.google.googlejavaformat.java.ImportOrderer; +import com.google.googlejavaformat.java.JavaFormatterOptions; +import com.google.googlejavaformat.java.RemoveUnusedImports; +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.lang.ImportOptimizer; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +/** Uses {@code google-java-format} to optimize imports. */ +public class GoogleJavaFormatImportOptimizer implements ImportOptimizer { + + @Override + public boolean supports(@NotNull PsiFile file) { + return JavaFileType.INSTANCE.equals(file.getFileType()) + && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled(); + } + + @Override + public @NotNull Runnable processFile(@NotNull PsiFile file) { + Project project = file.getProject(); + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); + Document document = documentManager.getDocument(file); + + if (document == null) { + return Runnables.doNothing(); + } + + JavaFormatterOptions.Style style = GoogleJavaFormatSettings.getInstance(project).getStyle(); + + String text; + try { + text = + ImportOrderer.reorderImports( + RemoveUnusedImports.removeUnusedImports(document.getText()), style); + } catch (FormatterException e) { + Notifications.displayParsingErrorNotification(project, file.getName()); + return Runnables.doNothing(); + } + + return () -> document.setText(text); + } +} diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java deleted file mode 100644 index c6067360..00000000 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * 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.google.googlejavaformat.intellij; - -import static com.google.common.base.Preconditions.checkState; - -import com.intellij.ide.plugins.IdeaPluginDescriptor; -import com.intellij.ide.plugins.PluginManagerCore; -import com.intellij.openapi.extensions.PluginId; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManagerListener; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.serviceContainer.ComponentManagerImpl; -import org.jetbrains.annotations.NotNull; - -/** - * A component that replaces the default IntelliJ {@link CodeStyleManager} with one that formats via - * google-java-format. - */ -final class GoogleJavaFormatInstaller implements ProjectManagerListener { - - @Override - public void projectOpened(@NotNull Project project) { - installFormatter(project); - } - - private static void installFormatter(Project project) { - CodeStyleManager currentManager = CodeStyleManager.getInstance(project); - - if (currentManager instanceof GoogleJavaFormatCodeStyleManager) { - currentManager = ((GoogleJavaFormatCodeStyleManager) currentManager).getDelegate(); - } - - setManager(project, new GoogleJavaFormatCodeStyleManager(currentManager)); - } - - private static void setManager(Project project, CodeStyleManager newManager) { - ComponentManagerImpl platformComponentManager = (ComponentManagerImpl) project; - IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(PluginId.getId("google-java-format")); - checkState(plugin != null, "Couldn't locate our own PluginDescriptor."); - platformComponentManager.registerServiceInstance(CodeStyleManager.class, newManager, plugin); - } -} diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/Notifications.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/Notifications.java new file mode 100644 index 00000000..d32aa98b --- /dev/null +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/Notifications.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Google Inc. All Rights Reserved. + * + * 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.google.googlejavaformat.intellij; + +import com.intellij.formatting.service.FormattingNotificationService; +import com.intellij.openapi.project.Project; + +class Notifications { + + static final String PARSING_ERROR_NOTIFICATION_GROUP = "google-java-format parsing error"; + static final String PARSING_ERROR_TITLE = PARSING_ERROR_NOTIFICATION_GROUP; + + static String parsingErrorMessage(String filename) { + return "google-java-format failed. Does " + filename + " have syntax errors?"; + } + + static void displayParsingErrorNotification(Project project, String filename) { + FormattingNotificationService.getInstance(project) + .reportError( + Notifications.PARSING_ERROR_NOTIFICATION_GROUP, + Notifications.PARSING_ERROR_TITLE, + Notifications.parsingErrorMessage(filename)); + } +} diff --git a/idea_plugin/src/main/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml index 42d5f3b3..2a82c955 100644 --- a/idea_plugin/src/main/resources/META-INF/plugin.xml +++ b/idea_plugin/src/main/resources/META-INF/plugin.xml @@ -22,13 +22,16 @@ Google - - com.intellij.java + com.intellij.modules.java + com.intellij.modules.lang + com.intellij.modules.platform +

1.16.0.0
+
Updated to use google-java-format 1.16.0.
+
Use the new IDE formatting APIs for a simplified plugin.
+
Optimize Imports now uses google-java-format.
1.15.0.0
Updated to use google-java-format 1.15.0.
1.14.0.0
@@ -66,11 +69,11 @@ - + + isLogByDefault="false"/> + diff --git a/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java new file mode 100644 index 00000000..fec086c6 --- /dev/null +++ b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java @@ -0,0 +1,250 @@ +/* + * Copyright 2023 Google Inc. All Rights Reserved. + * + * 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.google.googlejavaformat.intellij; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.googlejavaformat.intellij.GoogleJavaFormatSettings.State; +import com.google.googlejavaformat.java.Formatter; +import com.google.googlejavaformat.java.JavaFormatterOptions; +import com.google.googlejavaformat.java.JavaFormatterOptions.Style; +import com.intellij.formatting.service.AsyncFormattingRequest; +import com.intellij.formatting.service.FormattingService; +import com.intellij.formatting.service.FormattingServiceUtil; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.testFramework.ExtensionTestUtil; +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; +import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TestFixtureBuilder; +import java.io.IOException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class GoogleJavaFormatFormattingServiceTest { + private JavaCodeInsightTestFixture fixture; + private GoogleJavaFormatSettings settings; + private DelegatingFormatter delegatingFormatter; + + @Before + public void setUp() throws Exception { + TestFixtureBuilder projectBuilder = + IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getClass().getName()); + fixture = + JavaTestFixtureFactory.getFixtureFactory() + .createCodeInsightFixture(projectBuilder.getFixture()); + fixture.setUp(); + + delegatingFormatter = new DelegatingFormatter(); + ExtensionTestUtil.maskExtensions( + FormattingService.EP_NAME, + ImmutableList.of(delegatingFormatter), + fixture.getProjectDisposable()); + + settings = GoogleJavaFormatSettings.getInstance(fixture.getProject()); + State resetState = new State(); + resetState.setEnabled("true"); + settings.loadState(resetState); + } + + @After + public void tearDown() throws Exception { + fixture.tearDown(); + } + + @Test + public void defaultFormatSettings() throws Exception { + PsiFile file = + createPsiFile( + "com/foo/FormatTest.java", + "package com.foo;", + "public class FormatTest {", + "static final String CONST_STR = \"Hello\";", + "}"); + String origText = file.getText(); + CodeStyleManager manager = CodeStyleManager.getInstance(file.getProject()); + WriteCommandAction.runWriteCommandAction( + file.getProject(), () -> manager.reformatText(file, 0, file.getTextLength())); + + assertThat(file.getText()).isEqualTo(new Formatter().formatSource(origText)); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + @Test + public void aospStyle() throws Exception { + settings.setStyle(Style.AOSP); + PsiFile file = + createPsiFile( + "com/foo/FormatTest.java", + "package com.foo;", + "public class FormatTest {", + "static final String CONST_STR = \"Hello\";", + "}"); + String origText = file.getText(); + CodeStyleManager manager = CodeStyleManager.getInstance(file.getProject()); + WriteCommandAction.runWriteCommandAction( + file.getProject(), () -> manager.reformatText(file, 0, file.getTextLength())); + + assertThat(file.getText()) + .isEqualTo( + new Formatter(JavaFormatterOptions.builder().style(Style.AOSP).build()) + .formatSource(origText)); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + @Test + public void canChangeWhitespaceOnlyDoesNotReorderModifiers() throws Exception { + settings.setStyle(Style.GOOGLE); + PsiFile file = + createPsiFile( + "com/foo/FormatTest.java", + "package com.foo;", + "public class FormatTest {", + "final static String CONST_STR = \"Hello\";", + "}"); + CodeStyleManager manager = CodeStyleManager.getInstance(file.getProject()); + var offset = file.getText().indexOf("final static"); + WriteCommandAction.runWriteCommandAction( + file.getProject(), + () -> + FormattingServiceUtil.formatElement( + file.findElementAt(offset), /* canChangeWhitespaceOnly= */ true)); + + // In non-whitespace mode, this would flip the order of these modifiers. (Also check for leading + // spaces to make sure the formatter actually ran. + assertThat(file.getText()).containsMatch(" final static"); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + @Test + public void canChangeWhitespaceOnlyDoesNotReformatJavadoc() throws Exception { + settings.setStyle(Style.GOOGLE); + PsiFile file = + createPsiFile( + "com/foo/FormatTest.java", + "package com.foo;", + "public class FormatTest {", + "/**", + " * hello", + " */", + "static final String CONST_STR = \"Hello\";", + "}"); + CodeStyleManager manager = CodeStyleManager.getInstance(file.getProject()); + var offset = file.getText().indexOf("hello"); + WriteCommandAction.runWriteCommandAction( + file.getProject(), + () -> + FormattingServiceUtil.formatElement( + file.findElementAt(offset), /* canChangeWhitespaceOnly= */ true)); + + // In non-whitespace mode, this would join the Javadoc into a single line. + assertThat(file.getText()).containsMatch(" \\* hello"); + // Also check for leading spaces to make sure the formatter actually ran. (Technically, this is + // outside the range that we asked to be formatted, but gjf will still format it.) + assertThat(file.getText()).containsMatch(" static final"); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + @Test + public void canChangeNonWhitespaceReordersModifiers() throws Exception { + settings.setStyle(Style.GOOGLE); + PsiFile file = + createPsiFile( + "com/foo/FormatTest.java", + "package com.foo;", + "public class FormatTest {", + "final static String CONST_STR = \"Hello\";", + "}"); + CodeStyleManager manager = CodeStyleManager.getInstance(file.getProject()); + var offset = file.getText().indexOf("final static"); + WriteCommandAction.runWriteCommandAction( + file.getProject(), + () -> + FormattingServiceUtil.formatElement( + file.findElementAt(offset), /* canChangeWhitespaceOnly= */ false)); + + assertThat(file.getText()).containsMatch("static final"); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + @Test + public void canChangeNonWhitespaceReformatsJavadoc() throws Exception { + settings.setStyle(Style.GOOGLE); + PsiFile file = + createPsiFile( + "com/foo/FormatTest.java", + "package com.foo;", + "public class FormatTest {", + "/**", + " * hello", + " */", + "static final String CONST_STR = \"Hello\";", + "}"); + CodeStyleManager manager = CodeStyleManager.getInstance(file.getProject()); + var offset = file.getText().indexOf("hello"); + WriteCommandAction.runWriteCommandAction( + file.getProject(), + () -> + FormattingServiceUtil.formatElement( + file.findElementAt(offset), /* canChangeWhitespaceOnly= */ false)); + + assertThat(file.getText()).containsMatch("/\\*\\* hello \\*/"); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + private PsiFile createPsiFile(String path, String... contents) throws IOException { + VirtualFile virtualFile = + fixture.getTempDirFixture().createFile(path, String.join("\n", contents)); + fixture.configureFromExistingVirtualFile(virtualFile); + PsiFile psiFile = fixture.getFile(); + assertThat(psiFile).isNotNull(); + return psiFile; + } + + private static final class DelegatingFormatter extends GoogleJavaFormatFormattingService { + + private boolean invoked = false; + + private boolean wasInvoked() { + return invoked; + } + + @Override + protected FormattingTask createFormattingTask(AsyncFormattingRequest request) { + FormattingTask delegateTask = super.createFormattingTask(request); + return new FormattingTask() { + @Override + public boolean cancel() { + return delegateTask.cancel(); + } + + @Override + public void run() { + invoked = true; + delegateTask.run(); + } + }; + } + } +} diff --git a/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizerTest.java b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizerTest.java new file mode 100644 index 00000000..ad9fe274 --- /dev/null +++ b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizerTest.java @@ -0,0 +1,168 @@ +/* + * Copyright 2023 Google Inc. All Rights Reserved. + * + * 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.google.googlejavaformat.intellij; + +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.googlejavaformat.intellij.GoogleJavaFormatSettings.State; +import com.intellij.codeInsight.actions.OptimizeImportsProcessor; +import com.intellij.formatting.service.FormattingService; +import com.intellij.lang.ImportOptimizer; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.ExtensionTestUtil; +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; +import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TestFixtureBuilder; +import java.io.IOException; +import java.util.Set; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class GoogleJavaFormatImportOptimizerTest { + private JavaCodeInsightTestFixture fixture; + private DelegatingFormatter delegatingFormatter; + + @Before + public void setUp() throws Exception { + TestFixtureBuilder projectBuilder = + IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getClass().getName()); + fixture = + JavaTestFixtureFactory.getFixtureFactory() + .createCodeInsightFixture(projectBuilder.getFixture()); + fixture.setUp(); + + delegatingFormatter = new DelegatingFormatter(); + ExtensionTestUtil.maskExtensions( + FormattingService.EP_NAME, + ImmutableList.of(delegatingFormatter), + fixture.getProjectDisposable()); + + var settings = GoogleJavaFormatSettings.getInstance(fixture.getProject()); + State resetState = new State(); + resetState.setEnabled("true"); + settings.loadState(resetState); + } + + @After + public void tearDown() throws Exception { + fixture.tearDown(); + } + + @Test + public void removesUnusedImports() throws Exception { + PsiFile file = + createPsiFile( + "com/foo/ImportTest.java", + "package com.foo;", + "import java.util.List;", + "import java.util.ArrayList;", + "import java.util.Map;", + "public class ImportTest {", + "static final Map map;", + "}"); + OptimizeImportsProcessor processor = new OptimizeImportsProcessor(file.getProject(), file); + WriteCommandAction.runWriteCommandAction( + file.getProject(), + () -> { + processor.run(); + PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments(); + }); + + assertThat(file.getText()).doesNotContain("List"); + assertThat(file.getText()).contains("import java.util.Map;"); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + @Test + public void reordersImports() throws Exception { + PsiFile file = + createPsiFile( + "com/foo/ImportTest.java", + "package com.foo;", + "import java.util.List;", + "import java.util.ArrayList;", + "import java.util.Map;", + "public class ImportTest {", + "static final ArrayList arrayList;", + "static final List list;", + "static final Map map;", + "}"); + OptimizeImportsProcessor processor = new OptimizeImportsProcessor(file.getProject(), file); + WriteCommandAction.runWriteCommandAction( + file.getProject(), + () -> { + processor.run(); + PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments(); + }); + + assertThat(file.getText()) + .contains( + "import java.util.ArrayList;\n" + + "import java.util.List;\n" + + "import java.util.Map;\n"); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + + private PsiFile createPsiFile(String path, String... contents) throws IOException { + VirtualFile virtualFile = + fixture.getTempDirFixture().createFile(path, String.join("\n", contents)); + fixture.configureFromExistingVirtualFile(virtualFile); + PsiFile psiFile = fixture.getFile(); + assertThat(psiFile).isNotNull(); + return psiFile; + } + + private static final class DelegatingFormatter extends GoogleJavaFormatFormattingService { + + private boolean invoked = false; + + private boolean wasInvoked() { + return invoked; + } + + @Override + public @NotNull Set getImportOptimizers(@NotNull PsiFile file) { + return super.getImportOptimizers(file).stream().map(this::wrap).collect(toImmutableSet()); + } + + private ImportOptimizer wrap(ImportOptimizer delegate) { + return new ImportOptimizer() { + @Override + public boolean supports(@NotNull PsiFile file) { + return delegate.supports(file); + } + + @Override + public @NotNull Runnable processFile(@NotNull PsiFile file) { + return () -> { + invoked = true; + delegate.processFile(file).run(); + }; + } + }; + } + } +} From 0644d4c67ba42845f82eb23405f09fc1b97a7cff Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 2 Mar 2023 11:14:37 -0800 Subject: [PATCH 15/23] Make the `-add-exports=` passed to g-j-f consistent PiperOrigin-RevId: 513589436 --- README.md | 1 + core/pom.xml | 5 ++--- .../java/com/google/googlejavaformat/java/MainTest.java | 7 +++---- pom.xml | 5 +---- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2d53f8ae..f2c61e3d 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ following JVM flags are required when running on JDK 16 and newer, due to ``` --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED diff --git a/core/pom.xml b/core/pom.xml index 10565c66..038e4eda 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -102,13 +102,12 @@ https://docs.oracle.com/en/java/javase/11/docs/api + --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED,com.google.googlejavaformat + --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED,com.google.googlejavaformat --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED,com.google.googlejavaformat - --add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED,com.google.googlejavaformat --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED,com.google.googlejavaformat --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED,com.google.googlejavaformat --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED,com.google.googlejavaformat - --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED,com.google.googlejavaformat - --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED,com.google.googlejavaformat diff --git a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java index ad91bfa2..42e12d86 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java @@ -54,13 +54,12 @@ public class MainTest { private static final ImmutableList ADD_EXPORTS = ImmutableList.of( + "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"); + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"); @Test public void testUsageOutput() { diff --git a/pom.xml b/pom.xml index 150173ee..243da54d 100644 --- a/pom.xml +++ b/pom.xml @@ -235,15 +235,12 @@ jdk.compiler/com.sun.tools.javac.api jdk.compiler/com.sun.tools.javac.code jdk.compiler/com.sun.tools.javac.file jdk.compiler/com.sun.tools.javac.main jdk.compiler/com.sun.tools.javac.model jdk.compiler/com.sun.tools.javac.parser jdk.compiler/com.sun.tools.javac.processing jdk.compiler/com.sun.tools.javac.tree jdk.compiler/com.sun.tools.javac.util - jdk.compiler/com.sun.tools.javac.code jdk.compiler/com.sun.tools.javac.comp From 32a6c00ff4cfdc3697d556c43968d58602f3cac3 Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Fri, 3 Mar 2023 06:23:10 -0800 Subject: [PATCH 16/23] Switch (deprecated) ProjectManagerListener#onProjectOpened to StartupActivity. PiperOrigin-RevId: 513811140 --- ...=> InitialConfigurationStartupActivity.java} | 17 +++++++++-------- .../src/main/resources/META-INF/plugin.xml | 7 +------ 2 files changed, 10 insertions(+), 14 deletions(-) rename idea_plugin/src/main/java/com/google/googlejavaformat/intellij/{InitialConfigurationProjectManagerListener.java => InitialConfigurationStartupActivity.java} (83%) diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java similarity index 83% rename from idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java rename to idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java index 1906347f..7cedbbec 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java @@ -21,17 +21,17 @@ import com.intellij.notification.NotificationGroupManager; import com.intellij.notification.NotificationType; import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManagerListener; +import com.intellij.openapi.startup.StartupActivity; import org.jetbrains.annotations.NotNull; -final class InitialConfigurationProjectManagerListener implements ProjectManagerListener { +final class InitialConfigurationStartupActivity implements StartupActivity.Background { private static final String NOTIFICATION_TITLE = "Enable google-java-format"; private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_TITLE); @Override - public void projectOpened(@NotNull Project project) { + public void runActivity(@NotNull Project project) { GoogleJavaFormatSettings settings = GoogleJavaFormatSettings.getInstance(project); if (settings.isUninitialized()) { @@ -47,11 +47,12 @@ private void displayNewUserNotification(Project project, GoogleJavaFormatSetting NOTIFICATION_TITLE, "The google-java-format plugin is disabled by default. " + "Enable for this project.", - NotificationType.INFORMATION, - (n, e) -> { - settings.setEnabled(true); - n.expire(); - }); + NotificationType.INFORMATION); + notification.setListener( + (n, e) -> { + settings.setEnabled(true); + n.expire(); + }); notification.notify(project); } } diff --git a/idea_plugin/src/main/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml index 2a82c955..f272ab26 100644 --- a/idea_plugin/src/main/resources/META-INF/plugin.xml +++ b/idea_plugin/src/main/resources/META-INF/plugin.xml @@ -65,15 +65,10 @@ ]]> - - - - + Date: Fri, 3 Mar 2023 07:30:57 -0800 Subject: [PATCH 17/23] Link to instructions for properly configuring the IDE's JRE. Pop up this warning once per-project per-launch, only when the Formatter is enabled (or at startup if the formatter is already enabled in the project). PiperOrigin-RevId: 513822794 --- README.md | 27 +++-- idea_plugin/build.gradle | 10 +- .../GoogleJavaFormatFormattingService.java | 3 +- .../GoogleJavaFormatImportOptimizer.java | 3 +- .../intellij/GoogleJavaFormatSettings.java | 9 ++ .../InitialConfigurationStartupActivity.java | 2 + .../intellij/JreConfigurationChecker.java | 102 ++++++++++++++++++ .../src/main/resources/META-INF/plugin.xml | 3 + 8 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java diff --git a/README.md b/README.md index f2c61e3d..376863ff 100644 --- a/README.md +++ b/README.md @@ -44,14 +44,25 @@ presented when you first open a project offering to do this for you.) To enable it by default in new projects, use `File→Other Settings→Default Settings...`. -When enabled, it will replace the normal `Reformat Code` action, which can be -triggered from the `Code` menu or with the Ctrl-Alt-L (by default) keyboard -shortcut. - -The import ordering is not handled by this plugin, unfortunately. To fix the -import order, download the -[IntelliJ Java Google Style file](https://raw.githubusercontent.com/google/styleguide/gh-pages/intellij-java-google-style.xml) -and import it into File→Settings→Editor→Code Style. +When enabled, it will replace the normal `Reformat Code` and `Optimize Imports` +actions. + +#### IntelliJ JRE Config + +The google-java-format plugin uses some internal classes that aren't available +without extra configuration. To use the plugin, go to `Help→Edit Custom VM +Options...` and paste in these lines: + +``` +--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +``` + +Once you've done that, restart the IDE. ### Eclipse diff --git a/idea_plugin/build.gradle b/idea_plugin/build.gradle index 73214867..3032dd54 100644 --- a/idea_plugin/build.gradle +++ b/idea_plugin/build.gradle @@ -51,10 +51,12 @@ publishPlugin { } tasks.withType(Test).configureEach { - jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED" - jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED" - jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED" - jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED" + jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED" + jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED" + jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED" + jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED" + jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" } dependencies { diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java index 1e7ca1a6..50dc5d00 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java @@ -75,7 +75,8 @@ private static Formatter createFormatter(Style style, boolean canChangeWhiteSpac @Override public boolean canFormat(@NotNull PsiFile file) { return JavaFileType.INSTANCE.equals(file.getFileType()) - && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled(); + && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled() + && JreConfigurationChecker.checkJreConfiguration(file.getProject()); } @Override diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java index 3a9a30f4..5b517506 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java @@ -35,7 +35,8 @@ public class GoogleJavaFormatImportOptimizer implements ImportOptimizer { @Override public boolean supports(@NotNull PsiFile file) { return JavaFileType.INSTANCE.equals(file.getFileType()) - && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled(); + && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled() + && JreConfigurationChecker.checkJreConfiguration(file.getProject()); } @Override diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java index 1e92a4bd..ad740b2f 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java @@ -30,8 +30,14 @@ storages = {@Storage("google-java-format.xml")}) class GoogleJavaFormatSettings implements PersistentStateComponent { + private final Project project; + private State state = new State(); + GoogleJavaFormatSettings(Project project) { + this.project = project; + } + static GoogleJavaFormatSettings getInstance(Project project) { return ServiceManager.getService(project, GoogleJavaFormatSettings.class); } @@ -56,6 +62,9 @@ void setEnabled(boolean enabled) { } void setEnabled(EnabledState enabled) { + if (enabled.equals(EnabledState.ENABLED)) { + JreConfigurationChecker.checkJreConfiguration(project); + } state.enabled = enabled; } diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java index 7cedbbec..940def65 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java @@ -37,6 +37,8 @@ public void runActivity(@NotNull Project project) { if (settings.isUninitialized()) { settings.setEnabled(false); displayNewUserNotification(project, settings); + } else if (settings.isEnabled()) { + JreConfigurationChecker.checkJreConfiguration(project); } } diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java new file mode 100644 index 00000000..bf8e6ce8 --- /dev/null +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java @@ -0,0 +1,102 @@ +/* + * Copyright 2023 Google Inc. All Rights Reserved. + * + * 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.google.googlejavaformat.intellij; + +import com.google.common.base.Suppliers; +import com.intellij.ide.ui.IdeUiService; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationType; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import java.util.function.Supplier; + +class JreConfigurationChecker { + + private final Supplier hasAccess = Suppliers.memoize(this::checkJreConfiguration); + + private final Project project; + private final Logger logger = Logger.getInstance(JreConfigurationChecker.class); + + public JreConfigurationChecker(Project project) { + this.project = project; + } + + static boolean checkJreConfiguration(Project project) { + return project.getService(JreConfigurationChecker.class).hasAccess.get(); + } + + /** + * Determine whether the JRE is configured to work with the google-java-format plugin. If not, + * display a notification with instructions and return false. + */ + private boolean checkJreConfiguration() { + try { + boolean hasAccess = + testClassAccess( + "com.sun.tools.javac.api.JavacTrees", + "com.sun.tools.javac.code.Flags", + "com.sun.tools.javac.file.JavacFileManager", + "com.sun.tools.javac.parser.JavacParser", + "com.sun.tools.javac.tree.JCTree", + "com.sun.tools.javac.util.Log"); + if (!hasAccess) { + displayConfigurationErrorNotification(); + } + return hasAccess; + } catch (ClassNotFoundException e) { + logger.error("Error checking jre configuration for google-java-format", e); + return false; + } + } + + private boolean testClassAccess(String... classNames) throws ClassNotFoundException { + for (String className : classNames) { + if (!testClassAccess(className)) { + return false; + } + } + return true; + } + + private boolean testClassAccess(String className) throws ClassNotFoundException { + Class klass = Class.forName(className); + return klass + .getModule() + // isExported returns true if the package is either open or exported. Either one is + // sufficient + // to run the google-java-format code (even though the documentation specifies --add-opens). + .isExported(klass.getPackageName(), getClass().getClassLoader().getUnnamedModule()); + } + + private void displayConfigurationErrorNotification() { + Notification notification = + new Notification( + "Configure JRE for google-java-format", + "Configure the JRE for google-java-format", + "The google-java-format plugin needs additional configuration before it can be used. " + + "Follow the instructions here.", + NotificationType.INFORMATION); + notification.setListener( + (n, e) -> { + IdeUiService.getInstance() + .browse( + "https://github.com/google/google-java-format/blob/master/README.md#intellij-jre-config"); + n.expire(); + }); + notification.notify(project); + } +} diff --git a/idea_plugin/src/main/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml index f272ab26..cd400991 100644 --- a/idea_plugin/src/main/resources/META-INF/plugin.xml +++ b/idea_plugin/src/main/resources/META-INF/plugin.xml @@ -75,8 +75,11 @@ displayName="google-java-format Settings"/> + + From 1651dea8d2d55e4f3ba68c582b9f7f9f13127ff5 Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Fri, 3 Mar 2023 12:10:34 -0800 Subject: [PATCH 18/23] Convert the gradle config to kotlin. I moved the description out of the Gradle config and into the XML. (It was just in the gradle so we could add the gjf version number, but that seems unnecessary since I changed the versioning so that the plugin version matches the gjf version.) I also updated the IntelliJ Gradle plugin to 1.13.1 because plugin publishing is broken in 1.13.0. PiperOrigin-RevId: 513888985 --- idea_plugin/build.gradle | 67 ------------------- idea_plugin/build.gradle.kts | 66 ++++++++++++++++++ .../src/main/resources/META-INF/plugin.xml | 7 ++ 3 files changed, 73 insertions(+), 67 deletions(-) delete mode 100644 idea_plugin/build.gradle create mode 100644 idea_plugin/build.gradle.kts diff --git a/idea_plugin/build.gradle b/idea_plugin/build.gradle deleted file mode 100644 index 3032dd54..00000000 --- a/idea_plugin/build.gradle +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * 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. - */ - -plugins { - id "org.jetbrains.intellij" version "1.13.0" -} - -repositories { - mavenCentral() -} - -ext { - googleJavaFormatVersion = "1.16.0" -} - -apply plugin: "org.jetbrains.intellij" -apply plugin: "java" - -sourceCompatibility = JavaVersion.VERSION_11 -targetCompatibility = JavaVersion.VERSION_11 - -intellij { - pluginName = "google-java-format" - plugins = ["java"] - version = "2021.3" -} - -patchPluginXml { - pluginDescription = "Formats source code using the google-java-format tool. This version of " + - "the plugin uses version ${googleJavaFormatVersion} of the tool." - version.set("${googleJavaFormatVersion}.0") - sinceBuild = "213" - untilBuild = "" -} - -publishPlugin { - token = project.ext.properties.jetbrainsPluginRepoToken -} - -tasks.withType(Test).configureEach { - jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED" - jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED" - jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED" - jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED" - jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED" - jvmArgs += "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" -} - -dependencies { - implementation "com.google.googlejavaformat:google-java-format:${googleJavaFormatVersion}" - testImplementation "junit:junit:4.13.2" - testImplementation "com.google.truth:truth:1.1.3" - testImplementation "com.google.truth.extensions:truth-java8-extension:1.1.3" -} diff --git a/idea_plugin/build.gradle.kts b/idea_plugin/build.gradle.kts new file mode 100644 index 00000000..46a697e8 --- /dev/null +++ b/idea_plugin/build.gradle.kts @@ -0,0 +1,66 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * 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. + */ + +plugins { id("org.jetbrains.intellij") version "1.13.1" } + +apply(plugin = "org.jetbrains.intellij") + +apply(plugin = "java") + +repositories { mavenCentral() } + +val googleJavaFormatVersion = "1.16.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +intellij { + pluginName.set("google-java-format") + plugins.set(listOf("java")) + version.set("2021.3") +} + +tasks { + patchPluginXml { + version.set("${googleJavaFormatVersion}.0") + sinceBuild.set("213") + untilBuild.set("") + } + + publishPlugin { + val jetbrainsPluginRepoToken: String by project + token.set(jetbrainsPluginRepoToken) + } + + withType().configureEach { + jvmArgs( + "--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + ) + } +} + +dependencies { + implementation("com.google.googlejavaformat:google-java-format:${googleJavaFormatVersion}") + testImplementation("junit:junit:4.13.2") + testImplementation("com.google.truth:truth:1.1.3") +} diff --git a/idea_plugin/src/main/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml index cd400991..99b286db 100644 --- a/idea_plugin/src/main/resources/META-INF/plugin.xml +++ b/idea_plugin/src/main/resources/META-INF/plugin.xml @@ -26,6 +26,13 @@ com.intellij.modules.lang com.intellij.modules.platform + + + This plugin requires additional IDE configuration. For more information, + read + the documentation. + ]]>
1.16.0.0
From 20527aa56f5068f921ca2110a3fb50d48bdb1c8a Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Mon, 6 Mar 2023 09:49:54 -0800 Subject: [PATCH 19/23] Update the `--add-exports` instructions. I don't really understand _why_ this works. I'm able to (easily) reproduce the problem by not including the `=` in my `idea64.options` file. But then if I copy/paste the command line out of `ps`, it works fine. And since it's easy enough to change this, I'm inclined to just do that? Fixes #908. PiperOrigin-RevId: 514440074 --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 376863ff..4fe0e212 100644 --- a/README.md +++ b/README.md @@ -54,12 +54,12 @@ without extra configuration. To use the plugin, go to `Help→Edit Custom VM Options...` and paste in these lines: ``` ---add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED ``` Once you've done that, restart the IDE. @@ -105,12 +105,12 @@ following JVM flags are required when running on JDK 16 and newer, due to [JEP 396: Strongly Encapsulate JDK Internals by Default](https://openjdk.java.net/jeps/396): ``` ---add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED ---add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED ``` #### Maven From 1a0065696b4f408313dbd24f9d6606d758e98868 Mon Sep 17 00:00:00 2001 From: nickreid Date: Tue, 7 Mar 2023 13:06:49 -0800 Subject: [PATCH 20/23] Allow blank lines inserted before comments. Previously, blank lines could only be emitted before comments if they were there in the input. This was irrespective of BlankLineWanted.YES hints in the OpBuilder. These hints are already being set by some paths in GJF, so now that they're respected, blank lines will be added to some existing files. Eval in unknown commit PiperOrigin-RevId: 514817708 --- .../java/com/google/googlejavaformat/java/JavaOutput.java | 2 +- .../googlejavaformat/java/PartialFormattingTest.java | 1 + .../googlejavaformat/java/testdata/B27246427.output | 1 + .../google/googlejavaformat/java/testdata/Fields.input | 7 +++++++ .../google/googlejavaformat/java/testdata/Fields.output | 8 ++++++++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java b/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java index c43a91ad..656b65c8 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java @@ -111,7 +111,7 @@ public void append(String text, Range range) { * there's a blank line here and it's a comment. */ BlankLineWanted wanted = blankLines.getOrDefault(lastK, BlankLineWanted.NO); - if (isComment(text) ? sawNewlines : wanted.wanted().orElse(sawNewlines)) { + if ((sawNewlines && isComment(text)) || wanted.wanted().orElse(sawNewlines)) { ++newlinesPending; } } diff --git a/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java b/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java index b1142b3b..1750049d 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java @@ -679,6 +679,7 @@ public void noTokensOnLine() throws Exception { String input = lines( " package com.google.googlejavaformat.java;", + "", "/*", " * Copyright 2015 Google Inc.", " *", diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B27246427.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B27246427.output index b1e33e90..3d591f8f 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B27246427.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B27246427.output @@ -6,6 +6,7 @@ enum TrailingComment { /** a */ Object a; + /** b */ Object b; } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.input index 9dc34266..d012d17a 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.input @@ -1,5 +1,12 @@ class Fields { + int a = 1; + int b = 1; + + int c = 1; + /** Javadoc */ + int d = 1; + int x = 1; int y = 1; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.output index 376e8b62..81ebd2ac 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.output @@ -1,5 +1,13 @@ class Fields { + int a = 1; + int b = 1; + + int c = 1; + + /** Javadoc */ + int d = 1; + int x = 1; int y = 1; From 4c1aeffc8b0ea3d4e18ffe21162b835f2c51cc3d Mon Sep 17 00:00:00 2001 From: nickreid Date: Fri, 10 Mar 2023 10:55:13 -0800 Subject: [PATCH 21/23] Make OpsBuilder::add public final PiperOrigin-RevId: 515682100 --- core/src/main/java/com/google/googlejavaformat/OpsBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java index db431c04..a45e83b9 100644 --- a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java +++ b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java @@ -159,7 +159,7 @@ public BlankLineWanted merge(BlankLineWanted other) { int depth = 0; /** Add an {@link Op}, and record open/close ops for later validation of unclosed levels. */ - private void add(Op op) { + public final void add(Op op) { if (op instanceof OpenOp) { depth++; } else if (op instanceof CloseOp) { From 401d5c96946c856353a0edf83631f694303ae3ee Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Fri, 10 Mar 2023 13:22:52 -0800 Subject: [PATCH 22/23] Be louder about telling users they need to modify their IDE config to use gjf. Right now if your IDE isn't set up properly and you have gjf enabled in a project, you get a single warning. Any uses of the code formatter after that will instead use the built-in formatter. This is confusing to people who don't notice the notification. They think gjf is formatting their code strangely. Instead, let's warn on every single invocation of the formatter until they either fix the configuration issue or disable gjf in the project. Fixes #914 #919. PiperOrigin-RevId: 515722447 --- idea_plugin/build.gradle.kts | 4 +- .../GoogleJavaFormatFormattingService.java | 8 +++- .../GoogleJavaFormatImportOptimizer.java | 8 +++- .../intellij/JreConfigurationChecker.java | 40 ++++++++++--------- .../src/main/resources/META-INF/plugin.xml | 4 ++ 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/idea_plugin/build.gradle.kts b/idea_plugin/build.gradle.kts index 46a697e8..a05b0cb9 100644 --- a/idea_plugin/build.gradle.kts +++ b/idea_plugin/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ -plugins { id("org.jetbrains.intellij") version "1.13.1" } +plugins { id("org.jetbrains.intellij") version "1.13.2" } apply(plugin = "org.jetbrains.intellij") @@ -37,7 +37,7 @@ intellij { tasks { patchPluginXml { - version.set("${googleJavaFormatVersion}.0") + version.set("${googleJavaFormatVersion}.1") sinceBuild.set("213") untilBuild.set("") } diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java index 50dc5d00..150a7391 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java @@ -44,6 +44,11 @@ public class GoogleJavaFormatFormattingService extends AsyncDocumentFormattingSe @Override protected FormattingTask createFormattingTask(AsyncFormattingRequest request) { Project project = request.getContext().getProject(); + + if (!JreConfigurationChecker.checkJreConfiguration(project)) { + return null; + } + Style style = GoogleJavaFormatSettings.getInstance(project).getStyle(); Formatter formatter = createFormatter(style, request.canChangeWhitespaceOnly()); return new GoogleJavaFormatFormattingTask(formatter, request); @@ -75,8 +80,7 @@ private static Formatter createFormatter(Style style, boolean canChangeWhiteSpac @Override public boolean canFormat(@NotNull PsiFile file) { return JavaFileType.INSTANCE.equals(file.getFileType()) - && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled() - && JreConfigurationChecker.checkJreConfiguration(file.getProject()); + && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled(); } @Override diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java index 5b517506..498c8852 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java @@ -35,13 +35,17 @@ public class GoogleJavaFormatImportOptimizer implements ImportOptimizer { @Override public boolean supports(@NotNull PsiFile file) { return JavaFileType.INSTANCE.equals(file.getFileType()) - && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled() - && JreConfigurationChecker.checkJreConfiguration(file.getProject()); + && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled(); } @Override public @NotNull Runnable processFile(@NotNull PsiFile file) { Project project = file.getProject(); + + if (!JreConfigurationChecker.checkJreConfiguration(file.getProject())) { + return Runnables.doNothing(); + } + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); Document document = documentManager.getDocument(file); diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java index bf8e6ce8..5084b6a3 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java @@ -26,44 +26,44 @@ class JreConfigurationChecker { - private final Supplier hasAccess = Suppliers.memoize(this::checkJreConfiguration); + private static final Supplier hasAccess = + Suppliers.memoize(JreConfigurationChecker::checkJreConfiguration); + private static final Logger logger = Logger.getInstance(JreConfigurationChecker.class); private final Project project; - private final Logger logger = Logger.getInstance(JreConfigurationChecker.class); public JreConfigurationChecker(Project project) { this.project = project; } static boolean checkJreConfiguration(Project project) { - return project.getService(JreConfigurationChecker.class).hasAccess.get(); + var success = hasAccess.get(); + if (!success) { + project.getService(JreConfigurationChecker.class).displayConfigurationErrorNotification(); + } + return success; } /** * Determine whether the JRE is configured to work with the google-java-format plugin. If not, * display a notification with instructions and return false. */ - private boolean checkJreConfiguration() { + private static boolean checkJreConfiguration() { try { - boolean hasAccess = - testClassAccess( - "com.sun.tools.javac.api.JavacTrees", - "com.sun.tools.javac.code.Flags", - "com.sun.tools.javac.file.JavacFileManager", - "com.sun.tools.javac.parser.JavacParser", - "com.sun.tools.javac.tree.JCTree", - "com.sun.tools.javac.util.Log"); - if (!hasAccess) { - displayConfigurationErrorNotification(); - } - return hasAccess; + return testClassAccess( + "com.sun.tools.javac.api.JavacTrees", + "com.sun.tools.javac.code.Flags", + "com.sun.tools.javac.file.JavacFileManager", + "com.sun.tools.javac.parser.JavacParser", + "com.sun.tools.javac.tree.JCTree", + "com.sun.tools.javac.util.Log"); } catch (ClassNotFoundException e) { logger.error("Error checking jre configuration for google-java-format", e); return false; } } - private boolean testClassAccess(String... classNames) throws ClassNotFoundException { + private static boolean testClassAccess(String... classNames) throws ClassNotFoundException { for (String className : classNames) { if (!testClassAccess(className)) { return false; @@ -72,14 +72,16 @@ private boolean testClassAccess(String... classNames) throws ClassNotFoundExcept return true; } - private boolean testClassAccess(String className) throws ClassNotFoundException { + private static boolean testClassAccess(String className) throws ClassNotFoundException { Class klass = Class.forName(className); return klass .getModule() // isExported returns true if the package is either open or exported. Either one is // sufficient // to run the google-java-format code (even though the documentation specifies --add-opens). - .isExported(klass.getPackageName(), getClass().getClassLoader().getUnnamedModule()); + .isExported( + klass.getPackageName(), + JreConfigurationChecker.class.getClassLoader().getUnnamedModule()); } private void displayConfigurationErrorNotification() { diff --git a/idea_plugin/src/main/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml index 99b286db..d59c3c21 100644 --- a/idea_plugin/src/main/resources/META-INF/plugin.xml +++ b/idea_plugin/src/main/resources/META-INF/plugin.xml @@ -35,6 +35,10 @@ ]]> +
1.16.0.1
+
When the plugin isn't configured correctly, show the error on every + format command. Previously it was only being shown at startup and going + unnoticed.
1.16.0.0
Updated to use google-java-format 1.16.0.
Use the new IDE formatting APIs for a simplified plugin.
From 25ce685ffbc1143f3e7d93c8655e8590cfaaf61b Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Mon, 3 Apr 2023 22:10:40 -0700 Subject: [PATCH 23/23] Remove AD_HOC_FORMATTING from the IntelliJ plugin. I have a feeling this is causing a lot of the issues people are seeing where it's constantly trying to reformat and failing because of syntax errors. Hopefully this will make it only try to reformat when a user specifically requests it. PiperOrigin-RevId: 521657519 --- idea_plugin/build.gradle.kts | 4 ++-- .../intellij/GoogleJavaFormatFormattingService.java | 2 +- .../googlejavaformat/intellij/GoogleJavaFormatSettings.java | 3 +-- idea_plugin/src/main/resources/META-INF/plugin.xml | 2 ++ 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/idea_plugin/build.gradle.kts b/idea_plugin/build.gradle.kts index a05b0cb9..474c24d7 100644 --- a/idea_plugin/build.gradle.kts +++ b/idea_plugin/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ -plugins { id("org.jetbrains.intellij") version "1.13.2" } +plugins { id("org.jetbrains.intellij") version "1.13.3" } apply(plugin = "org.jetbrains.intellij") @@ -37,7 +37,7 @@ intellij { tasks { patchPluginXml { - version.set("${googleJavaFormatVersion}.1") + version.set("${googleJavaFormatVersion}.2") sinceBuild.set("213") untilBuild.set("") } diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java index 150a7391..9d2d7a59 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java @@ -74,7 +74,7 @@ private static Formatter createFormatter(Style style, boolean canChangeWhiteSpac @Override public @NotNull Set getFeatures() { - return Set.of(Feature.AD_HOC_FORMATTING, Feature.FORMAT_FRAGMENTS, Feature.OPTIMIZE_IMPORTS); + return Set.of(Feature.FORMAT_FRAGMENTS, Feature.OPTIMIZE_IMPORTS); } @Override diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java index ad740b2f..ee187c00 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java @@ -18,7 +18,6 @@ import com.google.googlejavaformat.java.JavaFormatterOptions; import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.project.Project; @@ -39,7 +38,7 @@ class GoogleJavaFormatSettings implements PersistentStateComponent +
1.16.0.2
+
Disable AD_HOC_FORMATTING, which should stop the formatter from running so often when it wasn't specifically requested.
1.16.0.1
When the plugin isn't configured correctly, show the error on every format command. Previously it was only being shown at startup and going