diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 51ecf6e80..ab0ebf024 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,4 +1,4 @@
-# Copyright 2020 The Error Prone Authors.
+# Copyright 2020 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.
@@ -17,22 +17,22 @@ name: CI
on:
push:
branches:
- - master
+ - master
pull_request:
branches:
- - master
+ - master
jobs:
- test:
+ test-OpenJDK:
name: "JDK ${{ matrix.java }} on ${{ matrix.os }}"
strategy:
fail-fast: false
matrix:
- os: [ ubuntu-latest ]
- java: [ 21, 17, 11 ]
- experimental: [ false ]
+ os: [ubuntu-latest]
+ java: [21, 17, 11]
+ experimental: [false]
include:
- # Only test on macos and windows with a single recent JDK to avoid a
+ # Only test on MacOS and Windows with a single recent JDK to avoid a
# combinatorial explosion of test configurations.
- os: macos-latest
java: 21
@@ -50,47 +50,86 @@ jobs:
uses: styfle/cancel-workflow-action@0.9.1
with:
access_token: ${{ github.token }}
- - name: 'Check out repository'
+ - name: "Check out repository"
uses: actions/checkout@v4
- - name: 'Set up JDK ${{ matrix.java }} from jdk.java.net'
+ - 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' }}
+ - name: "Set up JDK ${{ matrix.java }} from Zulu"
+ if: ${{ matrix.java != 'EA' && matrix.java != 'GraalVM' }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
- distribution: 'zulu'
- cache: 'maven'
- - name: 'Install'
+ distribution: "zulu"
+ cache: "maven"
+ - name: "Set up JDK ${{ matrix.java }}"
+ if: ${{ matrix.java == 'GraalVM' }}
+ uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: "21"
+ distribution: "graalvm-community"
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ native-image-job-reports: "true"
+ cache: "maven"
+ - name: "Install"
shell: bash
run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
- - name: 'Test'
+ - name: "Test"
shell: bash
run: mvn test -B
+ test-GraalVM:
+ name: "GraalVM on ${{ matrix.os }}"
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ continue-on-error: true
+ steps:
+ - name: Cancel previous
+ uses: styfle/cancel-workflow-action@0.9.1
+ with:
+ access_token: ${{ github.token }}
+ - name: "Check out repository"
+ uses: actions/checkout@v4
+ - name: "Set up GraalVM ${{ matrix.java }}"
+ uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: "21"
+ distribution: "graalvm-community"
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ native-image-job-reports: "true"
+ cache: "maven"
+ - name: "Native Build"
+ run: mvn -Pnative -DskipTests package -pl core -am
+ - name: "Native Test"
+ # Bash script for testing won't work on Windows
+ # TODO: Anyone reading this wants to write a *.bat or *.ps1 equivalent?
+ if: ${{ matrix.os != 'windows-latest' }}
+ run: util/test-native.sh
+
publish_snapshot:
- name: 'Publish snapshot'
- needs: test
+ name: "Publish snapshot"
+ needs: test-OpenJDK
if: github.event_name == 'push' && github.repository == 'google/google-java-format' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- - name: 'Check out repository'
+ - name: "Check out repository"
uses: actions/checkout@v4
- - name: 'Set up JDK 17'
+ - name: "Set up JDK 17"
uses: actions/setup-java@v4
with:
java-version: 17
- distribution: 'zulu'
- cache: 'maven'
+ distribution: "zulu"
+ cache: "maven"
server-id: sonatype-nexus-snapshots
server-username: CI_DEPLOY_USERNAME
server-password: CI_DEPLOY_PASSWORD
- - name: 'Publish'
+ - name: "Publish"
env:
CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }}
CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cf50d61c6..2f309026b 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,3 +1,17 @@
+# Copyright 2020 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.
+
name: Release google-java-format
on:
@@ -20,8 +34,8 @@ jobs:
uses: actions/setup-java@v4
with:
java-version: 21
- distribution: 'zulu'
- cache: 'maven'
+ distribution: "zulu"
+ cache: "maven"
server-id: sonatype-nexus-staging
server-username: CI_DEPLOY_USERNAME
server-password: CI_DEPLOY_PASSWORD
@@ -46,18 +60,16 @@ jobs:
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 }}"
+ run: mvn --no-transfer-progress -pl '!eclipse_plugin' -P sonatype-oss-release clean deploy -Dgpg.passphrase="${{ secrets.GPG_PASSPHRASE }}"
- name: Build Eclipse plugin
- run:
- mvn --no-transfer-progress -pl 'eclipse_plugin' verify gpg:sign -DskipTests=true -Dgpg.passphrase="${{ secrets.GPG_PASSPHRASE }}"
+ run: mvn --no-transfer-progress -pl 'eclipse_plugin' verify gpg:sign -DskipTests=true -Dgpg.passphrase="${{ secrets.GPG_PASSPHRASE }}"
- name: Push tag
run: |
git push origin "v${{ github.event.inputs.version }}"
- - name: Add Jars to Release Entry
+ - name: Add Artifacts to Release Entry
uses: softprops/action-gh-release@v0.1.14
with:
draft: true
@@ -67,3 +79,34 @@ jobs:
files: |
core/target/google-java-format-*.jar
eclipse_plugin/target/google-java-format-eclipse-plugin-*.jar
+
+ build-native-image:
+ name: "Build GraalVM native-image on ${{ matrix.os }}"
+ runs-on: ${{ matrix.os }}
+ needs: build-maven-jars
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ env:
+ SUFFIX: ${{fromJson('{"ubuntu-latest":"linux-x86-64", "macos-latest":"darwin-arm64", "windows-latest":"windows-x86-64"}')[matrix.os]}}
+ EXTENSION: ${{ matrix.os == 'windows-latest' && '.exe' || '' }}
+ steps:
+ - name: "Check out repository"
+ uses: actions/checkout@v4
+ - name: "Set up GraalVM"
+ uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: "21"
+ distribution: "graalvm-community"
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ native-image-job-reports: "true"
+ cache: "maven"
+ - name: "Native"
+ run: mvn -Pnative -DskipTests package -pl core -am
+ - name: "Move outputs"
+ run: cp core/target/google-java-format${{ env.EXTENSION }} google-java-format_${{ env.SUFFIX }}${{ env.EXTENSION }}
+ - name: "Upload native-image"
+ env:
+ GH_TOKEN: ${{ github.token }}
+ GH_REPO: ${{ github.repository }}
+ run: gh release upload "v${{ github.event.inputs.version }}" "google-java-format_${{ env.SUFFIX }}${{ env.EXTENSION }}"
diff --git a/README.md b/README.md
index 4fe0e2126..38fdac5e8 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
## Using the formatter
-### from the command-line
+### From the command-line
[Download the formatter](https://github.com/google/google-java-format/releases)
and run it with:
@@ -78,6 +78,8 @@ Implementation`.
### Third-party integrations
+* Visual Studio Code
+ * [google-java-format-for-vs-code](https://marketplace.visualstudio.com/items?itemName=JoseVSeb.google-java-format-for-vs-code)
* Gradle plugins
* [spotless](https://github.com/diffplug/spotless/tree/main/plugin-gradle#google-java-format)
* [sherter/google-java-format-gradle-plugin](https://github.com/sherter/google-java-format-gradle-plugin)
diff --git a/core/pom.xml b/core/pom.xml
index d1363fed1..d0d803a81 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -22,7 +22,7 @@
com.google.googlejavaformat
google-java-format-parent
- HEAD-SNAPSHOT
+ 1.20.0
google-java-format
@@ -282,13 +282,13 @@
org.graalvm.buildtools
native-maven-plugin
- 0.9.13
+ 0.10.0
true
build-native
- build
+ compile-no-fork
package
@@ -306,9 +306,20 @@
${project.build.directory}/${project.artifactId}-${project.version}-all-deps.jar
+ -H:+UnlockExperimentalVMOptions
-H:IncludeResourceBundles=com.sun.tools.javac.resources.compiler
+ -H:IncludeResourceBundles=com.sun.tools.javac.resources.javac
--no-fallback
--initialize-at-build-time=com.sun.tools.javac.file.Locations
+ -H:+ReportExceptionStackTraces
+ -H:-UseContainerSupport
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+ -march=compatibility
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 8914c8ea2..1123ac01c 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
@@ -368,16 +368,16 @@ public Void scan(Tree tree, Void unused) {
@Override
public Void visitCompilationUnit(CompilationUnitTree node, Void unused) {
- boolean first = true;
+ boolean afterFirstToken = false;
if (node.getPackageName() != null) {
markForPartialFormat();
visitPackage(node.getPackageName(), node.getPackageAnnotations());
builder.forcedBreak();
- first = false;
+ afterFirstToken = true;
}
dropEmptyDeclarations();
if (!node.getImports().isEmpty()) {
- if (!first) {
+ if (afterFirstToken) {
builder.blankLineWanted(BlankLineWanted.YES);
}
for (ImportTree importDeclaration : node.getImports()) {
@@ -386,7 +386,7 @@ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) {
scan(importDeclaration, null);
builder.forcedBreak();
}
- first = false;
+ afterFirstToken = true;
}
dropEmptyDeclarations();
for (Tree type : node.getTypeDecls()) {
@@ -395,22 +395,22 @@ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) {
// TODO(cushon): remove this if https://bugs.openjdk.java.net/browse/JDK-8027682 is fixed
continue;
}
- if (!first) {
+ if (afterFirstToken) {
builder.blankLineWanted(BlankLineWanted.YES);
}
markForPartialFormat();
scan(type, null);
builder.forcedBreak();
- first = false;
+ afterFirstToken = true;
dropEmptyDeclarations();
}
- handleModule(first, node);
+ handleModule(afterFirstToken, node);
// set a partial format marker at EOF to make sure we can format the entire file
markForPartialFormat();
return null;
}
- protected void handleModule(boolean first, CompilationUnitTree node) {}
+ protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) {}
/** Skips over extra semi-colons at the top-level, or in a class member declaration lists. */
protected void dropEmptyDeclarations() {
@@ -514,9 +514,9 @@ public boolean visitArrayInitializer(List extends ExpressionTree> expressions)
builder.open(plusTwo);
token("{");
builder.forcedBreak();
- boolean first = true;
+ boolean afterFirstToken = false;
for (Iterable extends ExpressionTree> row : Iterables.partition(expressions, cols)) {
- if (!first) {
+ if (afterFirstToken) {
builder.forcedBreak();
}
builder.open(row.iterator().next().getKind() == NEW_ARRAY || cols == 1 ? ZERO : plusFour);
@@ -531,7 +531,7 @@ public boolean visitArrayInitializer(List extends ExpressionTree> expressions)
}
builder.guessToken(",");
builder.close();
- first = false;
+ afterFirstToken = true;
}
builder.breakOp(minusTwo);
builder.close();
@@ -562,15 +562,15 @@ public boolean visitArrayInitializer(List extends ExpressionTree> expressions)
if (allowFilledElementsOnOwnLine) {
builder.open(ZERO);
}
- boolean first = true;
+ boolean afterFirstToken = false;
FillMode fillMode = shortItems ? FillMode.INDEPENDENT : FillMode.UNIFIED;
for (ExpressionTree expression : expressions) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(fillMode, " ", ZERO);
}
scan(expression, null);
- first = false;
+ afterFirstToken = true;
}
builder.guessToken(",");
if (allowFilledElementsOnOwnLine) {
@@ -842,14 +842,14 @@ public boolean visitEnumDeclaration(ClassTree node) {
token("implements");
builder.breakOp(" ");
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree superInterfaceType : node.getImplementsClause()) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakToFill(" ");
}
scan(superInterfaceType, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
builder.close();
@@ -893,16 +893,16 @@ public boolean visitEnumDeclaration(ClassTree node) {
builder.blankLineWanted(BlankLineWanted.NO);
builder.forcedBreak();
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
for (VariableTree enumConstant : enumConstants) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.forcedBreak();
builder.blankLineWanted(BlankLineWanted.PRESERVE);
}
markForPartialFormat();
visitEnumConstantDeclaration(enumConstant);
- first = false;
+ afterFirstToken = true;
}
if (builder.peekToken().orElse("").equals(",")) {
token(",");
@@ -984,18 +984,19 @@ void visitVariables(
Optional.ofNullable(fragment.getInitializer()),
Optional.of(";"),
/* receiverExpression= */ Optional.empty(),
- Optional.ofNullable(variableFragmentDims(true, 0, fragment.getType())));
+ Optional.ofNullable(variableFragmentDims(false, 0, fragment.getType())));
} else {
declareMany(fragments, annotationDirection);
}
}
- private static TypeWithDims variableFragmentDims(boolean first, int leadingDims, Tree type) {
+ private static TypeWithDims variableFragmentDims(
+ boolean afterFirstToken, int leadingDims, Tree type) {
if (type == null) {
return null;
}
- if (first) {
+ if (!afterFirstToken) {
return DimensionHelpers.extractDims(type, SortedDims.YES);
}
TypeWithDims dims = DimensionHelpers.extractDims(type, SortedDims.NO);
@@ -1022,15 +1023,15 @@ public Void visitForLoop(ForLoopTree node, Void unused) {
visitVariables(
variableFragments(it, it.next()), DeclarationKind.NONE, Direction.HORIZONTAL);
} else {
- boolean first = true;
+ boolean afterFirstToken = false;
builder.open(ZERO);
for (StatementTree t : node.getInitializer()) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
scan(((ExpressionStatementTree) t).getExpression(), null);
- first = false;
+ afterFirstToken = true;
}
token(";");
builder.close();
@@ -1087,11 +1088,11 @@ public Void visitIf(IfTree node, Void unused) {
}
}
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
boolean followingBlock = false;
int expressionsN = expressions.size();
for (int i = 0; i < expressionsN; i++) {
- if (!first) {
+ if (afterFirstToken) {
if (followingBlock) {
builder.space();
} else {
@@ -1115,7 +1116,7 @@ public Void visitIf(IfTree node, Void unused) {
AllowLeadingBlankLine.YES,
AllowTrailingBlankLine.valueOf(trailingClauses));
followingBlock = statements.get(i).getKind() == BLOCK;
- first = false;
+ afterFirstToken = true;
}
if (node.getElseStatement() != null) {
if (followingBlock) {
@@ -1208,15 +1209,15 @@ public Void visitInstanceOf(InstanceOfTree node, Void unused) {
public Void visitIntersectionType(IntersectionTypeTree node, Void unused) {
sync(node);
builder.open(plusFour);
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree type : node.getBounds()) {
- if (!first) {
+ if (afterFirstToken) {
builder.breakToFill(" ");
token("&");
builder.space();
}
scan(type, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
return null;
@@ -1243,9 +1244,9 @@ public Void visitLambdaExpression(LambdaExpressionTree node, Void unused) {
if (parens) {
token("(");
}
- boolean first = true;
+ boolean afterFirstToken = false;
for (VariableTree parameter : node.getParameters()) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
@@ -1253,7 +1254,7 @@ public Void visitLambdaExpression(LambdaExpressionTree node, Void unused) {
ImmutableList.of(parameter),
DeclarationKind.NONE,
fieldAnnotationDirection(parameter.getModifiers()));
- first = false;
+ afterFirstToken = true;
}
if (parens) {
token(")");
@@ -1295,14 +1296,14 @@ public Void visitAnnotation(AnnotationTree node, Void unused) {
builder.open(plusFour);
token("(");
builder.breakOp();
- boolean first = true;
+ boolean afterFirstToken = false;
// Format the member value pairs one-per-line if any of them are
// initialized with arrays.
boolean hasArrayInitializer =
Iterables.any(node.getArguments(), JavaInputAstVisitor::isArrayValue);
for (ExpressionTree argument : node.getArguments()) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
if (hasArrayInitializer) {
builder.forcedBreak();
@@ -1315,7 +1316,7 @@ public Void visitAnnotation(AnnotationTree node, Void unused) {
} else {
scan(argument, null);
}
- first = false;
+ afterFirstToken = true;
}
token(")");
builder.close();
@@ -1407,6 +1408,14 @@ public Void visitMethod(MethodTree node, Void unused) {
annotations,
Direction.VERTICAL,
/* declarationAnnotationBreak= */ Optional.empty());
+ if (node.getTypeParameters().isEmpty() && node.getReturnType() != null) {
+ // If there are type parameters, we use a heuristic above to format annotations after the
+ // type parameter declarations as type-use annotations. If there are no type parameters,
+ // use the heuristics in visitModifiers for recognizing well known type-use annotations and
+ // formatting them as annotations on the return type.
+ returnTypeAnnotations = typeAnnotations;
+ typeAnnotations = ImmutableList.of();
+ }
Tree baseReturnType = null;
Deque> dims = null;
@@ -1425,44 +1434,46 @@ public Void visitMethod(MethodTree node, Void unused) {
BreakTag breakBeforeType = genSym();
builder.open(ZERO);
{
- boolean first = true;
+ boolean afterFirstToken = false;
if (!typeAnnotations.isEmpty()) {
visitAnnotations(typeAnnotations, BreakOrNot.NO, BreakOrNot.NO);
- first = false;
+ afterFirstToken = true;
}
if (!node.getTypeParameters().isEmpty()) {
- if (!first) {
+ if (afterFirstToken) {
builder.breakToFill(" ");
}
token("<");
typeParametersRest(node.getTypeParameters(), plusFour);
- if (!returnTypeAnnotations.isEmpty()) {
- builder.breakToFill(" ");
- visitAnnotations(returnTypeAnnotations, BreakOrNot.NO, BreakOrNot.NO);
- }
- first = false;
+ afterFirstToken = true;
}
boolean openedNameAndTypeScope = false;
// constructor-like declarations that don't match the name of the enclosing class are
// parsed as method declarations with a null return type
if (baseReturnType != null) {
- if (!first) {
+ if (afterFirstToken) {
builder.breakOp(INDEPENDENT, " ", ZERO, Optional.of(breakBeforeType));
} else {
- first = false;
+ afterFirstToken = true;
}
if (!openedNameAndTypeScope) {
builder.open(make(breakBeforeType, plusFour, ZERO));
openedNameAndTypeScope = true;
}
+ builder.open(ZERO);
+ if (!returnTypeAnnotations.isEmpty()) {
+ visitAnnotations(returnTypeAnnotations, BreakOrNot.NO, BreakOrNot.NO);
+ builder.breakOp(" ");
+ }
scan(baseReturnType, null);
maybeAddDims(dims);
+ builder.close();
}
- if (!first) {
+ if (afterFirstToken) {
builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO, Optional.of(breakBeforeName));
} else {
- first = false;
+ afterFirstToken = true;
}
if (!openedNameAndTypeScope) {
builder.open(ZERO);
@@ -1702,14 +1713,14 @@ public Void visitParameterizedType(ParameterizedTypeTree node, Void unused) {
token("<");
builder.breakOp();
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree typeArgument : node.getTypeArguments()) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
scan(typeArgument, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
builder.close();
@@ -1895,13 +1906,13 @@ protected void visitSwitch(ExpressionTree expression, List extends CaseTree> c
tokenBreakTrailingComment("{", plusTwo);
builder.blankLineWanted(BlankLineWanted.NO);
builder.open(plusTwo);
- boolean first = true;
+ boolean afterFirstToken = false;
for (CaseTree caseTree : cases) {
- if (!first) {
+ if (afterFirstToken) {
builder.blankLineWanted(BlankLineWanted.PRESERVE);
}
scan(caseTree, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
builder.forcedBreak();
@@ -1944,9 +1955,9 @@ public Void visitTry(TryTree node, Void unused) {
if (!node.getResources().isEmpty()) {
token("(");
builder.open(node.getResources().size() > 1 ? plusFour : ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree resource : node.getResources()) {
- if (!first) {
+ if (afterFirstToken) {
builder.forcedBreak();
}
if (resource instanceof VariableTree) {
@@ -1971,7 +1982,7 @@ public Void visitTry(TryTree node, Void unused) {
token(";");
builder.space();
}
- first = false;
+ afterFirstToken = true;
}
if (builder.peekToken().equals(Optional.of(";"))) {
token(";");
@@ -2060,15 +2071,15 @@ public Void visitTypeParameter(TypeParameterTree node, Void unused) {
builder.open(plusFour);
builder.breakOp(" ");
builder.open(plusFour);
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree typeBound : node.getBounds()) {
- if (!first) {
+ if (afterFirstToken) {
builder.breakToFill(" ");
token("&");
builder.space();
}
scan(typeBound, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
builder.close();
@@ -2124,13 +2135,13 @@ protected void visitAnnotations(
if (breakBefore.isYes()) {
builder.breakToFill(" ");
}
- boolean first = true;
+ boolean afterFirstToken = false;
for (AnnotationTree annotation : annotations) {
- if (!first) {
+ if (afterFirstToken) {
builder.breakToFill(" ");
}
scan(annotation, null);
- first = false;
+ afterFirstToken = true;
}
if (breakAfter.isYes()) {
builder.breakToFill(" ");
@@ -2210,17 +2221,17 @@ private void visitStatement(
}
protected void visitStatements(List extends StatementTree> statements) {
- boolean first = true;
+ boolean afterFirstToken = false;
PeekingIterator it = Iterators.peekingIterator(statements.iterator());
dropEmptyDeclarations();
while (it.hasNext()) {
StatementTree tree = it.next();
builder.forcedBreak();
- if (!first) {
+ if (afterFirstToken) {
builder.blankLineWanted(BlankLineWanted.PRESERVE);
}
markForPartialFormat();
- first = false;
+ afterFirstToken = true;
List fragments = variableFragments(it, tree);
if (!fragments.isEmpty()) {
visitVariables(
@@ -2290,17 +2301,17 @@ private ImmutableList visitModifiers(
Deque declarationModifiers =
new ArrayDeque<>(splitModifiers.declarationModifiers());
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
boolean lastWasAnnotation = false;
while (!declarationModifiers.isEmpty() && !declarationModifiers.peekFirst().isModifier()) {
- if (!first) {
+ if (afterFirstToken) {
builder.addAll(
annotationsDirection.isVertical()
? forceBreakList(declarationAnnotationBreak)
: breakList(declarationAnnotationBreak));
}
formatAnnotationOrModifier(declarationModifiers);
- first = false;
+ afterFirstToken = true;
lastWasAnnotation = true;
}
builder.close();
@@ -2317,13 +2328,13 @@ private ImmutableList visitModifiers(
}
builder.open(ZERO);
- first = true;
+ afterFirstToken = false;
while (!declarationModifiers.isEmpty()) {
- if (!first) {
+ if (afterFirstToken) {
builder.addAll(breakFillList(Optional.empty()));
}
formatAnnotationOrModifier(declarationModifiers);
- first = false;
+ afterFirstToken = true;
}
builder.close();
builder.addAll(breakFillList(Optional.empty()));
@@ -2546,14 +2557,14 @@ private void visitUnionType(VariableTree declaration) {
Direction.HORIZONTAL,
/* declarationAnnotationBreak= */ Optional.empty());
List extends Tree> union = type.getTypeAlternatives();
- boolean first = true;
+ boolean afterFirstToken = false;
for (int i = 0; i < union.size() - 1; i++) {
- if (!first) {
+ if (afterFirstToken) {
builder.breakOp(" ");
token("|");
builder.space();
} else {
- first = false;
+ afterFirstToken = true;
}
scan(union.get(i), null);
}
@@ -2602,7 +2613,7 @@ protected void visitFormals(
return;
}
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
if (receiver.isPresent()) {
// TODO(user): Use builders.
declareOne(
@@ -2617,11 +2628,11 @@ protected void visitFormals(
!parameters.isEmpty() ? Optional.of(",") : Optional.empty(),
Optional.of(receiver.get().getNameExpression()),
/* typeWithDims= */ Optional.empty());
- first = false;
+ afterFirstToken = true;
}
for (int i = 0; i < parameters.size(); i++) {
VariableTree parameter = parameters.get(i);
- if (!first) {
+ if (afterFirstToken) {
builder.breakOp(" ");
}
visitToDeclare(
@@ -2631,7 +2642,7 @@ protected void visitFormals(
/* initializer= */ Optional.empty(),
"=",
i < parameters.size() - 1 ? Optional.of(",") : /* a= */ Optional.empty());
- first = false;
+ afterFirstToken = true;
}
builder.close();
}
@@ -2640,14 +2651,14 @@ protected void visitFormals(
private void visitThrowsClause(List extends ExpressionTree> thrownExceptionTypes) {
token("throws");
builder.breakToFill(" ");
- boolean first = true;
+ boolean afterFirstToken = false;
for (ExpressionTree thrownExceptionType : thrownExceptionTypes) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
scan(thrownExceptionType, null);
- first = false;
+ afterFirstToken = true;
}
}
@@ -2711,14 +2722,14 @@ private void visitDirective(
builder.space();
token(separator);
builder.forcedBreak();
- boolean first = true;
+ boolean afterFirstToken = false;
for (ExpressionTree item : items) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.forcedBreak();
}
scan(item, null);
- first = false;
+ afterFirstToken = true;
}
token(";");
builder.close();
@@ -2781,13 +2792,13 @@ private void visitName(Tree node) {
stack.addFirst(((MemberSelectTree) node).getIdentifier());
}
stack.addFirst(((IdentifierTree) node).getName());
- boolean first = true;
+ boolean afterFirstToken = false;
for (Name name : stack) {
- if (!first) {
+ if (afterFirstToken) {
token(".");
}
token(name.toString());
- first = false;
+ afterFirstToken = true;
}
}
@@ -2829,14 +2840,14 @@ protected void typeParametersRest(
builder.open(plusIndent);
builder.breakOp();
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
for (TypeParameterTree typeParameter : typeParameters) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
scan(typeParameter, null);
- first = false;
+ afterFirstToken = true;
}
token(">");
builder.close();
@@ -3228,14 +3239,14 @@ void addTypeArguments(List extends Tree> typeArguments, Indent plusIndent) {
}
token("<");
builder.open(plusIndent);
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree typeArgument : typeArguments) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakToFill(" ");
}
scan(typeArgument, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
token(">");
@@ -3256,11 +3267,11 @@ void addArguments(List extends ExpressionTree> arguments, Indent plusIndent) {
if (arguments.size() % 2 == 0 && argumentsAreTabular(arguments) == 2) {
builder.forcedBreak();
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
for (int i = 0; i < arguments.size() - 1; i += 2) {
ExpressionTree argument0 = arguments.get(i);
ExpressionTree argument1 = arguments.get(i + 1);
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.forcedBreak();
}
@@ -3270,7 +3281,7 @@ void addArguments(List extends ExpressionTree> arguments, Indent plusIndent) {
builder.breakOp(" ");
scan(argument1, null);
builder.close();
- first = false;
+ afterFirstToken = true;
}
builder.close();
} else if (isFormatMethod(arguments)) {
@@ -3294,15 +3305,15 @@ void addArguments(List extends ExpressionTree> arguments, Indent plusIndent) {
private void argList(List extends ExpressionTree> arguments) {
builder.open(ZERO);
- boolean first = true;
+ boolean afterFirstToken = false;
FillMode fillMode = hasOnlyShortItems(arguments) ? FillMode.INDEPENDENT : FillMode.UNIFIED;
for (ExpressionTree argument : arguments) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(fillMode, " ", ZERO);
}
scan(argument, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
}
@@ -3691,12 +3702,13 @@ private void declareMany(List fragments, Direction annotationDirec
int baseDims = dims.size();
maybeAddDims(dims);
baseDims = baseDims - dims.size();
- boolean first = true;
+ boolean afterFirstToken = false;
for (VariableTree fragment : fragments) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
}
- TypeWithDims fragmentDims = variableFragmentDims(first, baseDims, fragment.getType());
+ TypeWithDims fragmentDims =
+ variableFragmentDims(afterFirstToken, baseDims, fragment.getType());
dims = new ArrayDeque<>(fragmentDims.dims);
builder.breakOp(" ");
builder.open(ZERO);
@@ -3713,10 +3725,10 @@ private void declareMany(List fragments, Direction annotationDirec
builder.close();
}
builder.close();
- if (first) {
+ if (!afterFirstToken) {
builder.close();
}
- first = false;
+ afterFirstToken = true;
}
builder.close();
token(";");
@@ -3795,14 +3807,14 @@ private void classDeclarationTypeList(String token, List extends Tree> types)
builder.open(types.size() > 1 ? plusFour : ZERO);
token(token);
builder.space();
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree type : types) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
scan(type, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
index a7b5dbc44..e502f49a1 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
@@ -55,10 +55,10 @@ public Java17InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
}
@Override
- protected void handleModule(boolean first, CompilationUnitTree node) {
+ protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) {
ModuleTree module = node.getModule();
if (module != null) {
- if (!first) {
+ if (afterFirstToken) {
builder.blankLineWanted(BlankLineWanted.YES);
}
markForPartialFormat();
@@ -167,14 +167,14 @@ public void visitRecordDeclaration(ClassTree node) {
builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO);
token("implements");
builder.space();
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree superInterfaceType : node.getImplementsClause()) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
scan(superInterfaceType, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
}
@@ -238,14 +238,14 @@ public Void visitCase(CaseTree node, Void unused) {
token("case", plusTwo);
builder.open(labels.size() > 1 ? plusFour : ZERO);
builder.space();
- boolean first = true;
+ boolean afterFirstToken = false;
for (Tree expression : labels) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
scan(expression, null);
- first = false;
+ afterFirstToken = true;
}
builder.close();
}
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
index 5ef81fad6..2192a32a9 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java
@@ -24,6 +24,8 @@
import com.sun.source.tree.PatternCaseLabelTree;
import com.sun.source.tree.PatternTree;
import com.sun.source.tree.StringTemplateTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.tree.JCTree;
import javax.lang.model.element.Name;
/**
@@ -66,13 +68,13 @@ public Void visitDeconstructionPattern(DeconstructionPatternTree node, Void unus
builder.open(plusFour);
token("(");
builder.breakOp();
- boolean first = true;
+ boolean afterFirstToken = false;
for (PatternTree pattern : node.getNestedPatterns()) {
- if (!first) {
+ if (afterFirstToken) {
token(",");
builder.breakOp(" ");
}
- first = false;
+ afterFirstToken = true;
scan(pattern, null);
}
builder.close();
@@ -107,4 +109,20 @@ protected void variableName(Name name) {
visit(name);
}
}
+
+ @Override
+ public Void scan(Tree tree, Void unused) {
+ // Pre-visit AST for preview features, since com.sun.source.tree.AnyPattern can't be
+ // accessed directly without --enable-preview.
+ if (tree instanceof JCTree.JCAnyPattern) {
+ visitJcAnyPattern((JCTree.JCAnyPattern) tree);
+ return null;
+ } else {
+ return super.scan(tree, unused);
+ }
+ }
+
+ private void visitJcAnyPattern(JCTree.JCAnyPattern unused) {
+ token("_");
+ }
}
diff --git a/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java b/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java
index 1a4ed09b4..08fbbbab3 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java
@@ -15,7 +15,6 @@
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 static org.junit.Assert.fail;
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 5fb356750..15a6e75f9 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
@@ -62,7 +62,8 @@ public class FormatterIntegrationTest {
"Unnamed",
"I981",
"StringTemplate",
- "I1020")
+ "I1020",
+ "I1037")
.build();
@Parameters(name = "{index}: {0}")
@@ -104,10 +105,9 @@ public static Iterable