aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSorin Basca <sorinbasca@google.com>2024-01-16 12:42:22 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2024-01-16 12:42:22 +0000
commit627f7e9e05bd10152525ff7a5f4337c1ab52efbb (patch)
treed592898fe81e13c3f80b13796f41ac7dc8417424
parent23900a4073caf1243f551a2b9a1a2c13eab80dcf (diff)
parent897a1820eb727e6daab691616114df17d04bc66d (diff)
downloadgoogle-java-format-627f7e9e05bd10152525ff7a5f4337c1ab52efbb.tar.gz
Merge commit 'v1.19.0' am: 9b3a28805d am: 26c8b23036 am: 897a1820eb
Original change: https://android-review.googlesource.com/c/platform/external/google-java-format/+/2887766 Change-Id: Ie42d6efd5e39b9dffa497a5252e12b4875a6679b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.github/dependabot.yml6
-rw-r--r--.github/workflows/ci.yml16
-rw-r--r--.github/workflows/release.yml17
-rw-r--r--README.md58
-rw-r--r--core/pom.xml40
-rw-r--r--core/src/main/java/com/google/googlejavaformat/CommentsHelper.java19
-rw-r--r--core/src/main/java/com/google/googlejavaformat/Doc.java34
-rw-r--r--core/src/main/java/com/google/googlejavaformat/OpsBuilder.java2
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java57
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/Formatter.java40
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java53
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java5
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java10
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java50
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaInput.java17
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java19
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java2
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java52
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/Main.java102
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java28
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java11
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java (renamed from core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java)109
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java101
-rw-r--r--core/src/main/resources/META-INF/native-image/reflect-config.json18
-rw-r--r--core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java11
-rw-r--r--core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java2
-rw-r--r--core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolTest.java65
-rw-r--r--core/src/test/java/com/google/googlejavaformat/java/MainTest.java32
-rw-r--r--core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java1
-rw-r--r--core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java21
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input2
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output2
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B21283374.output56
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B26694550.output6
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B27246427.output1
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.input7
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.output8
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B308157568.input23
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/B308157568.output23
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.input6
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/CL240367479.output6
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.input7
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/Fields.output8
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.input19
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.output21
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.input5
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.output5
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.input12
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.output14
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/M.output108
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.input29
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.output38
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.input7
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.output7
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input9
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output9
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.input21
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.output46
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.input8
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.output8
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.input44
-rw-r--r--core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.output48
-rw-r--r--eclipse_plugin/pom.xml4
-rw-r--r--idea_plugin/build.gradle62
-rw-r--r--idea_plugin/build.gradle.kts66
-rw-r--r--idea_plugin/src/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java246
-rw-r--r--idea_plugin/src/com/google/googlejavaformat/intellij/FormatterUtil.java64
-rw-r--r--idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java172
-rw-r--r--idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java57
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form (renamed from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form)0
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java (renamed from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java)0
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java141
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java90
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java (renamed from idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java)12
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java (renamed from idea_plugin/src/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java)19
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java104
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/Notifications.java38
-rw-r--r--idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java (renamed from idea_plugin/src/com/google/googlejavaformat/intellij/UiFormatterStyle.java)0
-rw-r--r--idea_plugin/src/main/resources/META-INF/plugin.xml (renamed from idea_plugin/resources/META-INF/plugin.xml)47
-rw-r--r--idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java250
-rw-r--r--idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizerTest.java168
-rw-r--r--pom.xml34
-rwxr-xr-xscripts/mvn-deploy.sh35
83 files changed, 2186 insertions, 1034 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index daec318..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-version: 2
-updates:
- - package-ecosystem: "maven"
- directory: "/"
- schedule:
- interval: "daily"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 016b8e7..ee1174d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,19 +29,19 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
- java: [ 17, 11 ]
+ java: [ 21, 17, 11 ]
experimental: [ false ]
include:
# Only test on macos and windows with a single recent JDK to avoid a
# combinatorial explosion of test configurations.
- os: macos-latest
- java: 17
+ java: 21
experimental: false
- os: windows-latest
- java: 17
+ java: 21
experimental: false
- os: ubuntu-latest
- java: 18-ea
+ java: EA
experimental: true
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
@@ -52,7 +52,15 @@ jobs:
access_token: ${{ github.token }}
- name: 'Check out repository'
uses: actions/checkout@v2
+ - name: 'Set up JDK ${{ matrix.java }} from jdk.java.net'
+ if: ${{ matrix.java == 'EA' }}
+ uses: oracle-actions/setup-java@v1
+ with:
+ website: jdk.java.net
+ release: ${{ matrix.java }}
+ cache: 'maven'
- name: 'Set up JDK ${{ matrix.java }}'
+ if: ${{ matrix.java != 'EA' }}
uses: actions/setup-java@v2
with:
java-version: ${{ matrix.java }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a3f066d..3176f73 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,16 +12,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
- steps:
- - name: Setup Signing Key
- run: |
- gpg-agent --daemon --default-cache-ttl 7200
- echo -e "${{ secrets.GPG_SIGNING_KEY }}" | gpg --batch --import --no-tty
- echo "hello world" > temp.txt
- gpg --detach-sig --yes -v --output=/dev/null --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" temp.txt
- rm temp.txt
- gpg --list-secret-keys --keyid-format LONG
-
+ steps:
- name: Checkout
uses: actions/checkout@v2.4.0
@@ -34,12 +25,15 @@ jobs:
server-id: sonatype-nexus-staging
server-username: CI_DEPLOY_USERNAME
server-password: CI_DEPLOY_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
- name: Bump Version Number
run: |
mvn --no-transfer-progress versions:set versions:commit -DnewVersion="${{ github.event.inputs.version }}"
+ mvn --no-transfer-progress versions:set versions:commit -DnewVersion="${{ github.event.inputs.version }}" -pl eclipse_plugin
mvn tycho-versions:update-eclipse-metadata -pl eclipse_plugin
- git ls-files | grep 'pom.xml$' | xargs git add
+ git ls-files | grep -E '(pom.xml|MANIFEST.MF)$' | xargs git add
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
git config --global user.name "${{ github.actor }}"
git commit -m "Release google-java-format ${{ github.event.inputs.version }}"
@@ -51,6 +45,7 @@ jobs:
env:
CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }}
CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run:
mvn --no-transfer-progress -pl '!eclipse_plugin' -P sonatype-oss-release clean deploy -Dgpg.passphrase="${{ secrets.GPG_PASSPHRASE }}"
diff --git a/README.md b/README.md
index 5d1d7c7..4fe0e21 100644
--- a/README.md
+++ b/README.md
@@ -27,21 +27,6 @@ To reformat changed lines in a specific patch, use
formatting. This is a deliberate design decision to unify our code formatting on
a single format.*
-#### JDK 16
-
-The following flags are required when running on JDK 16, due to
-[JEP 396: Strongly Encapsulate JDK Internals by Default](https://openjdk.java.net/jeps/396):
-
-```
-java \
- --add-exports jdk.compiler/com.sun.tools.javac.api=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 \
- -jar google-java-format-${GJF_VERSION?}-all-deps.jar <options> [files...]
-```
-
### IntelliJ, Android Studio, and other JetBrains IDEs
A
@@ -59,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.
+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:
-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.
+```
+--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
@@ -87,16 +83,13 @@ Implementation`.
* [sherter/google-java-format-gradle-plugin](https://github.com/sherter/google-java-format-gradle-plugin)
* Apache Maven plugins
* [spotless](https://github.com/diffplug/spotless/tree/main/plugin-maven#google-java-format)
- * [coveo/fmt-maven-plugin](https://github.com/coveo/fmt-maven-plugin)
+ * [spotify/fmt-maven-plugin](https://github.com/spotify/fmt-maven-plugin)
* [talios/googleformatter-maven-plugin](https://github.com/talios/googleformatter-maven-plugin)
* [Cosium/maven-git-code-format](https://github.com/Cosium/maven-git-code-format):
A maven plugin that automatically deploys google-java-format as a
pre-commit git hook.
* SBT plugins
* [sbt/sbt-java-formatter](https://github.com/sbt/sbt-java-formatter)
-* [maltzj/google-style-precommit-hook](https://github.com/maltzj/google-style-precommit-hook):
- A pre-commit (pre-commit.com) hook that will automatically run GJF whenever
- you commit code to your repository
* [Github Actions](https://github.com/features/actions)
* [googlejavaformat-action](https://github.com/axel-op/googlejavaformat-action):
Automatically format your Java files when you push on github
@@ -107,6 +100,19 @@ The formatter can be used in software which generates java to output more
legible java code. Just include the library in your maven/gradle/etc.
configuration.
+`google-java-format` uses internal javac APIs for parsing Java source. The
+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
+```
+
#### Maven
```xml
diff --git a/core/pom.xml b/core/pom.xml
index 1d47159..d1363fe 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -102,13 +102,12 @@
<link>https://docs.oracle.com/en/java/javase/11/docs/api</link>
</links>
<additionalJOptions>
+ <additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
+ <additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
- <additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
- <additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
- <additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED,com.google.googlejavaformat</additionalJOption>
</additionalJOptions>
</configuration>
<executions>
@@ -227,7 +226,34 @@
<profile>
<id>jdk11</id>
<activation>
- <jdk>(,14)</jdk>
+ <jdk>[11,17)</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/Java17InputAstVisitor.java</exclude>
+ <exclude>**/Java21InputAstVisitor.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <excludePackageNames>com.google.googlejavaformat.java.java17</excludePackageNames>
+ <excludePackageNames>com.google.googlejavaformat.java.java21</excludePackageNames>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>jdk17</id>
+ <activation>
+ <jdk>[17,21)</jdk>
</activation>
<build>
<plugins>
@@ -236,14 +262,14 @@
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<excludes>
- <exclude>**/Java14InputAstVisitor.java</exclude>
+ <exclude>**/Java21InputAstVisitor.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
- <excludePackageNames>com.google.googlejavaformat.java.java14</excludePackageNames>
+ <excludePackageNames>com.google.googlejavaformat.java.java21</excludePackageNames>
</configuration>
</plugin>
</plugins>
@@ -256,7 +282,7 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
- <version>0.9.9</version>
+ <version>0.9.13</version>
<extensions>true</extensions>
<executions>
<execution>
diff --git a/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java b/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java
index 45e507b..1e33003 100644
--- a/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java
+++ b/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java
@@ -14,6 +14,10 @@
package com.google.googlejavaformat;
+import com.google.googlejavaformat.Input.Tok;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
/**
* Rewrite comments. This interface is implemented by {@link
* com.google.googlejavaformat.java.JavaCommentsHelper JavaCommentsHelper}.
@@ -28,4 +32,19 @@ public interface CommentsHelper {
* @return the rewritten comment
*/
String rewrite(Input.Tok tok, int maxWidth, int column0);
+
+ static Optional<String> reformatParameterComment(Tok tok) {
+ if (!tok.isSlashStarComment()) {
+ return Optional.empty();
+ }
+ var match = PARAMETER_COMMENT.matcher(tok.getOriginalText());
+ if (!match.matches()) {
+ return Optional.empty();
+ }
+ return Optional.of(String.format("/* %s= */", match.group(1)));
+ }
+
+ Pattern PARAMETER_COMMENT =
+ Pattern.compile(
+ "/\\*\\s*(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\Q...\\E)?)\\s*=\\s*\\*/");
}
diff --git a/core/src/main/java/com/google/googlejavaformat/Doc.java b/core/src/main/java/com/google/googlejavaformat/Doc.java
index 35acca3..cab6885 100644
--- a/core/src/main/java/com/google/googlejavaformat/Doc.java
+++ b/core/src/main/java/com/google/googlejavaformat/Doc.java
@@ -15,9 +15,12 @@
package com.google.googlejavaformat;
import static com.google.common.collect.Iterables.getLast;
+import static com.google.googlejavaformat.CommentsHelper.reformatParameterComment;
import static java.lang.Math.max;
import com.google.common.base.MoreObjects;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
@@ -101,16 +104,13 @@ public abstract class Doc {
private static final DiscreteDomain<Integer> INTEGERS = DiscreteDomain.integers();
// Memoized width; Float.POSITIVE_INFINITY if contains forced breaks.
- private boolean widthComputed = false;
- private float width = 0.0F;
+ private final Supplier<Float> width = Suppliers.memoize(this::computeWidth);
// Memoized flat; not defined (and never computed) if contains forced breaks.
- private boolean flatComputed = false;
- private String flat = "";
+ private final Supplier<String> flat = Suppliers.memoize(this::computeFlat);
// Memoized Range.
- private boolean rangeComputed = false;
- private Range<Integer> range = EMPTY_RANGE;
+ private final Supplier<Range<Integer>> range = Suppliers.memoize(this::computeRange);
/**
* Return the width of a {@code Doc}, or {@code Float.POSITIVE_INFINITY} if it must be broken.
@@ -118,11 +118,7 @@ public abstract class Doc {
* @return the width
*/
final float getWidth() {
- if (!widthComputed) {
- width = computeWidth();
- widthComputed = true;
- }
- return width;
+ return width.get();
}
/**
@@ -132,11 +128,7 @@ public abstract class Doc {
* @return the flat-string value
*/
final String getFlat() {
- if (!flatComputed) {
- flat = computeFlat();
- flatComputed = true;
- }
- return flat;
+ return flat.get();
}
/**
@@ -145,11 +137,7 @@ public abstract class Doc {
* @return the {@code Doc}'s {@link Range}
*/
final Range<Integer> range() {
- if (!rangeComputed) {
- range = computeRange();
- rangeComputed = true;
- }
- return range;
+ return range.get();
}
/**
@@ -727,7 +715,7 @@ public abstract class Doc {
// Account for line comments with missing spaces, see computeFlat.
return tok.length() + 1;
} else {
- return tok.length();
+ return reformatParameterComment(tok).map(String::length).orElse(tok.length());
}
}
return idx != -1 ? Float.POSITIVE_INFINITY : (float) tok.length();
@@ -741,7 +729,7 @@ public abstract class Doc {
if (tok.isSlashSlashComment() && !tok.getOriginalText().startsWith("// ")) {
return "// " + tok.getOriginalText().substring("//".length());
}
- return tok.getOriginalText();
+ return reformatParameterComment(tok).orElse(tok.getOriginalText());
}
@Override
diff --git a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java
index db431c0..a45e83b 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 final class OpsBuilder {
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) {
diff --git a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java
index 9d8ae41..3d68a23 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java
@@ -14,40 +14,73 @@
package com.google.googlejavaformat.java;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
+import java.nio.file.Path;
import java.util.concurrent.Callable;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Encapsulates information about a file to be formatted, including which parts of the file to
* format.
*/
-class FormatFileCallable implements Callable<String> {
+class FormatFileCallable implements Callable<FormatFileCallable.Result> {
+
+ @AutoValue
+ abstract static class Result {
+ abstract @Nullable Path path();
+
+ abstract String input();
+
+ abstract @Nullable String output();
+
+ boolean changed() {
+ return !input().equals(output());
+ }
+
+ abstract @Nullable FormatterException exception();
+
+ static Result create(
+ @Nullable Path path,
+ String input,
+ @Nullable String output,
+ @Nullable FormatterException exception) {
+ return new AutoValue_FormatFileCallable_Result(path, input, output, exception);
+ }
+ }
+
+ private final Path path;
private final String input;
private final CommandLineOptions parameters;
private final JavaFormatterOptions options;
public FormatFileCallable(
- CommandLineOptions parameters, String input, JavaFormatterOptions options) {
+ CommandLineOptions parameters, Path path, String input, JavaFormatterOptions options) {
+ this.path = path;
this.input = input;
this.parameters = parameters;
this.options = options;
}
@Override
- public String call() throws FormatterException {
- if (parameters.fixImportsOnly()) {
- return fixImports(input);
- }
+ public Result call() {
+ try {
+ if (parameters.fixImportsOnly()) {
+ return Result.create(path, input, fixImports(input), /* exception= */ null);
+ }
- Formatter formatter = new Formatter(options);
- String formatted = formatter.formatSource(input, characterRanges(input).asRanges());
- formatted = fixImports(formatted);
- if (parameters.reflowLongStrings()) {
- formatted = StringWrapper.wrap(Formatter.MAX_LINE_LENGTH, formatted, formatter);
+ Formatter formatter = new Formatter(options);
+ String formatted = formatter.formatSource(input, characterRanges(input).asRanges());
+ formatted = fixImports(formatted);
+ if (parameters.reflowLongStrings()) {
+ formatted = StringWrapper.wrap(Formatter.MAX_LINE_LENGTH, formatted, formatter);
+ }
+ return Result.create(path, input, formatted, /* exception= */ null);
+ } catch (FormatterException e) {
+ return Result.create(path, input, /* output= */ null, e);
}
- return formatted;
}
private String fixImports(String input) throws FormatterException {
diff --git a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
index aac829d..5aa7a12 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
@@ -136,9 +136,9 @@ public final class Formatter {
JavacParser parser =
parserFactory.newParser(
javaInput.getText(),
- /*keepDocComments=*/ true,
- /*keepEndPos=*/ true,
- /*keepLineMap=*/ true);
+ /* keepDocComments= */ true,
+ /* keepEndPos= */ true,
+ /* keepLineMap= */ true);
unit = parser.parseCompilationUnit();
unit.sourcefile = source;
@@ -151,16 +151,14 @@ public final class Formatter {
OpsBuilder builder = new OpsBuilder(javaInput, javaOutput);
// Output the compilation unit.
JavaInputAstVisitor visitor;
- if (Runtime.version().feature() >= 14) {
- try {
- visitor =
- Class.forName("com.google.googlejavaformat.java.java14.Java14InputAstVisitor")
- .asSubclass(JavaInputAstVisitor.class)
- .getConstructor(OpsBuilder.class, int.class)
- .newInstance(builder, options.indentationMultiplier());
- } catch (ReflectiveOperationException e) {
- throw new LinkageError(e.getMessage(), e);
- }
+ if (Runtime.version().feature() >= 21) {
+ visitor =
+ createVisitor(
+ "com.google.googlejavaformat.java.java21.Java21InputAstVisitor", builder, options);
+ } else if (Runtime.version().feature() >= 17) {
+ visitor =
+ createVisitor(
+ "com.google.googlejavaformat.java.java17.Java17InputAstVisitor", builder, options);
} else {
visitor = new JavaInputAstVisitor(builder, options.indentationMultiplier());
}
@@ -173,6 +171,18 @@ public final class Formatter {
javaOutput.flush();
}
+ private static JavaInputAstVisitor createVisitor(
+ final String className, final OpsBuilder builder, final JavaFormatterOptions options) {
+ try {
+ return Class.forName(className)
+ .asSubclass(JavaInputAstVisitor.class)
+ .getConstructor(OpsBuilder.class, int.class)
+ .newInstance(builder, options.indentationMultiplier());
+ } catch (ReflectiveOperationException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ }
+
static boolean errorDiagnostic(Diagnostic<?> input) {
if (input.getKind() != Diagnostic.Kind.ERROR) {
return false;
@@ -262,7 +272,9 @@ public final class Formatter {
// TODO(cushon): this is only safe because the modifier ordering doesn't affect whitespace,
// and doesn't change the replacements that are output. This is not true in general for
// 'de-linting' changes (e.g. import ordering).
- javaInput = ModifierOrderer.reorderModifiers(javaInput, characterRanges);
+ if (options.reorderModifiers()) {
+ javaInput = ModifierOrderer.reorderModifiers(javaInput, characterRanges);
+ }
String lineSeparator = Newlines.guessLineSeparator(input);
JavaOutput javaOutput =
diff --git a/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java
new file mode 100644
index 0000000..3c315aa
--- /dev/null
+++ b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * 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.java;
+
+import static com.google.common.collect.Sets.toImmutableEnumSet;
+
+import com.google.auto.service.AutoService;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Set;
+import javax.lang.model.SourceVersion;
+import javax.tools.Tool;
+
+/** Provide a way to be invoked without necessarily starting a new VM. */
+@AutoService(Tool.class)
+public class GoogleJavaFormatTool implements Tool {
+ @Override
+ public String name() {
+ return "google-java-format";
+ }
+
+ @Override
+ public Set<SourceVersion> getSourceVersions() {
+ return Arrays.stream(SourceVersion.values()).collect(toImmutableEnumSet());
+ }
+
+ @Override
+ public int run(InputStream in, OutputStream out, OutputStream err, String... args) {
+ PrintStream outStream = new PrintStream(out);
+ PrintStream errStream = new PrintStream(err);
+ try {
+ return Main.main(in, outStream, errStream, args);
+ } catch (RuntimeException e) {
+ errStream.print(e.getMessage());
+ errStream.flush();
+ return 1; // pass non-zero value back indicating an error has happened
+ }
+ }
+}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java
index 7bcad4c..438eac5 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java
@@ -29,10 +29,11 @@ public class GoogleJavaFormatToolProvider implements ToolProvider {
@Override
public int run(PrintWriter out, PrintWriter err, String... args) {
try {
- return Main.main(out, err, args);
+ return Main.main(System.in, out, err, args);
} catch (RuntimeException e) {
err.print(e.getMessage());
- return -1; // pass non-zero value back indicating an error has happened
+ err.flush();
+ return 1; // pass non-zero value back indicating an error has happened
}
}
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java b/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java
index 346324a..d34ecc4 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java
@@ -53,11 +53,13 @@ public final class JavaCommentsHelper implements CommentsHelper {
}
if (tok.isSlashSlashComment()) {
return indentLineComments(lines, column0);
- } else if (javadocShaped(lines)) {
- return indentJavadoc(lines, column0);
- } else {
- return preserveIndentation(lines, column0);
}
+ return CommentsHelper.reformatParameterComment(tok)
+ .orElseGet(
+ () ->
+ javadocShaped(lines)
+ ? indentJavadoc(lines, column0)
+ : preserveIndentation(lines, column0));
}
// For non-javadoc-shaped block comments, shift the entire block to the correct
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java b/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java
index fbb6fe7..67c13d0 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java
@@ -14,6 +14,7 @@
package com.google.googlejavaformat.java;
+import com.google.auto.value.AutoValue;
import com.google.errorprone.annotations.Immutable;
/**
@@ -27,7 +28,8 @@ import com.google.errorprone.annotations.Immutable;
* preferences, and in fact it would work directly against our primary goals.
*/
@Immutable
-public class JavaFormatterOptions {
+@AutoValue
+public abstract class JavaFormatterOptions {
public enum Style {
/** The default Google Java Style configuration. */
@@ -47,27 +49,17 @@ public class JavaFormatterOptions {
}
}
- private final Style style;
- private final boolean formatJavadoc;
-
- private JavaFormatterOptions(Style style, boolean formatJavadoc) {
- this.style = style;
- this.formatJavadoc = formatJavadoc;
- }
-
/** Returns the multiplier for the unit of indent. */
public int indentationMultiplier() {
- return style.indentationMultiplier();
+ return style().indentationMultiplier();
}
- public boolean formatJavadoc() {
- return formatJavadoc;
- }
+ public abstract boolean formatJavadoc();
+
+ public abstract boolean reorderModifiers();
/** Returns the code style. */
- public Style style() {
- return style;
- }
+ public abstract Style style();
/** Returns the default formatting options. */
public static JavaFormatterOptions defaultOptions() {
@@ -76,28 +68,22 @@ public class JavaFormatterOptions {
/** Returns a builder for {@link JavaFormatterOptions}. */
public static Builder builder() {
- return new Builder();
+ return new AutoValue_JavaFormatterOptions.Builder()
+ .style(Style.GOOGLE)
+ .formatJavadoc(true)
+ .reorderModifiers(true);
}
/** A builder for {@link JavaFormatterOptions}. */
- public static class Builder {
- private Style style = Style.GOOGLE;
- private boolean formatJavadoc = true;
+ @AutoValue.Builder
+ public abstract static class Builder {
- private Builder() {}
+ public abstract Builder style(Style style);
- public Builder style(Style style) {
- this.style = style;
- return this;
- }
+ public abstract Builder formatJavadoc(boolean formatJavadoc);
- public Builder formatJavadoc(boolean formatJavadoc) {
- this.formatJavadoc = formatJavadoc;
- return this;
- }
+ public abstract Builder reorderModifiers(boolean reorderModifiers);
- public JavaFormatterOptions build() {
- return new JavaFormatterOptions(style, formatJavadoc);
- }
+ public abstract JavaFormatterOptions build();
}
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
index 165bdeb..7b5eb84 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
@@ -49,6 +49,7 @@ import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
@@ -349,7 +350,8 @@ public final class JavaInput extends Input {
stopTokens = ImmutableSet.<TokenKind>builder().addAll(stopTokens).add(TokenKind.EOF).build();
Context context = new Context();
Options.instance(context).put("--enable-preview", "true");
- new JavacFileManager(context, true, UTF_8);
+ JavaFileManager fileManager = new JavacFileManager(context, false, UTF_8);
+ context.put(JavaFileManager.class, fileManager);
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
context.put(DiagnosticListener.class, diagnosticCollector);
Log log = Log.instance(context);
@@ -385,7 +387,14 @@ public final class JavaInput extends Input {
final boolean isNumbered; // Is this tok numbered? (tokens and comments)
String extraNewline = null; // Extra newline at end?
List<String> strings = new ArrayList<>();
- if (Character.isWhitespace(tokText0)) {
+ if (tokText.startsWith("'")
+ || tokText.startsWith("\"")
+ || JavacTokens.isStringFragment(t.kind())) {
+ // Perform this check first, STRINGFRAGMENT tokens can start with arbitrary characters.
+ isToken = true;
+ isNumbered = true;
+ strings.add(originalTokText);
+ } else if (Character.isWhitespace(tokText0)) {
isToken = false;
isNumbered = false;
Iterator<String> it = Newlines.lineIterator(originalTokText);
@@ -402,10 +411,6 @@ public final class JavaInput extends Input {
strings.add(line);
}
}
- } else if (tokText.startsWith("'") || tokText.startsWith("\"")) {
- isToken = true;
- isNumbered = true;
- strings.add(originalTokText);
} else if (tokText.startsWith("//") || tokText.startsWith("/*")) {
// For compatibility with an earlier lexer, the newline after a // comment is its own tok.
if (tokText.startsWith("//")
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
index daed250..ea967b3 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
@@ -285,7 +285,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
ImmutableSetMultimap.Builder<String, String> result = ImmutableSetMultimap.builder();
for (String annotation :
ImmutableList.of(
+ "org.jspecify.annotations.NonNull",
+ "org.jspecify.annotations.Nullable",
"org.jspecify.nullness.Nullable",
+ "org.checkerframework.checker.nullness.qual.NonNull",
"org.checkerframework.checker.nullness.qual.Nullable")) {
String simpleName = annotation.substring(annotation.lastIndexOf('.') + 1);
result.put(simpleName, annotation);
@@ -927,7 +930,6 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
@Override
public Void visitMemberReference(MemberReferenceTree node, Void unused) {
- sync(node);
builder.open(plusFour);
scan(node.getQualifierExpression(), null);
builder.breakOp();
@@ -1247,7 +1249,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
token(",");
builder.breakOp(" ");
}
- scan(parameter, null);
+ visitVariables(
+ ImmutableList.of(parameter),
+ DeclarationKind.NONE,
+ fieldAnnotationDirection(parameter.getModifiers()));
first = false;
}
if (parens) {
@@ -2639,7 +2644,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
for (ExpressionTree thrownExceptionType : thrownExceptionTypes) {
if (!first) {
token(",");
- builder.breakToFill(" ");
+ builder.breakOp(" ");
}
scan(thrownExceptionType, null);
first = false;
@@ -3558,7 +3563,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
if (receiverExpression.isPresent()) {
scan(receiverExpression.get(), null);
} else {
- visit(name);
+ variableName(name);
}
builder.op(op);
}
@@ -3601,6 +3606,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
return baseDims;
}
+ protected void variableName(Name name) {
+ visit(name);
+ }
+
private void maybeAddDims(Deque<List<? extends AnnotationTree>> annotations) {
maybeAddDims(new ArrayDeque<>(), annotations);
}
@@ -3691,7 +3700,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
builder.breakOp(" ");
builder.open(ZERO);
maybeAddDims(dims);
- visit(fragment.getName());
+ variableName(fragment.getName());
maybeAddDims(dims);
ExpressionTree initializer = fragment.getInitializer();
if (initializer != null) {
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 c43a91a..656b65c 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 final class JavaOutput extends Output {
* 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/main/java/com/google/googlejavaformat/java/JavacTokens.java b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java
index ba7e3b7..dd8760b 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java
@@ -15,6 +15,7 @@
package com.google.googlejavaformat.java;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Arrays.stream;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -27,6 +28,7 @@ import com.sun.tools.javac.parser.Tokens.Token;
import com.sun.tools.javac.parser.Tokens.TokenKind;
import com.sun.tools.javac.parser.UnicodeReader;
import com.sun.tools.javac.util.Context;
+import java.util.Objects;
import java.util.Set;
/** A wrapper around javac's lexer. */
@@ -71,6 +73,16 @@ class JavacTokens {
}
}
+ private static final TokenKind STRINGFRAGMENT =
+ stream(TokenKind.values())
+ .filter(t -> t.name().contentEquals("STRINGFRAGMENT"))
+ .findFirst()
+ .orElse(null);
+
+ static boolean isStringFragment(TokenKind kind) {
+ return STRINGFRAGMENT != null && Objects.equals(kind, STRINGFRAGMENT);
+ }
+
/** Lex the input and return a list of {@link RawTok}s. */
public static ImmutableList<RawTok> getTokens(
String source, Context context, Set<TokenKind> stopTokens) {
@@ -106,13 +118,39 @@ class JavacTokens {
if (last < t.pos) {
tokens.add(new RawTok(null, null, last, t.pos));
}
- tokens.add(
- new RawTok(
- t.kind == TokenKind.STRINGLITERAL ? "\"" + t.stringVal() + "\"" : null,
- t.kind,
- t.pos,
- t.endPos));
- last = t.endPos;
+ int pos = t.pos;
+ int endPos = t.endPos;
+ if (isStringFragment(t.kind)) {
+ // A string template is tokenized as a series of STRINGFRAGMENT tokens containing the string
+ // literal values, followed by the tokens for the template arguments. For the formatter, we
+ // want the stream of tokens to appear in order by their start position, and also to have
+ // all the content from the original source text (including leading and trailing ", and the
+ // \ escapes from template arguments). This logic processes the token stream from javac to
+ // meet those requirements.
+ while (isStringFragment(t.kind)) {
+ endPos = t.endPos;
+ scanner.nextToken();
+ t = scanner.token();
+ }
+ // Read tokens for the string template arguments, until we read the end of the string
+ // template. The last token in a string template is always a trailing string fragment. Use
+ // lookahead to defer reading the token after the template until the next iteration of the
+ // outer loop.
+ while (scanner.token(/* lookahead= */ 1).endPos < endPos) {
+ scanner.nextToken();
+ t = scanner.token();
+ }
+ tokens.add(new RawTok(source.substring(pos, endPos), t.kind, pos, endPos));
+ last = endPos;
+ } else {
+ tokens.add(
+ new RawTok(
+ t.kind == TokenKind.STRINGLITERAL ? "\"" + t.stringVal() + "\"" : null,
+ t.kind,
+ t.pos,
+ t.endPos));
+ last = t.endPos;
+ }
} while (scanner.token().kind != TokenKind.EOF);
if (last < end) {
tokens.add(new RawTok(null, null, last, end));
diff --git a/core/src/main/java/com/google/googlejavaformat/java/Main.java b/core/src/main/java/com/google/googlejavaformat/java/Main.java
index 953ca58..0845e0e 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/Main.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java
@@ -16,25 +16,30 @@ package com.google.googlejavaformat.java;
import static java.lang.Math.min;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Comparator.comparing;
import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.MoreExecutors;
import com.google.googlejavaformat.FormatterDiagnostic;
import com.google.googlejavaformat.java.JavaFormatterOptions.Style;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
+import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
/** The main class for the Java formatter CLI. */
public final class Main {
@@ -62,24 +67,33 @@ public final class Main {
*
* @param args the command-line arguments
*/
- public static void main(String[] args) {
- PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out, UTF_8));
- PrintWriter err = new PrintWriter(new OutputStreamWriter(System.err, UTF_8));
- int result = main(out, err, args);
+ public static void main(String... args) {
+ int result = main(System.in, System.out, System.err, args);
System.exit(result);
}
/**
- * Package-private main entry point used this CLI program and the java.util.spi.ToolProvider
+ * Package-private main entry point used by the {@link javax.tools.Tool Tool} implementation in
+ * the same package as this Main class.
+ */
+ static int main(InputStream in, PrintStream out, PrintStream err, String... args) {
+ PrintWriter outWriter = new PrintWriter(new OutputStreamWriter(out, UTF_8));
+ PrintWriter errWriter = new PrintWriter(new OutputStreamWriter(err, UTF_8));
+ return main(in, outWriter, errWriter, args);
+ }
+
+ /**
+ * Package-private main entry point used by the {@link java.util.spi.ToolProvider ToolProvider}
* implementation in the same package as this Main class.
*/
- static int main(PrintWriter out, PrintWriter err, String... args) {
+ static int main(InputStream in, PrintWriter out, PrintWriter err, String... args) {
try {
- Main formatter = new Main(out, err, System.in);
+ Main formatter = new Main(out, err, in);
return formatter.format(args);
} catch (UsageException e) {
err.print(e.getMessage());
- return 0;
+ // We return exit code 2 to differentiate usage issues from code formatting issues.
+ return 2;
} finally {
err.flush();
out.flush();
@@ -120,50 +134,55 @@ public final class Main {
int numThreads = min(MAX_THREADS, parameters.files().size());
ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
- Map<Path, String> inputs = new LinkedHashMap<>();
- Map<Path, Future<String>> results = new LinkedHashMap<>();
+ ExecutorCompletionService<FormatFileCallable.Result> cs =
+ new ExecutorCompletionService<>(executorService);
boolean allOk = true;
+ int files = 0;
for (String fileName : parameters.files()) {
if (!fileName.endsWith(".java")) {
errWriter.println("Skipping non-Java file: " + fileName);
continue;
}
Path path = Paths.get(fileName);
- String input;
try {
- input = new String(Files.readAllBytes(path), UTF_8);
- inputs.put(path, input);
- results.put(
- path, executorService.submit(new FormatFileCallable(parameters, input, options)));
+ String input = new String(Files.readAllBytes(path), UTF_8);
+ cs.submit(new FormatFileCallable(parameters, path, input, options));
+ files++;
} catch (IOException e) {
errWriter.println(fileName + ": could not read file: " + e.getMessage());
allOk = false;
}
}
- for (Map.Entry<Path, Future<String>> result : results.entrySet()) {
- Path path = result.getKey();
- String formatted;
+ List<FormatFileCallable.Result> results = new ArrayList<>();
+ while (files > 0) {
try {
- formatted = result.getValue().get();
+ files--;
+ results.add(cs.take().get());
} catch (InterruptedException e) {
errWriter.println(e.getMessage());
allOk = false;
continue;
} catch (ExecutionException e) {
- if (e.getCause() instanceof FormatterException) {
- for (FormatterDiagnostic diagnostic : ((FormatterException) e.getCause()).diagnostics()) {
- errWriter.println(path + ":" + diagnostic);
- }
- } else {
- errWriter.println(path + ": error: " + e.getCause().getMessage());
- e.getCause().printStackTrace(errWriter);
+ errWriter.println("error: " + e.getCause().getMessage());
+ e.getCause().printStackTrace(errWriter);
+ allOk = false;
+ continue;
+ }
+ }
+ Collections.sort(results, comparing(FormatFileCallable.Result::path));
+ for (FormatFileCallable.Result result : results) {
+ Path path = result.path();
+ if (result.exception() != null) {
+ for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) {
+ errWriter.println(path + ":" + diagnostic);
}
allOk = false;
continue;
}
- boolean changed = !formatted.equals(inputs.get(path));
+ String formatted = result.output();
+ boolean changed = result.changed();
if (changed && parameters.setExitIfChanged()) {
allOk = false;
}
@@ -186,6 +205,10 @@ public final class Main {
outWriter.write(formatted);
}
}
+ if (!MoreExecutors.shutdownAndAwaitTermination(executorService, Duration.ofSeconds(5))) {
+ errWriter.println("Failed to shut down ExecutorService");
+ allOk = false;
+ }
return allOk ? 0 : 1;
}
@@ -198,9 +221,16 @@ public final class Main {
}
String stdinFilename = parameters.assumeFilename().orElse(STDIN_FILENAME);
boolean ok = true;
- try {
- String output = new FormatFileCallable(parameters, input, options).call();
- boolean changed = !input.equals(output);
+ FormatFileCallable.Result result =
+ new FormatFileCallable(parameters, null, input, options).call();
+ if (result.exception() != null) {
+ for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) {
+ errWriter.println(stdinFilename + ":" + diagnostic);
+ }
+ ok = false;
+ } else {
+ String output = result.output();
+ boolean changed = result.changed();
if (changed && parameters.setExitIfChanged()) {
ok = false;
}
@@ -211,12 +241,6 @@ public final class Main {
} else {
outWriter.write(output);
}
- } catch (FormatterException e) {
- for (FormatterDiagnostic diagnostic : e.diagnostics()) {
- errWriter.println(stdinFilename + ":" + diagnostic);
- }
- ok = false;
- // TODO(cpovirk): Catch other types of exception (as we do in the formatFiles case).
}
return ok ? 0 : 1;
}
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 20e55e9..a0fc2f5 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.DCTree.DCReference;
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;
@@ -252,7 +251,10 @@ public class RemoveUnusedImports {
ParserFactory parserFactory = ParserFactory.instance(context);
JavacParser parser =
parserFactory.newParser(
- javaInput, /*keepDocComments=*/ true, /*keepEndPos=*/ true, /*keepLineMap=*/ true);
+ javaInput,
+ /* keepDocComments= */ true,
+ /* keepEndPos= */ true,
+ /* keepLineMap= */ true);
unit = parser.parseCompilationUnit();
unit.sourcefile = source;
Iterable<Diagnostic<? extends JavaFileObject>> errorDiagnostics =
@@ -290,9 +292,7 @@ public class RemoveUnusedImports {
}
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(
@@ -301,18 +301,15 @@ public class RemoveUnusedImports {
Multimap<String, Range<Integer>> 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;
}
@@ -325,6 +322,15 @@ public class RemoveUnusedImports {
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<Integer, String> replacements) {
// save non-empty fixed ranges for reformatting after fixes are applied
diff --git a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
index c0f16e9..f241ae4 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
@@ -15,6 +15,7 @@
package com.google.googlejavaformat.java;
import static com.google.common.collect.Iterables.getLast;
+import static java.lang.Math.min;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
@@ -96,7 +97,9 @@ public final class StringWrapper {
if (!expected.equals(actual)) {
throw new FormatterException(
String.format(
- "Something has gone terribly wrong. Please file a bug: "
+ "Something has gone terribly wrong. We planned to make the below formatting change,"
+ + " but have aborted because it would unexpectedly change the AST.\n"
+ + "Please file a bug: "
+ "https://github.com/google/google-java-format/issues/new"
+ "\n\n=== Actual: ===\n%s\n=== Expected: ===\n%s\n",
actual, expected));
@@ -120,6 +123,10 @@ public final class StringWrapper {
if (literalTree.getKind() != Kind.STRING_LITERAL) {
return null;
}
+ int pos = getStartPosition(literalTree);
+ if (input.substring(pos, min(input.length(), pos + 3)).equals("\"\"\"")) {
+ return null;
+ }
Tree parent = getCurrentPath().getParentPath().getLeaf();
if (parent instanceof MemberSelectTree
&& ((MemberSelectTree) parent).getExpression().equals(literalTree)) {
@@ -396,7 +403,7 @@ public final class StringWrapper {
ParserFactory parserFactory = ParserFactory.instance(context);
JavacParser parser =
parserFactory.newParser(
- source, /*keepDocComments=*/ true, /*keepEndPos=*/ true, /*keepLineMap=*/ true);
+ source, /* keepDocComments= */ true, /* keepEndPos= */ true, /* keepLineMap= */ true);
unit = parser.parseCompilationUnit();
unit.sourcefile = sjfo;
Iterable<Diagnostic<? extends JavaFileObject>> errorDiagnostics =
diff --git a/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
index 890687f..6818f4a 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
@@ -12,7 +12,7 @@
* the License.
*/
-package com.google.googlejavaformat.java.java14;
+package com.google.googlejavaformat.java.java17;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Iterables.getOnlyElement;
@@ -25,9 +25,11 @@ import com.google.googlejavaformat.java.JavaInputAstVisitor;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.BindingPatternTree;
import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.CaseLabelTree;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.ModuleTree;
@@ -39,39 +41,23 @@ import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeInfo;
-import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import javax.lang.model.element.Name;
/**
- * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified for
- * Java 14.
+ * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified in
+ * Java 17.
*/
-public class Java14InputAstVisitor extends JavaInputAstVisitor {
- private static final Method COMPILATION_UNIT_TREE_GET_MODULE =
- maybeGetMethod(CompilationUnitTree.class, "getModule");
- private static final Method CLASS_TREE_GET_PERMITS_CLAUSE =
- maybeGetMethod(ClassTree.class, "getPermitsClause");
- private static final Method BINDING_PATTERN_TREE_GET_VARIABLE =
- maybeGetMethod(BindingPatternTree.class, "getVariable");
- private static final Method BINDING_PATTERN_TREE_GET_TYPE =
- maybeGetMethod(BindingPatternTree.class, "getType");
- private static final Method BINDING_PATTERN_TREE_GET_BINDING =
- maybeGetMethod(BindingPatternTree.class, "getBinding");
- private static final Method CASE_TREE_GET_LABELS = maybeGetMethod(CaseTree.class, "getLabels");
+public class Java17InputAstVisitor extends JavaInputAstVisitor {
- public Java14InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
+ public Java17InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
super(builder, indentMultiplier);
}
@Override
protected void handleModule(boolean first, CompilationUnitTree node) {
- if (COMPILATION_UNIT_TREE_GET_MODULE == null) {
- // Java < 17, see https://bugs.openjdk.java.net/browse/JDK-8255464
- return;
- }
- ModuleTree module = (ModuleTree) invoke(COMPILATION_UNIT_TREE_GET_MODULE, node);
+ ModuleTree module = node.getModule();
if (module != null) {
if (!first) {
builder.blankLineWanted(BlankLineWanted.YES);
@@ -84,34 +70,20 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
@Override
protected List<? extends Tree> getPermitsClause(ClassTree node) {
- if (CLASS_TREE_GET_PERMITS_CLAUSE != null) {
- return (List<? extends Tree>) invoke(CLASS_TREE_GET_PERMITS_CLAUSE, node);
- } else {
- // Java < 15
- return super.getPermitsClause(node);
- }
+ return node.getPermitsClause();
}
@Override
public Void visitBindingPattern(BindingPatternTree node, Void unused) {
sync(node);
- if (BINDING_PATTERN_TREE_GET_VARIABLE != null) {
- VariableTree variableTree = (VariableTree) invoke(BINDING_PATTERN_TREE_GET_VARIABLE, node);
- visitBindingPattern(
- variableTree.getModifiers(), variableTree.getType(), variableTree.getName());
- } else if (BINDING_PATTERN_TREE_GET_TYPE != null && BINDING_PATTERN_TREE_GET_BINDING != null) {
- Tree type = (Tree) invoke(BINDING_PATTERN_TREE_GET_TYPE, node);
- Name name = (Name) invoke(BINDING_PATTERN_TREE_GET_BINDING, node);
- visitBindingPattern(/* modifiers= */ null, type, name);
- } else {
- throw new LinkageError(
- "BindingPatternTree must have either getVariable() or both getType() and getBinding(),"
- + " but does not");
- }
+ VariableTree variableTree = node.getVariable();
+ visitBindingPattern(
+ variableTree.getModifiers(), variableTree.getType(), variableTree.getName());
return null;
}
private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) {
+ builder.open(plusFour);
if (modifiers != null) {
List<AnnotationTree> annotations =
visitModifiers(modifiers, Direction.HORIZONTAL, Optional.empty());
@@ -119,7 +91,12 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
}
scan(type, null);
builder.breakOp(" ");
- visit(name);
+ if (name.isEmpty()) {
+ token("_");
+ } else {
+ visit(name);
+ }
+ builder.close();
}
@Override
@@ -248,17 +225,14 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
sync(node);
markForPartialFormat();
builder.forcedBreak();
- List<? extends Tree> labels;
- boolean isDefault;
- if (CASE_TREE_GET_LABELS != null) {
- labels = (List<? extends Tree>) invoke(CASE_TREE_GET_LABELS, node);
- isDefault =
- labels.size() == 1
- && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL");
- } else {
- labels = node.getExpressions();
- isDefault = labels.isEmpty();
- }
+ List<? extends CaseLabelTree> labels = node.getLabels();
+ boolean isDefault =
+ labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL");
+ builder.open(
+ node.getCaseKind().equals(CaseTree.CaseKind.RULE)
+ && !node.getBody().getKind().equals(Tree.Kind.BLOCK)
+ ? plusFour
+ : ZERO);
if (isDefault) {
token("default", plusTwo);
} else {
@@ -276,6 +250,15 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
}
builder.close();
}
+
+ final ExpressionTree guard = getGuard(node);
+ if (guard != null) {
+ builder.space();
+ token("when");
+ builder.space();
+ scan(guard, null);
+ }
+
switch (node.getCaseKind()) {
case STATEMENT:
token(":");
@@ -287,8 +270,8 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
builder.space();
token("-");
token(">");
- builder.space();
if (node.getBody().getKind() == Tree.Kind.BLOCK) {
+ builder.space();
// Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks.
visitBlock(
(BlockTree) node.getBody(),
@@ -296,6 +279,7 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
AllowLeadingBlankLine.NO,
AllowTrailingBlankLine.NO);
} else {
+ builder.breakOp(" ");
scan(node.getBody(), null);
}
builder.guessToken(";");
@@ -303,22 +287,11 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
default:
throw new AssertionError(node.getCaseKind());
}
+ builder.close();
return null;
}
- private static Method maybeGetMethod(Class<?> c, String name) {
- try {
- return c.getMethod(name);
- } catch (ReflectiveOperationException e) {
- return null;
- }
- }
-
- private static Object invoke(Method m, Object target) {
- try {
- return m.invoke(target);
- } catch (ReflectiveOperationException e) {
- throw new LinkageError(e.getMessage(), e);
- }
+ protected ExpressionTree getGuard(final CaseTree node) {
+ return null;
}
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java
new file mode 100644
index 0000000..897d6ff
--- /dev/null
+++ b/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2023 The google-java-format Authors.
+ *
+ * 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.java.java21;
+
+import com.google.googlejavaformat.OpsBuilder;
+import com.google.googlejavaformat.java.java17.Java17InputAstVisitor;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.ConstantCaseLabelTree;
+import com.sun.source.tree.DeconstructionPatternTree;
+import com.sun.source.tree.DefaultCaseLabelTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.PatternCaseLabelTree;
+import com.sun.source.tree.PatternTree;
+import com.sun.source.tree.StringTemplateTree;
+import javax.lang.model.element.Name;
+
+/**
+ * Extends {@link Java17InputAstVisitor} with support for AST nodes that were added or modified in
+ * Java 21.
+ */
+public class Java21InputAstVisitor extends Java17InputAstVisitor {
+
+ public Java21InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
+ super(builder, indentMultiplier);
+ }
+
+ @Override
+ protected ExpressionTree getGuard(final CaseTree node) {
+ return node.getGuard();
+ }
+
+ @Override
+ public Void visitDefaultCaseLabel(DefaultCaseLabelTree node, Void unused) {
+ token("default");
+ return null;
+ }
+
+ @Override
+ public Void visitPatternCaseLabel(PatternCaseLabelTree node, Void unused) {
+ scan(node.getPattern(), null);
+ return null;
+ }
+
+ @Override
+ public Void visitConstantCaseLabel(ConstantCaseLabelTree node, Void aVoid) {
+ scan(node.getConstantExpression(), null);
+ return null;
+ }
+
+ @Override
+ public Void visitDeconstructionPattern(DeconstructionPatternTree node, Void unused) {
+ sync(node);
+ scan(node.getDeconstructor(), null);
+ builder.open(plusFour);
+ token("(");
+ builder.breakOp();
+ boolean first = true;
+ for (PatternTree pattern : node.getNestedPatterns()) {
+ if (!first) {
+ token(",");
+ builder.breakOp(" ");
+ }
+ first = false;
+ scan(pattern, null);
+ }
+ builder.close();
+ token(")");
+ return null;
+ }
+
+ @SuppressWarnings("preview")
+ @Override
+ public Void visitStringTemplate(StringTemplateTree node, Void aVoid) {
+ sync(node);
+ scan(node.getProcessor(), null);
+ token(".");
+ token(builder.peekToken().get());
+ return null;
+ }
+
+ @Override
+ protected void variableName(Name name) {
+ if (name.isEmpty()) {
+ token("_");
+ } else {
+ visit(name);
+ }
+ }
+}
diff --git a/core/src/main/resources/META-INF/native-image/reflect-config.json b/core/src/main/resources/META-INF/native-image/reflect-config.json
index 2c65803..4d30840 100644
--- a/core/src/main/resources/META-INF/native-image/reflect-config.json
+++ b/core/src/main/resources/META-INF/native-image/reflect-config.json
@@ -2,5 +2,23 @@
{
"name": "com.sun.tools.javac.parser.UnicodeReader",
"allDeclaredMethods": true
+ },
+ {
+ "name": "com.google.googlejavaformat.java.java17.Java17InputAstVisitor",
+ "methods": [
+ {
+ "name": "<init>",
+ "parameterTypes": ["com.google.googlejavaformat.OpsBuilder", "int"]
+ }
+ ]
+ },
+ {
+ "name": "com.google.googlejavaformat.java.java21.Java21InputAstVisitor",
+ "methods": [
+ {
+ "name": "<init>",
+ "parameterTypes": ["com.google.googlejavaformat.OpsBuilder", "int"]
+ }
+ ]
}
]
diff --git a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
index 61a4346..cf15ecb 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
@@ -52,6 +52,15 @@ public class FormatterIntegrationTest {
.putAll(15, "I603")
.putAll(16, "I588")
.putAll(17, "I683", "I684", "I696")
+ .putAll(
+ 21,
+ "SwitchGuardClause",
+ "SwitchRecord",
+ "SwitchDouble",
+ "SwitchUnderscore",
+ "I880",
+ "Unnamed",
+ "I981")
.build();
@Parameters(name = "{index}: {0}")
@@ -93,7 +102,7 @@ public class FormatterIntegrationTest {
String expectedOutput = outputs.get(fileName);
Optional<Integer> version =
VERSIONED_TESTS.inverse().get(fileName).stream().collect(toOptional());
- if (version.isPresent() && Runtime.version().feature() < version.get()) {
+ if (Runtime.version().feature() < version.orElse(Integer.MAX_VALUE)) {
continue;
}
testInputs.add(new Object[] {fileName, input, expectedOutput});
diff --git a/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java
index 15e4522..3d41a07 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java
@@ -46,7 +46,7 @@ public class GoogleJavaFormatToolProviderTest {
int result = format.run(new PrintWriter(out, true), new PrintWriter(err, true), "--help");
- assertThat(result).isEqualTo(0);
+ assertThat(result).isNotEqualTo(0);
String usage = err.toString();
diff --git a/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolTest.java b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolTest.java
new file mode 100644
index 0000000..691bb22
--- /dev/null
+++ b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * 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.java;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.ServiceLoader;
+import javax.tools.Tool;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link GoogleJavaFormatToolProvider}. */
+@RunWith(JUnit4.class)
+public class GoogleJavaFormatToolTest {
+
+ @Test
+ public void testUsageOutputAfterLoadingViaToolName() {
+ String name = "google-java-format";
+
+ assertThat(
+ ServiceLoader.load(Tool.class).stream()
+ .map(ServiceLoader.Provider::get)
+ .map(Tool::name))
+ .contains(name);
+
+ Tool format =
+ ServiceLoader.load(Tool.class).stream()
+ .filter(provider -> name.equals(provider.get().name()))
+ .findFirst()
+ .get()
+ .get();
+
+ InputStream in = new ByteArrayInputStream(new byte[0]);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayOutputStream err = new ByteArrayOutputStream();
+
+ int result = format.run(in, out, err, "--help");
+
+ assertThat(result).isNotEqualTo(0);
+
+ String usage = new String(err.toByteArray(), UTF_8);
+
+ // Check that doc links are included.
+ assertThat(usage).containsMatch("http.*/google-java-format");
+ assertThat(usage).contains("Usage: google-java-format");
+ }
+}
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 ac3eb39..42e12d8 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<String> 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() {
@@ -132,7 +131,7 @@ public class MainTest {
process.waitFor();
String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8);
assertThat(err).contains("Usage: google-java-format");
- assertThat(process.exitValue()).isEqualTo(0);
+ assertThat(process.exitValue()).isEqualTo(2);
}
// end to end javadoc formatting test
@@ -613,4 +612,27 @@ public class MainTest {
assertThat(main.format("--skip-javadoc-formatting", "-")).isEqualTo(0);
assertThat(out.toString()).isEqualTo(joiner.join(input));
}
+
+ @Test
+ public void reorderModifiersOptionTest() throws Exception {
+ String[] input = {
+ "class Test {", //
+ " static public void main(String... args) {}",
+ "}",
+ "",
+ };
+ String[] fixed = {
+ "class Test {", //
+ " public static void main(String... args) {}",
+ "}",
+ "",
+ };
+ String source = joiner.join(input);
+ assertThat(new Formatter(JavaFormatterOptions.builder().build()).formatSource(source))
+ .isEqualTo(joiner.join(fixed));
+ assertThat(
+ new Formatter(JavaFormatterOptions.builder().reorderModifiers(false).build())
+ .formatSource(source))
+ .isEqualTo(source);
+ }
}
diff --git a/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java b/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java
index b1142b3..1750049 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 final class PartialFormattingTest {
String input =
lines(
" package com.google.googlejavaformat.java;",
+ "",
"/*",
" * Copyright 2015 Google Inc.",
" *",
diff --git a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java
index 99e1b2f..f7be369 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java
@@ -15,6 +15,7 @@
package com.google.googlejavaformat.java;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
import com.google.common.base.Joiner;
import org.junit.Test;
@@ -52,6 +53,26 @@ public class StringWrapperTest {
assertThat(StringWrapper.wrap(100, input, new Formatter())).isEqualTo(output);
}
+ @Test
+ public void textBlock() throws Exception {
+ assumeTrue(Runtime.version().feature() >= 15);
+ String input =
+ lines(
+ "package com.mypackage;",
+ "public class ReproBug {",
+ " private String myString;",
+ " private ReproBug() {",
+ " String str =",
+ " \"\"\"",
+ " "
+ + " {\"sourceEndpoint\":\"ri.something.1-1.object-internal.1\",\"targetEndpoint\":\"ri.some"
+ + "thing.1-1.object-internal.2\",\"typeId\":\"typeId\"}\"\"\";",
+ " myString = str;",
+ " }",
+ "}");
+ assertThat(StringWrapper.wrap(100, input, new Formatter())).isEqualTo(input);
+ }
+
private static String lines(String... line) {
return Joiner.on('\n').join(line) + '\n';
}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input
index 7317f17..86e46d5 100644
--- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input
@@ -1,6 +1,6 @@
public class B20844369 {
private static final String ID_PATTERN =
- // TODO(user): add min/max lengths for the numbers here, e.g. android ID
+ // TODO(daw): add min/max lengths for the numbers here, e.g. android ID
"(?:(?<androidId>\\d+)\\+)?" // optional Android ID
+ "(?<type>\\d+)" // type
+ ":"
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output
index 62f9721..982dc2b 100644
--- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output
@@ -1,6 +1,6 @@
public class B20844369 {
private static final String ID_PATTERN =
- // TODO(user): add min/max lengths for the numbers here, e.g. android ID
+ // TODO(daw): add min/max lengths for the numbers here, e.g. android ID
"(?:(?<androidId>\\d+)\\+)?" // optional Android ID
+ "(?<type>\\d+)" // type
+ ":"
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B21283374.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B21283374.output
index f98a37b..777303e 100644
--- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B21283374.output
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B21283374.output
@@ -14,41 +14,41 @@ class B21283374 {
{
f(
- /*flagA=*/ Boolean.TRUE,
- /*flagB=*/ Boolean.FALSE,
- /*flagC=*/ Boolean.FALSE,
- /*flagD=*/ Boolean.FALSE,
- /*flagE=*/ Boolean.FALSE,
- /*flagF=*/ Boolean.FALSE,
- /*flagG=*/ Boolean.FALSE,
- /*flagH=*/ Boolean.FALSE,
- /*flagI=*/ Boolean.FALSE,
- /*flagJ=*/ Boolean.FALSE,
- /*flagK=*/ Boolean.FALSE);
+ /* flagA= */ Boolean.TRUE,
+ /* flagB= */ Boolean.FALSE,
+ /* flagC= */ Boolean.FALSE,
+ /* flagD= */ Boolean.FALSE,
+ /* flagE= */ Boolean.FALSE,
+ /* flagF= */ Boolean.FALSE,
+ /* flagG= */ Boolean.FALSE,
+ /* flagH= */ Boolean.FALSE,
+ /* flagI= */ Boolean.FALSE,
+ /* flagJ= */ Boolean.FALSE,
+ /* flagK= */ Boolean.FALSE);
f(
- /*flagA=*/ Boolean.TRUE,
- /*flagB=*/ Boolean.FALSE,
- /*flagC=*/ Boolean.FALSE,
- /*flagD=*/ Boolean.FALSE,
- /*flagE=*/ Boolean.FALSE,
- /*flagF=*/ Boolean.FALSE,
- /*flagG=*/ Boolean.FALSE,
- /*flagH=*/ Boolean.FALSE,
- /*flagI=*/ Boolean.FALSE,
- /*flagJ=*/ Boolean.FALSE,
- /*flagK=*/ Boolean.FALSE);
+ /* flagA= */ Boolean.TRUE,
+ /* flagB= */ Boolean.FALSE,
+ /* flagC= */ Boolean.FALSE,
+ /* flagD= */ Boolean.FALSE,
+ /* flagE= */ Boolean.FALSE,
+ /* flagF= */ Boolean.FALSE,
+ /* flagG= */ Boolean.FALSE,
+ /* flagH= */ Boolean.FALSE,
+ /* flagI= */ Boolean.FALSE,
+ /* flagJ= */ Boolean.FALSE,
+ /* flagK= */ Boolean.FALSE);
- assertThat(foo.barAndBaz(/*paramName=*/ false, thingy)).isEqualTo(new Something(""));
- assertThat(foo.barAndBaz(/*paramName=*/ false, thingy)).isEqualTo(new Something(""));
- assertThat(foo.barAndBaz(/*paramName=*/ false, thingy)).isEqualTo(new Something(""));
+ assertThat(foo.barAndBaz(/* paramName= */ false, thingy)).isEqualTo(new Something(""));
+ assertThat(foo.barAndBaz(/* paramName= */ false, thingy)).isEqualTo(new Something(""));
+ assertThat(foo.barAndBaz(/* paramName= */ false, thingy)).isEqualTo(new Something(""));
- f(/*paramName=*/ false);
+ f(/* paramName= */ false);
assertThat__________________________________________________________(
- /*paramName=*/ false, thingy);
+ /* paramName= */ false, thingy);
assertThat__________________________________________________________(
- /*paramName=*/ false, thingy);
+ /* paramName= */ false, thingy);
f(
arg1, /* which arg is this attached to? */
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B26694550.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B26694550.output
index 945a40f..aedafb7 100644
--- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B26694550.output
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B26694550.output
@@ -3,15 +3,15 @@ class B26694550 {
/* === not a param comment */
fffffffffffffffffffffffffffffff(
ImmutableList.copyOf(keys), /*&=*/
- /*keepGoing=*/ false,
+ /* keepGoing= */ false,
ggggggggggggggggggggggggggggggggggggggggggg);
fffffffffffffffffffffffffffffff(
ImmutableList.copyOf(keys),
- /*keepGoing=*/ false,
+ /* keepGoing= */ false,
ggggggggggggggggggggggggggggggggggggggggggg);
fffffffffffffffffffffffffffffff(
ImmutableList.copyOf(keys),
- /*foo_bar=*/ false,
+ /* foo_bar= */ false,
/*foo-bar=*/ false,
ggggggggggggggggggggggggggggggggggggggggggg);
}
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 b1e33e9..3d591f8 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/B28788559.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B28788559.input
deleted file mode 100644
index a38d10b..0000000
--- 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 8a1db80..0000000
--- 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/B308157568.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B308157568.input
new file mode 100644
index 0000000..089a3f1
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B308157568.input
@@ -0,0 +1,23 @@
+class C {
+ @A(0x14)
+ int f(Object o) {
+ @A(0x40)
+ int local;
+ try (@A(0x41)
+ JarFile jarFile = new JarFile("hello.jar")) {
+ } catch (
+ @A(0x42)
+ IOException e) {
+ }
+ if (o instanceof @A(0x43) String) {}
+ new @A(0x44) ArrayList<>();
+ Supplier<List<?>> a = @A(0x45) ArrayList::new;
+ Supplier<List<?>> b = @A(0x46) ImmutableList::of;
+ String s = (@A(0x47) String) o;
+ List<?> xs = new ArrayList<@A(0x48) String>();
+ xs = ImmutableList.<@A(0x49) String>of();
+ Supplier<List<?>> c = ArrayList<@A(0x4A) String>::new;
+ Supplier<List<?>> d = ImmutableList::<@A(0x4B) String>of;
+ return 0;
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B308157568.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B308157568.output
new file mode 100644
index 0000000..089a3f1
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B308157568.output
@@ -0,0 +1,23 @@
+class C {
+ @A(0x14)
+ int f(Object o) {
+ @A(0x40)
+ int local;
+ try (@A(0x41)
+ JarFile jarFile = new JarFile("hello.jar")) {
+ } catch (
+ @A(0x42)
+ IOException e) {
+ }
+ if (o instanceof @A(0x43) String) {}
+ new @A(0x44) ArrayList<>();
+ Supplier<List<?>> a = @A(0x45) ArrayList::new;
+ Supplier<List<?>> b = @A(0x46) ImmutableList::of;
+ String s = (@A(0x47) String) o;
+ List<?> xs = new ArrayList<@A(0x48) String>();
+ xs = ImmutableList.<@A(0x49) String>of();
+ Supplier<List<?>> c = ArrayList<@A(0x4A) String>::new;
+ Supplier<List<?>> d = ImmutableList::<@A(0x4B) String>of;
+ return 0;
+ }
+}
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 be76390..0000000
--- 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 025d237..0000000
--- 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 {}
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 9dc3426..d012d17 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 376e8b6..81ebd2a 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;
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.input
new file mode 100644
index 0000000..dfc8a4c
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.input
@@ -0,0 +1,19 @@
+class I880 {
+ public String f(int i) {
+ return switch (i) {
+ case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 -> "looooooooooooooooooooooooooooooooooooooooong expression";
+ default -> "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong expression";
+ };
+ }
+
+ public boolean test(int i) {
+ return switch (i) {
+ case 0 -> // zero
+ false;
+ case 1 -> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".length()
+ == 0;
+ default -> // otherwise
+ true;
+ };
+ }
+} \ No newline at end of file
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.output
new file mode 100644
index 0000000..f918665
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I880.output
@@ -0,0 +1,21 @@
+class I880 {
+ public String f(int i) {
+ return switch (i) {
+ case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ->
+ "looooooooooooooooooooooooooooooooooooooooong expression";
+ default ->
+ "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong expression";
+ };
+ }
+
+ public boolean test(int i) {
+ return switch (i) {
+ case 0 -> // zero
+ false;
+ case 1 ->
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".length() == 0;
+ default -> // otherwise
+ true;
+ };
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.input
new file mode 100644
index 0000000..0660079
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.input
@@ -0,0 +1,5 @@
+class I959 {
+ public void test() {
+ new File(".").listFiles((final var dir, final var name) -> true);
+ }
+} \ No newline at end of file
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.output
new file mode 100644
index 0000000..76a07f4
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I959.output
@@ -0,0 +1,5 @@
+class I959 {
+ public void test() {
+ new File(".").listFiles((final var dir, final var name) -> true);
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.input
new file mode 100644
index 0000000..bba0b72
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.input
@@ -0,0 +1,12 @@
+class Foo {
+ private static final int X = 42;
+ private static final String A = STR."\{X} = \{X}";
+ private static final String B = STR."";
+ private static final String C = STR."\{X}";
+ private static final String D = STR."\{X}\{X}";
+ private static final String E = STR."\{X}\{X}\{X}";
+ private static final String F = STR." \{X}";
+ private static final String G = STR."\{X} ";
+ private static final String H = STR."\{X} one long incredibly unbroken sentence moving from "+"topic to topic so that no-one had a chance to interrupt";
+ private static final String I = STR."\{X} \uD83D\uDCA9 ";
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.output
new file mode 100644
index 0000000..ff173fb
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I981.output
@@ -0,0 +1,14 @@
+class Foo {
+ private static final int X = 42;
+ private static final String A = STR."\{X} = \{X}";
+ private static final String B = STR."";
+ private static final String C = STR."\{X}";
+ private static final String D = STR."\{X}\{X}";
+ private static final String E = STR."\{X}\{X}\{X}";
+ private static final String F = STR." \{X}";
+ private static final String G = STR."\{X} ";
+ private static final String H =
+ STR."\{X} one long incredibly unbroken sentence moving from "
+ + "topic to topic so that no-one had a chance to interrupt";
+ private static final String I = STR."\{X} \uD83D\uDCA9 ";
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/M.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/M.output
index dbad1bb..d28d5dd 100644
--- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/M.output
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/M.output
@@ -36,8 +36,16 @@ class M {
/** JavaDoc. */
void methodName3(int x)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5, Exception6,
- Exception7, Exception8, Exception9 {}
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {}
/** JavaDoc. */
void methodName4(
@@ -114,8 +122,16 @@ class M {
int t,
int u,
int v)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5, Exception6,
- Exception7, Exception8, Exception9 {}
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {}
Pair<
Pair<Pair<Pair<T, T>, Pair<T, T>>, Pair<Pair<T, T>, Pair<T, T>>>,
@@ -135,8 +151,16 @@ class M {
Pair<Pair<Pair<T, T>, Pair<T, T>>, Pair<Pair<T, T>, Pair<T, T>>>,
Pair<Pair<Pair<T, T>, Pair<T, T>>, Pair<Pair<T, T>, Pair<T, T>>>>
methodName11(int x)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5, Exception6,
- Exception7, Exception8, Exception9 {
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {
return null;
}
@@ -225,8 +249,16 @@ class M {
int t,
int u,
int v)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5, Exception6,
- Exception7, Exception8, Exception9 {
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {
return null;
}
@@ -338,8 +370,16 @@ class M {
T30,
T31>
T methodName22(int x)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5, Exception6,
- Exception7, Exception8, Exception9 {
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {
return null;
}
@@ -518,8 +558,16 @@ class M {
int t,
int u,
int v)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5, Exception6,
- Exception7, Exception8, Exception9 {
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {
return null;
}
@@ -640,8 +688,16 @@ class M {
Pair<Pair<Pair<T, T>, Pair<T, T>>, Pair<Pair<T, T>, Pair<T, T>>>,
Pair<Pair<Pair<T, T>, Pair<T, T>>, Pair<Pair<T, T>, Pair<T, T>>>>
methodName(int x)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5,
- Exception6, Exception7, Exception8, Exception9 {
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {
return null;
}
@@ -829,8 +885,16 @@ class M {
int t,
int u,
int v)
- throws Exception0, Exception1, Exception2, Exception3, Exception4, Exception5,
- Exception6, Exception7, Exception8, Exception9 {
+ throws Exception0,
+ Exception1,
+ Exception2,
+ Exception3,
+ Exception4,
+ Exception5,
+ Exception6,
+ Exception7,
+ Exception8,
+ Exception9 {
return null;
}
@@ -1011,6 +1075,14 @@ class M {
ZZZZZZZZZZ z,
ZZZZZZZZZZ z,
ZZZZZZZZZZ z)
- throws EEEEEEEEEE, EEEEEEEEEE, EEEEEEEEEE, EEEEEEEEEE, EEEEEEEEEE, EEEEEEEEEE, EEEEEEEEEE,
- EEEEEEEEEE, EEEEEEEEEE, EEEEEEEEEE {}
+ throws EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE,
+ EEEEEEEEEE {}
}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.input
new file mode 100644
index 0000000..decebe9
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.input
@@ -0,0 +1,29 @@
+package com.google.googlejavaformat.java.test;
+
+/** Tests for formatting of ParameterComments. */
+class Q {
+ static void f(int a) {
+ f(/*a=*/ 1);
+ f(
+ /* a= */ 1
+ );
+ }
+ static void g(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) {
+ g(
+ /*a=*/ 1,
+ /*b=*/ 1,
+ /*c=*/ 1,
+ /*d=*/ 1,
+ /*e=*/ 1,
+ /*f=*/ 1,
+ /*g=*/ 1,
+ /*h=*/ 1,
+ /*i=*/ 1,
+ /*j=*/ 1,
+ /*k=*/ 1);
+ g(/*a=*/ 1, /*b=*/ 1, /*c=*/ 1, /*d=*/ 1, /*e=*/ 1, /*f=*/ 1, /*g=*/ 1, /*h=*/ 1, /*i=*/ 1);
+ }
+ static void h(Object... xs) {
+ h(/*xs...=*/ null);
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.output
new file mode 100644
index 0000000..d26ca11
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ParameterComment.output
@@ -0,0 +1,38 @@
+package com.google.googlejavaformat.java.test;
+
+/** Tests for formatting of ParameterComments. */
+class Q {
+ static void f(int a) {
+ f(/* a= */ 1);
+ f(/* a= */ 1);
+ }
+
+ static void g(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) {
+ g(
+ /* a= */ 1,
+ /* b= */ 1,
+ /* c= */ 1,
+ /* d= */ 1,
+ /* e= */ 1,
+ /* f= */ 1,
+ /* g= */ 1,
+ /* h= */ 1,
+ /* i= */ 1,
+ /* j= */ 1,
+ /* k= */ 1);
+ g(
+ /* a= */ 1,
+ /* b= */ 1,
+ /* c= */ 1,
+ /* d= */ 1,
+ /* e= */ 1,
+ /* f= */ 1,
+ /* g= */ 1,
+ /* h= */ 1,
+ /* i= */ 1);
+ }
+
+ static void h(Object... xs) {
+ h(/* xs...= */ null);
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.input
new file mode 100644
index 0000000..54e22a7
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.input
@@ -0,0 +1,7 @@
+class SwitchDouble {
+ void x(Object o) {
+ switch (o) {
+ case null, default:
+ }
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.output
new file mode 100644
index 0000000..54e22a7
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchDouble.output
@@ -0,0 +1,7 @@
+class SwitchDouble {
+ void x(Object o) {
+ switch (o) {
+ case null, default:
+ }
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input
new file mode 100644
index 0000000..25df580
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input
@@ -0,0 +1,9 @@
+class SwitchGuardClause {
+ boolean test(Object x) {
+ return switch (x) {
+ case String s when s.length() < 5 -> true;
+ case Integer i -> false;
+ default -> true;
+ };
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output
new file mode 100644
index 0000000..25df580
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output
@@ -0,0 +1,9 @@
+class SwitchGuardClause {
+ boolean test(Object x) {
+ return switch (x) {
+ case String s when s.length() < 5 -> true;
+ case Integer i -> false;
+ default -> true;
+ };
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.input
new file mode 100644
index 0000000..2f4fb35
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.input
@@ -0,0 +1,21 @@
+record SwitchRecord(int i) {
+ int x(Object o) {
+ return switch (o) {
+ case SwitchRecord(int i) -> i;
+ default -> 0;
+ };
+ }
+ int f(Object o) {
+ return switch (o) {
+ case SwitchRecord(int one, int two, int three, int four, int five, int six, int seven, int eight, int nine) -> nine;
+ default -> 0;
+ };
+ }
+ int g(Object o) {
+ return switch (o) {
+ case SwitchRecord(int one, int two, int three, int four, int five, int six, int seven, int eight, int nine) ->
+ System.err.println("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.");
+ default -> 0;
+ };
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.output
new file mode 100644
index 0000000..89d212f
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchRecord.output
@@ -0,0 +1,46 @@
+record SwitchRecord(int i) {
+ int x(Object o) {
+ return switch (o) {
+ case SwitchRecord(int i) -> i;
+ default -> 0;
+ };
+ }
+
+ int f(Object o) {
+ return switch (o) {
+ case SwitchRecord(
+ int one,
+ int two,
+ int three,
+ int four,
+ int five,
+ int six,
+ int seven,
+ int eight,
+ int nine) ->
+ nine;
+ default -> 0;
+ };
+ }
+
+ int g(Object o) {
+ return switch (o) {
+ case SwitchRecord(
+ int one,
+ int two,
+ int three,
+ int four,
+ int five,
+ int six,
+ int seven,
+ int eight,
+ int nine) ->
+ System.err.println(
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
+ + " incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis"
+ + " nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+ + " consequat.");
+ default -> 0;
+ };
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.input
new file mode 100644
index 0000000..8d611d2
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.input
@@ -0,0 +1,8 @@
+public class SwitchUnderscore {
+ void x(Object o) {
+ switch (o) {
+ case String _:
+ default:
+ }
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.output
new file mode 100644
index 0000000..8d611d2
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchUnderscore.output
@@ -0,0 +1,8 @@
+public class SwitchUnderscore {
+ void x(Object o) {
+ switch (o) {
+ case String _:
+ default:
+ }
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.input
new file mode 100644
index 0000000..6c5efd1
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.input
@@ -0,0 +1,44 @@
+class Unnamed {
+ {
+ int acc = 0;
+ for (Order _ : orders) {
+ if (acc < LIMIT) {
+ acc++;
+ }
+ }
+
+ for (int i = 0, _ = sideEffect(); i < 10; i++) { }
+
+ Queue<Integer> q = null;
+ while (q.size() >= 3) {
+ var x = q.remove();
+ var y = q.remove();
+ var _ = q.remove();
+ new Point(x, y);
+ }
+
+ while (q.size() >= 3) {
+ var x = q.remove();
+ var _ = q.remove();
+ var _ = q.remove();
+ new Point(x, 0) ;
+ }
+
+ String s = null;
+ try {
+ int i = Integer.parseInt(s);
+ } catch (NumberFormatException _) {
+ System.out.println("Bad number: " + s);
+ }
+
+ try { doSomething(); }
+ catch (Exception _) { doSomething(); }
+ catch (Throwable _) { doSomething(); }
+
+ try (var _ = ScopedContext.acquire()) {
+ doSomething();
+ }
+
+ stream.collect(Collectors.toMap(String::toUpperCase, _ -> "NODATA"));
+ }
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.output
new file mode 100644
index 0000000..84d8f87
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/Unnamed.output
@@ -0,0 +1,48 @@
+class Unnamed {
+ {
+ int acc = 0;
+ for (Order _ : orders) {
+ if (acc < LIMIT) {
+ acc++;
+ }
+ }
+
+ for (int i = 0, _ = sideEffect(); i < 10; i++) {}
+
+ Queue<Integer> q = null;
+ while (q.size() >= 3) {
+ var x = q.remove();
+ var y = q.remove();
+ var _ = q.remove();
+ new Point(x, y);
+ }
+
+ while (q.size() >= 3) {
+ var x = q.remove();
+ var _ = q.remove();
+ var _ = q.remove();
+ new Point(x, 0);
+ }
+
+ String s = null;
+ try {
+ int i = Integer.parseInt(s);
+ } catch (NumberFormatException _) {
+ System.out.println("Bad number: " + s);
+ }
+
+ try {
+ doSomething();
+ } catch (Exception _) {
+ doSomething();
+ } catch (Throwable _) {
+ doSomething();
+ }
+
+ try (var _ = ScopedContext.acquire()) {
+ doSomething();
+ }
+
+ stream.collect(Collectors.toMap(String::toUpperCase, _ -> "NODATA"));
+ }
+}
diff --git a/eclipse_plugin/pom.xml b/eclipse_plugin/pom.xml
index 534e685..9e6acda 100644
--- a/eclipse_plugin/pom.xml
+++ b/eclipse_plugin/pom.xml
@@ -32,7 +32,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <tycho-version>2.6.0</tycho-version>
+ <tycho-version>3.0.0</tycho-version>
</properties>
<dependencies>
@@ -56,7 +56,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
- <version>3.2.0</version>
+ <version>3.3.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
diff --git a/idea_plugin/build.gradle b/idea_plugin/build.gradle
deleted file mode 100644
index d9f769d..0000000
--- a/idea_plugin/build.gradle
+++ /dev/null
@@ -1,62 +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.3.1"
-}
-
-repositories {
- mavenCentral()
-}
-
-ext {
- googleJavaFormatVersion = "1.13.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 = "221.3427-EAP-CANDIDATE-SNAPSHOT"
-}
-
-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"
- untilBuild = ""
-}
-
-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/build.gradle.kts b/idea_plugin/build.gradle.kts
new file mode 100644
index 0000000..5e96582
--- /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.15.0" }
+
+apply(plugin = "org.jetbrains.intellij")
+
+apply(plugin = "java")
+
+repositories { mavenCentral() }
+
+val googleJavaFormatVersion = "1.17.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<Test>().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.5")
+}
diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java b/idea_plugin/src/com/google/googlejavaformat/intellij/CodeStyleManagerDecorator.java
deleted file mode 100644
index af5da95..0000000
--- a/idea_plugin/src/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<? extends TextRange> 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<? extends TextRange> 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 <T extends Throwable> void performActionWithFormatterDisabled(ThrowableRunnable<T> r)
- throws T {
- delegate.performActionWithFormatterDisabled(r);
- }
-
- @Override
- public <T> T performActionWithFormatterDisabled(Computable<T> 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/com/google/googlejavaformat/intellij/FormatterUtil.java b/idea_plugin/src/com/google/googlejavaformat/intellij/FormatterUtil.java
deleted file mode 100644
index a5e69c9..0000000
--- a/idea_plugin/src/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<TextRange, String> getReplacements(
- Formatter formatter, String text, Collection<? extends TextRange> ranges) {
- try {
- ImmutableMap.Builder<TextRange, String> 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<Range<Integer>> toRanges(Collection<? extends TextRange> textRanges) {
- return textRanges.stream()
- .map(textRange -> Range.closedOpen(textRange.getStartOffset(), textRange.getEndOffset()))
- .collect(Collectors.toList());
- }
-
- private static TextRange toTextRange(Range<Integer> 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/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java b/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java
deleted file mode 100644
index 3d56743..0000000
--- a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatCodeStyleManager.java
+++ /dev/null
@@ -1,172 +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<? extends TextRange> 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<TextRange> 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<? extends TextRange> 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<? extends TextRange> 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.
- *
- * <p>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<? extends TextRange> ranges) {
- Style style = GoogleJavaFormatSettings.getInstance(getProject()).getStyle();
- Formatter formatter = new Formatter(JavaFormatterOptions.builder().style(style).build());
- performReplacements(
- document, FormatterUtil.getReplacements(formatter, document.getText(), ranges));
- }
-
- private void performReplacements(
- final Document document, final Map<TextRange, String> replacements) {
-
- if (replacements.isEmpty()) {
- return;
- }
-
- TreeMap<TextRange, String> sorted = new TreeMap<>(comparing(TextRange::getStartOffset));
- sorted.putAll(replacements);
- WriteCommandAction.runWriteCommandAction(
- getProject(),
- () -> {
- for (Entry<TextRange, String> 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/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java b/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatInstaller.java
deleted file mode 100644
index c606736..0000000
--- a/idea_plugin/src/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/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form
index 1db1d79..1db1d79 100644
--- a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form
+++ b/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
index 2f34b74..2f34b74 100644
--- a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java
+++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java
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 0000000..9d2d7a5
--- /dev/null
+++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java
@@ -0,0 +1,141 @@
+/*
+ * 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<ImportOptimizer> IMPORT_OPTIMIZERS =
+ ImmutableSet.of(new GoogleJavaFormatImportOptimizer());
+
+ @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);
+ }
+
+ @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<Feature> getFeatures() {
+ return Set.of(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<ImportOptimizer> 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<Range<Integer>> 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<TextRange> 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 0000000..4251242
--- /dev/null
+++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatImportOptimizer.java
@@ -0,0 +1,90 @@
+/*
+ * 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();
+
+ if (!JreConfigurationChecker.checkJreConfiguration(file.getProject())) {
+ return Runnables.doNothing();
+ }
+
+ PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
+ Document document = documentManager.getDocument(file);
+
+ if (document == null) {
+ return Runnables.doNothing();
+ }
+
+ JavaFormatterOptions.Style style = GoogleJavaFormatSettings.getInstance(project).getStyle();
+
+ final String origText = document.getText();
+ String text;
+ try {
+ text = ImportOrderer.reorderImports(RemoveUnusedImports.removeUnusedImports(origText), style);
+ } catch (FormatterException e) {
+ Notifications.displayParsingErrorNotification(project, file.getName());
+ return Runnables.doNothing();
+ }
+
+ // pointless to change document text if it hasn't changed, plus this can interfere with
+ // e.g. GoogleJavaFormattingService's output, i.e. it can overwrite the results from the main
+ // formatter.
+ if (text.equals(origText)) {
+ return Runnables.doNothing();
+ }
+
+ return () -> {
+ if (documentManager.isDocumentBlockedByPsi(document)) {
+ documentManager.doPostponedOperationsAndUnblockDocument(document);
+ }
+
+ // similarly to above, don't overwrite new document text if it has changed - we use
+ // getCharsSequence() as we should have `writeAction()` (which I think means effectively a
+ // write-lock) and it saves calling getText(), which apparently is expensive.
+ CharSequence newText = document.getCharsSequence();
+ if (CharSequence.compare(origText, newText) != 0) {
+ return;
+ }
+
+ document.setText(text);
+ };
+ }
+}
diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java
index 1e92a4b..ee187c0 100644
--- a/idea_plugin/src/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java
+++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java
@@ -18,7 +18,6 @@ package com.google.googlejavaformat.intellij;
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;
@@ -30,10 +29,16 @@ import org.jetbrains.annotations.NotNull;
storages = {@Storage("google-java-format.xml")})
class GoogleJavaFormatSettings implements PersistentStateComponent<GoogleJavaFormatSettings.State> {
+ 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);
+ return project.getService(GoogleJavaFormatSettings.class);
}
@Nullable
@@ -56,6 +61,9 @@ class GoogleJavaFormatSettings implements PersistentStateComponent<GoogleJavaFor
}
void setEnabled(EnabledState enabled) {
+ if (enabled.equals(EnabledState.ENABLED)) {
+ JreConfigurationChecker.checkJreConfiguration(project);
+ }
state.enabled = enabled;
}
diff --git a/idea_plugin/src/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java
index 1906347..940def6 100644
--- a/idea_plugin/src/com/google/googlejavaformat/intellij/InitialConfigurationProjectManagerListener.java
+++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/InitialConfigurationStartupActivity.java
@@ -21,22 +21,24 @@ import com.intellij.notification.NotificationGroup;
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()) {
settings.setEnabled(false);
displayNewUserNotification(project, settings);
+ } else if (settings.isEnabled()) {
+ JreConfigurationChecker.checkJreConfiguration(project);
}
}
@@ -47,11 +49,12 @@ final class InitialConfigurationProjectManagerListener implements ProjectManager
NOTIFICATION_TITLE,
"The google-java-format plugin is disabled by default. "
+ "<a href=\"enable\">Enable for this project</a>.",
- 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/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java
new file mode 100644
index 0000000..5084b6a
--- /dev/null
+++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/JreConfigurationChecker.java
@@ -0,0 +1,104 @@
+/*
+ * 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 static final Supplier<Boolean> hasAccess =
+ Suppliers.memoize(JreConfigurationChecker::checkJreConfiguration);
+ private static final Logger logger = Logger.getInstance(JreConfigurationChecker.class);
+
+ private final Project project;
+
+ public JreConfigurationChecker(Project project) {
+ this.project = project;
+ }
+
+ static boolean checkJreConfiguration(Project project) {
+ 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 static boolean checkJreConfiguration() {
+ try {
+ 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 static boolean testClassAccess(String... classNames) throws ClassNotFoundException {
+ for (String className : classNames) {
+ if (!testClassAccess(className)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ 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(),
+ JreConfigurationChecker.class.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. "
+ + "<a href=\"instructions\">Follow the instructions here</a>.",
+ 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/java/com/google/googlejavaformat/intellij/Notifications.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/Notifications.java
new file mode 100644
index 0000000..d32aa98
--- /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/com/google/googlejavaformat/intellij/UiFormatterStyle.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java
index 24ed6f6..24ed6f6 100644
--- a/idea_plugin/src/com/google/googlejavaformat/intellij/UiFormatterStyle.java
+++ b/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
index f10cde5..1b1e67f 100644
--- a/idea_plugin/resources/META-INF/plugin.xml
+++ b/idea_plugin/src/main/resources/META-INF/plugin.xml
@@ -22,13 +22,36 @@
Google
</vendor>
- <!-- Mark it as available on all JetBrains IDEs. It's really only useful in
- IDEA and Android Studio, but there's no way to specify that for some
- reason. It won't crash PyCharm or anything, so whatever. -->
- <depends>com.intellij.java</depends>
+ <depends>com.intellij.modules.java</depends>
+ <depends>com.intellij.modules.lang</depends>
+ <depends>com.intellij.modules.platform</depends>
+ <description><![CDATA[
+ Formats source code using the google-java-format tool.<p>
+
+ This plugin requires additional IDE configuration. For more information,
+ <a href="https://github.com/google/google-java-format/blob/master/README.md#intellij-jre-config">read
+ the documentation.</a>
+ ]]></description>
<change-notes><![CDATA[
<dl>
+ <dt>1.17.0.0</dt>
+ <dd>Updated to use google-java-format 1.17.0.</dd>
+ <dd>Fixed "Document is locked" errors (Thanks, <code>@facboy</code>!)</dd>
+ <dt>1.16.0.2</dt>
+ <dd>Disable AD_HOC_FORMATTING, which should stop the formatter from running so often when it wasn't specifically requested.
+ <dt>1.16.0.1</dt>
+ <dd>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.
+ <dt>1.16.0.0</dt>
+ <dd>Updated to use google-java-format 1.16.0.</dd>
+ <dd>Use the new IDE formatting APIs for a simplified plugin.</dd>
+ <dd>Optimize Imports now uses google-java-format.</dd>
+ <dt>1.15.0.0</dt>
+ <dd>Updated to use google-java-format 1.15.0.</dd>
+ <dt>1.14.0.0</dt>
+ <dd>Updated to use google-java-format 1.14.</dd>
<dt>1.13.0.0</dt>
<dd>Updated to use google-java-format 1.13.</dd>
<dt>1.12.0.0</dt>
@@ -58,23 +81,23 @@
</dl>
]]></change-notes>
- <applicationListeners>
- <listener
- class="com.google.googlejavaformat.intellij.InitialConfigurationProjectManagerListener"
- topic="com.intellij.openapi.project.ProjectManagerListener"/>
- <listener class="com.google.googlejavaformat.intellij.GoogleJavaFormatInstaller"
- topic="com.intellij.openapi.project.ProjectManagerListener"/>
- </applicationListeners>
-
<extensions defaultExtensionNs="com.intellij">
+ <formattingService
+ implementation="com.google.googlejavaformat.intellij.GoogleJavaFormatFormattingService"/>
+ <postStartupActivity implementation="com.google.googlejavaformat.intellij.InitialConfigurationStartupActivity"/>
<projectConfigurable
instance="com.google.googlejavaformat.intellij.GoogleJavaFormatConfigurable"
id="google-java-format.settings"
displayName="google-java-format Settings"/>
<projectService
serviceImplementation="com.google.googlejavaformat.intellij.GoogleJavaFormatSettings"/>
+ <projectService serviceImplementation="com.google.googlejavaformat.intellij.JreConfigurationChecker"/>
<notificationGroup displayType="STICKY_BALLOON" id="Enable google-java-format"
+ isLogByDefault="false"/>
+ <notificationGroup displayType="STICKY_BALLOON" id="Configure JRE for google-java-format"
isLogByDefault="true"/>
+ <notificationGroup displayType="BALLOON" id="google-java-format parsing error"
+ isLogByDefault="false"/>
</extensions>
</idea-plugin>
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 0000000..fec086c
--- /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<IdeaProjectTestFixture> 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.<PsiElement>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.<PsiElement>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.<PsiElement>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.<PsiElement>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 0000000..ad9fe27
--- /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<IdeaProjectTestFixture> 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<ImportOptimizer> 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();
+ };
+ }
+ };
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index c7f6856..8b3ca51 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,7 +27,6 @@
<modules>
<module>core</module>
- <module>eclipse_plugin</module>
</modules>
<name>Google Java Format Parent</name>
@@ -87,13 +86,13 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
- <guava.version>31.0.1-jre</guava.version>
+ <guava.version>32.1.3-jre</guava.version>
<truth.version>1.1.3</truth.version>
<checker.version>3.21.2</checker.version>
- <errorprone.version>2.11.0</errorprone.version>
+ <errorprone.version>2.16</errorprone.version>
<auto-value.version>1.9</auto-value.version>
<auto-service.version>1.0.1</auto-service.version>
- <maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
+ <maven-javadoc-plugin.version>3.4.0</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
</properties>
@@ -173,7 +172,7 @@
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>3.3.1</version>
+ <version>3.4.0</version>
</plugin>
<plugin>
<artifactId>maven-gpg-plugin</artifactId>
@@ -198,13 +197,13 @@
<!-- compile-time arguments for google-java-format -->
<arg>-XDcompilePolicy=simple</arg>
<arg>-Xplugin:ErrorProne</arg>
+ <arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
+ <arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
- <arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
- <arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
@@ -233,6 +232,16 @@
<manifest>
<mainClass>com.google.googlejavaformat.java.Main</mainClass>
</manifest>
+ <manifestEntries>
+ <Add-Exports><!--
+ -->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.parser <!--
+ -->jdk.compiler/com.sun.tools.javac.tree <!--
+ -->jdk.compiler/com.sun.tools.javac.util<!--
+ --></Add-Exports>
+ </manifestEntries>
</archive>
</configuration>
</plugin>
@@ -251,7 +260,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>3.3.1</version>
+ <version>3.4.0</version>
<configuration>
<doclint>none</doclint>
</configuration>
@@ -349,5 +358,14 @@
</plugins>
</build>
</profile>
+ <profile>
+ <id>eclipse</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <modules>
+ <module>eclipse_plugin</module>
+ </modules>
+ </profile>
</profiles>
</project>
diff --git a/scripts/mvn-deploy.sh b/scripts/mvn-deploy.sh
deleted file mode 100755
index 0133e36..0000000
--- a/scripts/mvn-deploy.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-# Copyright 2020 Google Inc.
-#
-# 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.
-
-if [ $# -lt 1 ]; then
- echo "usage $0 <ssl-key> [<param> ...]"
- exit 1;
-fi
-key=${1}
-shift
-params=${@}
-
-#validate key
-keystatus=$(gpg --list-keys | grep ${key} | awk '{print $1}')
-if [ "${keystatus}" != "pub" ]; then
- echo "Could not find public key with label ${key}"
- echo -n "Available keys from: "
- gpg --list-keys | grep --invert-match '^sub'
-
- exit 1
-fi
-
-mvn ${params} -P sonatype-oss-release -Dgpg.keyname=${key} deploy