From 64f98edf6aeef20a890f09122bb8a5729bc7585a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 27 Feb 2023 11:32:35 -0800 Subject: [PATCH 01/15] 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 79a260eab..716abcc07 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 02/15] 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 ecb30eed6..a0fc2f54a 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 03/15] 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 294d77e73..6c9695d47 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 04/15] 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 6c9695d47..732148670 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 af5da957a..000000000 --- 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 a5e69c93d..000000000 --- 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 c9aa64a67..000000000 --- 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 000000000..1e7ca1a6f --- /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 000000000..3a9a30f49 --- /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 c60673602..000000000 --- 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 000000000..d32aa98b9 --- /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 42d5f3b31..2a82c9557 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 000000000..fec086c68 --- /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 000000000..ad9fe2744 --- /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 05/15] 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 2d53f8ae5..f2c61e3dd 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 10565c66a..038e4eda6 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 ad91bfa26..42e12d860 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 150173ee6..243da54d3 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 06/15] 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 1906347f7..7cedbbec7 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 2a82c9557..f272ab26d 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 07/15] 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 f2c61e3dd..376863fff 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 732148670..3032dd54d 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 1e7ca1a6f..50dc5d008 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 3a9a30f49..5b5175069 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 1e92a4bdd..ad740b2fd 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 7cedbbec7..940def655 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 000000000..bf8e6ce8f --- /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 f272ab26d..cd4009912 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 08/15] 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 3032dd54d..000000000 --- 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 000000000..46a697e8c --- /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 cd4009912..99b286db5 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 09/15] 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 376863fff..4fe0e2126 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 10/15] 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 c43a91ad1..656b65c83 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 b1142b3b2..1750049d7 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 b1e33e904..3d591f8f6 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 9dc342663..d012d17a3 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 376e8b620..81ebd2ac4 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 11/15] 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 db431c040..a45e83b9e 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 12/15] 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 46a697e8c..a05b0cb93 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 50dc5d008..150a7391c 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 5b5175069..498c88526 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 bf8e6ce8f..5084b6a39 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 99b286db5..d59c3c210 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 13/15] 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 a05b0cb93..474c24d79 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 150a7391c..9d2d7a595 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 ad740b2fd..ee187c00d 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 From 18f835849551f81d60d582300a0a3c585b5774b4 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 20 Apr 2023 09:56:25 -0700 Subject: [PATCH 14/15] Remove tests for handling of extra semi-colons javac no longer accepts these, see https://bugs.openjdk.org/browse/JDK-8027682 PiperOrigin-RevId: 525775880 --- .../google/googlejavaformat/java/testdata/B28788559.input | 7 ------- .../googlejavaformat/java/testdata/B28788559.output | 8 -------- .../googlejavaformat/java/testdata/CL240367479.input | 6 ------ .../googlejavaformat/java/testdata/CL240367479.output | 6 ------ 4 files changed, 27 deletions(-) delete mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.input delete mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.output delete mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.input delete mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.output diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.input deleted file mode 100644 index a38d10b73..000000000 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.input +++ /dev/null @@ -1,7 +0,0 @@ -import a.A;; -import b.B; - -class Test { - A a; - B b; -} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.output deleted file mode 100644 index 8a1db808f..000000000 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.output +++ /dev/null @@ -1,8 +0,0 @@ -import a.A; -; -import b.B; - -class Test { - A a; - B b; -} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.input deleted file mode 100644 index be76390d7..000000000 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.input +++ /dev/null @@ -1,6 +0,0 @@ -package foo;; - -import com.google.second.Foo; -import com.google.first.Bar; - -public class Blim {} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.output deleted file mode 100644 index 025d2370e..000000000 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.output +++ /dev/null @@ -1,6 +0,0 @@ -package foo; -; -import com.google.second.Foo; -import com.google.first.Bar; - -public class Blim {} From d6b1bcc610a1aadfcbddaa22b9d3fbe1b4c429d7 Mon Sep 17 00:00:00 2001 From: cushon Date: Mon, 1 May 2023 20:37:37 +0000 Subject: [PATCH 15/15] Release google-java-format 1.17.0 --- core/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 038e4eda6..dcbd25f1c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,7 +22,7 @@ com.google.googlejavaformat google-java-format-parent - HEAD-SNAPSHOT + 1.17.0 google-java-format diff --git a/pom.xml b/pom.xml index 243da54d3..43842be2b 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ com.google.googlejavaformat google-java-format-parent pom - HEAD-SNAPSHOT + 1.17.0 core