From 4340416bbee832e690d9384c00895b677d6f1b2b Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Fri, 4 Oct 2024 11:21:12 -0700 Subject: [PATCH 01/19] Update the IntelliJ plugin to 1.24.0. PiperOrigin-RevId: 682389166 --- idea_plugin/build.gradle.kts | 4 ++-- idea_plugin/src/main/resources/META-INF/plugin.xml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/idea_plugin/build.gradle.kts b/idea_plugin/build.gradle.kts index c7d1d4ba..d5eb5993 100644 --- a/idea_plugin/build.gradle.kts +++ b/idea_plugin/build.gradle.kts @@ -17,7 +17,7 @@ import org.jetbrains.intellij.platform.gradle.TestFrameworkType // https://github.com/JetBrains/intellij-platform-gradle-plugin/releases plugins { - id("org.jetbrains.intellij.platform") version "2.0.1" + id("org.jetbrains.intellij.platform") version "2.1.0" } repositories { @@ -29,7 +29,7 @@ repositories { } // https://github.com/google/google-java-format/releases -val googleJavaFormatVersion = "1.23.0" +val googleJavaFormatVersion = "1.24.0" java { sourceCompatibility = JavaVersion.VERSION_17 diff --git a/idea_plugin/src/main/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml index 8d757453..c09257ba 100644 --- a/idea_plugin/src/main/resources/META-INF/plugin.xml +++ b/idea_plugin/src/main/resources/META-INF/plugin.xml @@ -35,6 +35,8 @@ ]]> +
1.24.0.0
+
Updated to use google-java-format 1.24.0.
1.23.0.0
Updated to use google-java-format 1.23.0.
Fix crashes in IntelliJ 2024.2 (Thanks, @nrayburn-tech!)
From 6c436705fa75e7c8c16a94140222af4f44f3707e Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 4 Oct 2024 16:41:39 -0700 Subject: [PATCH 02/19] Remove JDK 11 from CI also use 21 for snapshot builds, see https://github.com/google/google-java-format/pull/1015 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c623efe3..e64353d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - java: [21, 17, 11] + java: [21, 17] experimental: [false] include: # Only test on MacOS and Windows with a single recent JDK to avoid a @@ -114,10 +114,10 @@ jobs: steps: - name: "Check out repository" uses: actions/checkout@v4 - - name: "Set up JDK 17" + - name: "Set up JDK 21" uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: "zulu" cache: "maven" server-id: sonatype-nexus-snapshots From a3737d91d9446acd4c56fb46e178423d3cf9b66a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 4 Oct 2024 17:46:43 -0700 Subject: [PATCH 03/19] Update google-java-format to build with Java 17 https://github.com/google/google-java-format/issues/1159 PiperOrigin-RevId: 682509011 --- core/pom.xml | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index c3753a15..5b6bbb4d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -93,7 +93,7 @@ maven-javadoc-plugin - 11 + 17 UTF-8 UTF-8 UTF-8 @@ -215,41 +215,14 @@ org.apache.maven.plugins maven-compiler-plugin - 11 - 11 + 17 + 17 - - jdk11 - - [11,17) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - **/Java17InputAstVisitor.java - **/Java21InputAstVisitor.java - - - - - maven-javadoc-plugin - - com.google.googlejavaformat.java.java17 - com.google.googlejavaformat.java.java21 - - - - - jdk17 From 098431221007a0fce1b0d213ded10948087332a4 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 4 Oct 2024 18:07:08 -0700 Subject: [PATCH 04/19] Update Error Prone version to 2.32.0 Startblock: * unknown commit is submitted PiperOrigin-RevId: 682513849 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 96e613a0..ebea1ed3 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ 32.1.3-jre 1.4.0 1.0.0 - 2.28.0 + 2.32.0 1.9 1.0.1 3.6.3 From 6586afee9abcb42f79b64314074f299f036099bd Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 18 Oct 2024 17:47:58 -0700 Subject: [PATCH 05/19] Update Error Prone version to 2.34.0 And pass `--should-stop=ifError=FLOW`, see https://github.com/google/error-prone/releases/tag/v2.34.0 PiperOrigin-RevId: 687476556 --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ebea1ed3..c3134022 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ 32.1.3-jre 1.4.0 1.0.0 - 2.32.0 + 2.34.0 1.9 1.0.1 3.6.3 @@ -196,6 +196,7 @@ -XDcompilePolicy=simple + --should-stop=ifError=FLOW -Xplugin:ErrorProne --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED From 00cce7b893508e986d91c8f00947948bdaefc76a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 28 Oct 2024 08:19:16 -0700 Subject: [PATCH 06/19] Place the trailing `"""` of a text block to appear on its own line PiperOrigin-RevId: 690618067 --- .../googlejavaformat/java/StringWrapper.java | 20 +++++++++++++------ .../java/StringWrapperTest.java | 3 ++- .../java/testdata/RSLs.output | 3 ++- 3 files changed, 18 insertions(+), 8 deletions(-) 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 6814054a..c0b4dc1d 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -65,6 +65,9 @@ /** Wraps string literals that exceed the column limit. */ public final class StringWrapper { + + public static final String TEXT_BLOCK_DELIMITER = "\"\"\""; + /** Reflows long string literals in the given Java source code. */ public static String wrap(String input, Formatter formatter) throws FormatterException { return StringWrapper.wrap(Formatter.MAX_LINE_LENGTH, input, formatter); @@ -162,7 +165,7 @@ public Void visitLiteral(LiteralTree literalTree, Void aVoid) { return null; } int pos = getStartPosition(literalTree); - if (input.substring(pos, min(input.length(), pos + 3)).equals("\"\"\"")) { + if (input.substring(pos, min(input.length(), pos + 3)).equals(TEXT_BLOCK_DELIMITER)) { textBlocks.add(literalTree); return null; } @@ -206,7 +209,7 @@ private void indentTextBlocks( ? "" : " ".repeat(startColumn - 1); - StringBuilder output = new StringBuilder("\"\"\""); + StringBuilder output = new StringBuilder(TEXT_BLOCK_DELIMITER); for (int i = 0; i < lines.size(); i++) { String line = lines.get(i); String trimmed = line.stripLeading().stripTrailing(); @@ -215,11 +218,16 @@ private void indentTextBlocks( // Don't add incidental leading whitespace to empty lines output.append(prefix); } - if (i == lines.size() - 1 && trimmed.equals("\"\"\"")) { - // If the trailing line is just """, indenting is more than the prefix of incidental + if (i == lines.size() - 1) { + String withoutDelimiter = + trimmed.substring(0, trimmed.length() - TEXT_BLOCK_DELIMITER.length()); + if (!withoutDelimiter.isEmpty()) { + output.append(withoutDelimiter).append('\\').append(separator).append(prefix); + } + // If the trailing line is just """, indenting it more than the prefix of incidental // whitespace has no effect, and results in a javac text-blocks warning that 'trailing // white space will be removed'. - output.append("\"\"\""); + output.append(TEXT_BLOCK_DELIMITER); } else { output.append(line); } @@ -482,7 +490,7 @@ private static boolean needWrapping(int columnLimit, String input) { Iterator it = Newlines.lineIterator(input); while (it.hasNext()) { String line = it.next(); - if (line.length() > columnLimit || line.contains("\"\"\"")) { + if (line.length() > columnLimit || line.contains(TEXT_BLOCK_DELIMITER)) { return true; } } 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 fd176ed4..7854126c 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java @@ -65,7 +65,8 @@ public void textBlock() throws Exception { " String str =", " \"\"\"", "{\"sourceEndpoint\":\"ri.something.1-1.object-internal.1\",\"targetEndpoint" - + "\":\"ri.something.1-1.object-internal.2\",\"typeId\":\"typeId\"}\"\"\";", + + "\":\"ri.something.1-1.object-internal.2\",\"typeId\":\"typeId\"}\\", + "\"\"\";", " myString = str;", " }", "}"); diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output index 5ca1fb8c..5f59bc5e 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output @@ -23,7 +23,8 @@ class RSLs { """; String f = """ - ipsum"""; + ipsum\ + """; String g = """ lorem\ From 666fe33f770d07b1e79b2b6dd468735cead652a1 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 8 Nov 2024 07:59:30 -0800 Subject: [PATCH 07/19] Add an integration test for comments before text blocks PiperOrigin-RevId: 694499404 --- .../googlejavaformat/java/testdata/B377585941.input | 8 ++++++++ .../googlejavaformat/java/testdata/B377585941.output | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.input create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.input new file mode 100644 index 00000000..bd3107be --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.input @@ -0,0 +1,8 @@ +class T { + { + f( + /* foo */ """ + hello + """); + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output new file mode 100644 index 00000000..1dfc4950 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output @@ -0,0 +1,8 @@ +class T { + { + f( + /* foo */ """ + hello + """); + } +} From 98c6bbd0534620901cf8d13cf0c843a4c888e947 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 8 Nov 2024 09:38:27 -0800 Subject: [PATCH 08/19] If a text block is outdented, outdent the opening `"""` too PiperOrigin-RevId: 694533649 --- .../googlejavaformat/java/JavaInputAstVisitor.java | 10 ++++++++++ .../google/googlejavaformat/java/StringWrapper.java | 6 +++--- .../googlejavaformat/java/StringWrapperTest.java | 2 +- .../googlejavaformat/java/testdata/B377585941.output | 4 ++-- .../google/googlejavaformat/java/testdata/RSLs.input | 7 +++++++ .../google/googlejavaformat/java/testdata/RSLs.output | 11 +++++++++-- 6 files changed, 32 insertions(+), 8 deletions(-) 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 01f9a3e0..af981011 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -70,6 +70,7 @@ import com.google.googlejavaformat.FormattingError; import com.google.googlejavaformat.Indent; import com.google.googlejavaformat.Input; +import com.google.googlejavaformat.Newlines; import com.google.googlejavaformat.Op; import com.google.googlejavaformat.OpenOp; import com.google.googlejavaformat.OpsBuilder; @@ -1667,6 +1668,15 @@ public Void visitMemberSelect(MemberSelectTree node, Void unused) { public Void visitLiteral(LiteralTree node, Void unused) { sync(node); String sourceForNode = getSourceForNode(node, getCurrentPath()); + if (sourceForNode.endsWith("\"\"\"") + && (Newlines.hasNewlineAt(sourceForNode, sourceForNode.length() - 4) != -1)) { + // If the closing delimiter of a text block starts at the margin, outdent the opening + // delimiter as well by adding a break with negative indentation. Outdenting for text blocks + // with wide contents is also handled by StringWrapper, but this means the behaviour for + // the opening delimiter is consistent if string wrapping is disabled, and also effectively + // preserves user choice about which text blocks stay de-indented. + builder.breakOp(Indent.Const.make(Integer.MIN_VALUE / indentMultiplier, indentMultiplier)); + } if (isUnaryMinusLiteral(sourceForNode)) { token("-"); sourceForNode = sourceForNode.substring(1).trim(); 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 c0b4dc1d..81ea800d 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -190,9 +190,10 @@ public Void visitLiteral(LiteralTree literalTree, Void aVoid) { private void indentTextBlocks( TreeRangeMap replacements, List textBlocks) { for (Tree tree : textBlocks) { - int startPosition = getStartPosition(tree); + int startPosition = lineMap.getStartPosition(lineMap.getLineNumber(getStartPosition(tree))); int endPosition = getEndPosition(unit, tree); String text = input.substring(startPosition, endPosition); + int startColumn = CharMatcher.whitespace().negate().indexIn(text) + 1; // Find the source code of the text block with incidental whitespace removed. // The first line of the text block is always """, and it does not affect incidental @@ -203,13 +204,12 @@ private void indentTextBlocks( int deindent = initialLines.get(1).stripTrailing().length() - lines.get(0).stripTrailing().length(); - int startColumn = lineMap.getColumnNumber(startPosition); String prefix = (deindent == 0 || lines.stream().anyMatch(x -> x.length() + startColumn > columnLimit)) ? "" : " ".repeat(startColumn - 1); - StringBuilder output = new StringBuilder(TEXT_BLOCK_DELIMITER); + StringBuilder output = new StringBuilder(prefix).append(initialLines.get(0).stripLeading()); for (int i = 0; i < lines.size(); i++) { String line = lines.get(i); String trimmed = line.stripLeading().stripTrailing(); 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 7854126c..bc1a9338 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java @@ -63,7 +63,7 @@ public void textBlock() throws Exception { " private String myString;", " private ReproBug() {", " String str =", - " \"\"\"", + "\"\"\"", "{\"sourceEndpoint\":\"ri.something.1-1.object-internal.1\",\"targetEndpoint" + "\":\"ri.something.1-1.object-internal.2\",\"typeId\":\"typeId\"}\\", "\"\"\";", diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output index 1dfc4950..bd3107be 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output @@ -2,7 +2,7 @@ class T { { f( /* foo */ """ - hello - """); + hello + """); } } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input index 22aa8f2b..e557ea2c 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input @@ -50,5 +50,12 @@ ipsum hello %s """ .formatted("world"); + f( + /* foo= */ """ + foo + """, + /* bar= */ """ + bar + """); } } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output index 5f59bc5e..7d291b15 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output @@ -42,13 +42,13 @@ class RSLs { ipsum """; String j = - """ +""" lorem one long incredibly unbroken sentence moving from topic to topic so that no one had a chance to interrupt ipsum """; String k = - """ +""" lorem ipsum """; @@ -65,5 +65,12 @@ ipsum hello %s """ .formatted("world"); + f( + /* foo= */ """ + foo + """, + /* bar= */ """ + bar + """); } } From 81e9e1c0293494a80dd15681f2500af73d640db7 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 8 Nov 2024 10:20:30 -0800 Subject: [PATCH 09/19] Always break between leading comments and opening delimiters of text blocks PiperOrigin-RevId: 694550141 --- core/src/main/java/com/google/googlejavaformat/Input.java | 5 +++++ .../main/java/com/google/googlejavaformat/OpsBuilder.java | 2 +- .../java/com/google/googlejavaformat/java/JavaInput.java | 5 +++++ .../google/googlejavaformat/java/testdata/B377585941.output | 3 ++- .../com/google/googlejavaformat/java/testdata/RSLs.output | 6 ++++-- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/Input.java b/core/src/main/java/com/google/googlejavaformat/Input.java index 66a39219..d059111d 100644 --- a/core/src/main/java/com/google/googlejavaformat/Input.java +++ b/core/src/main/java/com/google/googlejavaformat/Input.java @@ -71,6 +71,11 @@ public interface Tok { /** Is the {@code Tok} a comment? */ boolean isComment(); + + /** Is the {@code Tok} a text block? */ + default boolean isTextBlock() { + return false; + } } /** A {@code Token} is a language-level token. */ diff --git a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java index a45e83b9..008be11f 100644 --- a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java +++ b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java @@ -534,7 +534,7 @@ public final ImmutableList build() { space = tokBefore.isSlashStarComment(); newlines = 0; lastWasComment = true; - if (tokBefore.isJavadocComment()) { + if (tokBefore.isJavadocComment() || token.getTok().isTextBlock()) { tokOps.put(j, Doc.Break.makeForced()); } allowBlankAfterLastComment = 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 01c61777..6be39ab4 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java @@ -166,6 +166,11 @@ public boolean isComment() { return isSlashSlashComment() || isSlashStarComment(); } + @Override + public boolean isTextBlock() { + return originalText.startsWith("\"\"\""); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output index bd3107be..7f57dfbe 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output @@ -1,7 +1,8 @@ class T { { f( - /* foo */ """ + /* foo */ + """ hello """); } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output index 7d291b15..cfe77c93 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output @@ -66,10 +66,12 @@ ipsum """ .formatted("world"); f( - /* foo= */ """ + /* foo= */ + """ foo """, - /* bar= */ """ + /* bar= */ + """ bar """); } From d4b3f0dd7dafcb863141570f2ac4cb2a9cee0a7b Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 08:22:56 -0800 Subject: [PATCH 10/19] Fix an off-by-one bug with column limit handling for text blocks PiperOrigin-RevId: 695348215 --- .../googlejavaformat/java/StringWrapper.java | 3 ++- .../java/testdata/B361077825.input | 15 +++++++++++++++ .../java/testdata/B361077825.output | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.input create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output 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 81ea800d..ab4093a1 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -205,7 +205,8 @@ private void indentTextBlocks( initialLines.get(1).stripTrailing().length() - lines.get(0).stripTrailing().length(); String prefix = - (deindent == 0 || lines.stream().anyMatch(x -> x.length() + startColumn > columnLimit)) + (deindent == 0 + || lines.stream().anyMatch(x -> x.length() + startColumn - 1 > columnLimit)) ? "" : " ".repeat(startColumn - 1); diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.input new file mode 100644 index 00000000..c93942a5 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.input @@ -0,0 +1,15 @@ +class T { + String a = + """ + # No implicit input file, because they can only be created outside a symbolic macro, + """; + + String b = + """ + # No implicit input file, because they can only be created outside a symbolic macro, + """; + String c = + """ + # No implicit input file, because they can only be created outside a symbolic macro, + """; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output new file mode 100644 index 00000000..b407bfe2 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output @@ -0,0 +1,15 @@ +class T { + String a = + """ + # No implicit input file, because they can only be created outside a symbolic macro, + """; + + String b = + """ + # No implicit input file, because they can only be created outside a symbolic macro, + """; + String c = +""" + # No implicit input file, because they can only be created outside a symbolic macro, +"""; +} From fc31690af3b057b0f9eeb3d78d8687c0831e0a1b Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 08:46:09 -0800 Subject: [PATCH 11/19] More text block test cases PiperOrigin-RevId: 695356983 --- .../googlejavaformat/java/testdata/RSLs.input | 27 +++++++++++++++++ .../java/testdata/RSLs.output | 29 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input index e557ea2c..6eaa2855 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.input @@ -57,5 +57,32 @@ ipsum /* bar= */ """ bar """); + """ + hello + """.codePoints().forEach(System.err::println); + String s = + """ + foo + """ + + """ + bar + """; + String t = + """ +foo +""" + + """ +bar +"""; + String u = + stringVariableOne + + + """ + ... + """ + stringVariableTwo + + + """ + ... + """; } } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output index cfe77c93..9993f5aa 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output @@ -74,5 +74,34 @@ ipsum """ bar """); + """ + hello + """ + .codePoints() + .forEach(System.err::println); + String s = + """ + foo + """ + + """ + bar + """; + String t = +""" +foo +""" + + +""" +bar +"""; + String u = + stringVariableOne + + """ + ... + """ + + stringVariableTwo + + """ + ... + """; } } From 02b6ad1537afb8b6f59c49ce822394a258b2b810 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 09:03:49 -0800 Subject: [PATCH 12/19] Back out text block special cases PiperOrigin-RevId: 695363436 --- .../java/com/google/googlejavaformat/Input.java | 5 ----- .../com/google/googlejavaformat/OpsBuilder.java | 2 +- .../google/googlejavaformat/java/JavaInput.java | 5 ----- .../java/JavaInputAstVisitor.java | 10 ---------- .../googlejavaformat/java/StringWrapper.java | 11 ++++++++--- .../googlejavaformat/java/StringWrapperTest.java | 2 +- .../java/testdata/B361077825.output | 2 +- .../java/testdata/B377585941.output | 3 +-- .../googlejavaformat/java/testdata/RSLs.output | 15 ++++++--------- 9 files changed, 18 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/Input.java b/core/src/main/java/com/google/googlejavaformat/Input.java index d059111d..66a39219 100644 --- a/core/src/main/java/com/google/googlejavaformat/Input.java +++ b/core/src/main/java/com/google/googlejavaformat/Input.java @@ -71,11 +71,6 @@ public interface Tok { /** Is the {@code Tok} a comment? */ boolean isComment(); - - /** Is the {@code Tok} a text block? */ - default boolean isTextBlock() { - return false; - } } /** A {@code Token} is a language-level token. */ diff --git a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java index 008be11f..a45e83b9 100644 --- a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java +++ b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java @@ -534,7 +534,7 @@ public final ImmutableList build() { space = tokBefore.isSlashStarComment(); newlines = 0; lastWasComment = true; - if (tokBefore.isJavadocComment() || token.getTok().isTextBlock()) { + if (tokBefore.isJavadocComment()) { tokOps.put(j, Doc.Break.makeForced()); } allowBlankAfterLastComment = 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 6be39ab4..01c61777 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java @@ -166,11 +166,6 @@ public boolean isComment() { return isSlashSlashComment() || isSlashStarComment(); } - @Override - public boolean isTextBlock() { - return originalText.startsWith("\"\"\""); - } - @Override public String toString() { return MoreObjects.toStringHelper(this) 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 af981011..01f9a3e0 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -70,7 +70,6 @@ import com.google.googlejavaformat.FormattingError; import com.google.googlejavaformat.Indent; import com.google.googlejavaformat.Input; -import com.google.googlejavaformat.Newlines; import com.google.googlejavaformat.Op; import com.google.googlejavaformat.OpenOp; import com.google.googlejavaformat.OpsBuilder; @@ -1668,15 +1667,6 @@ public Void visitMemberSelect(MemberSelectTree node, Void unused) { public Void visitLiteral(LiteralTree node, Void unused) { sync(node); String sourceForNode = getSourceForNode(node, getCurrentPath()); - if (sourceForNode.endsWith("\"\"\"") - && (Newlines.hasNewlineAt(sourceForNode, sourceForNode.length() - 4) != -1)) { - // If the closing delimiter of a text block starts at the margin, outdent the opening - // delimiter as well by adding a break with negative indentation. Outdenting for text blocks - // with wide contents is also handled by StringWrapper, but this means the behaviour for - // the opening delimiter is consistent if string wrapping is disabled, and also effectively - // preserves user choice about which text blocks stay de-indented. - builder.breakOp(Indent.Const.make(Integer.MIN_VALUE / indentMultiplier, indentMultiplier)); - } if (isUnaryMinusLiteral(sourceForNode)) { token("-"); sourceForNode = sourceForNode.substring(1).trim(); 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 ab4093a1..42099d6b 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -190,10 +190,15 @@ public Void visitLiteral(LiteralTree literalTree, Void aVoid) { private void indentTextBlocks( TreeRangeMap replacements, List textBlocks) { for (Tree tree : textBlocks) { - int startPosition = lineMap.getStartPosition(lineMap.getLineNumber(getStartPosition(tree))); + int startPosition = getStartPosition(tree); int endPosition = getEndPosition(unit, tree); String text = input.substring(startPosition, endPosition); - int startColumn = CharMatcher.whitespace().negate().indexIn(text) + 1; + int lineStartPosition = lineMap.getStartPosition(lineMap.getLineNumber(startPosition)); + int startColumn = + CharMatcher.whitespace() + .negate() + .indexIn(input.substring(lineStartPosition, endPosition)) + + 1; // Find the source code of the text block with incidental whitespace removed. // The first line of the text block is always """, and it does not affect incidental @@ -210,7 +215,7 @@ private void indentTextBlocks( ? "" : " ".repeat(startColumn - 1); - StringBuilder output = new StringBuilder(prefix).append(initialLines.get(0).stripLeading()); + StringBuilder output = new StringBuilder(initialLines.get(0).stripLeading()); for (int i = 0; i < lines.size(); i++) { String line = lines.get(i); String trimmed = line.stripLeading().stripTrailing(); 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 bc1a9338..7854126c 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java @@ -63,7 +63,7 @@ public void textBlock() throws Exception { " private String myString;", " private ReproBug() {", " String str =", - "\"\"\"", + " \"\"\"", "{\"sourceEndpoint\":\"ri.something.1-1.object-internal.1\",\"targetEndpoint" + "\":\"ri.something.1-1.object-internal.2\",\"typeId\":\"typeId\"}\\", "\"\"\";", diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output index b407bfe2..62344bd5 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B361077825.output @@ -9,7 +9,7 @@ class T { # No implicit input file, because they can only be created outside a symbolic macro, """; String c = -""" + """ # No implicit input file, because they can only be created outside a symbolic macro, """; } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output index 7f57dfbe..bd3107be 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B377585941.output @@ -1,8 +1,7 @@ class T { { f( - /* foo */ - """ + /* foo */ """ hello """); } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output index 9993f5aa..6e9a3ae5 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/RSLs.output @@ -42,13 +42,13 @@ class RSLs { ipsum """; String j = -""" + """ lorem one long incredibly unbroken sentence moving from topic to topic so that no one had a chance to interrupt ipsum """; String k = -""" + """ lorem ipsum """; @@ -66,12 +66,10 @@ ipsum """ .formatted("world"); f( - /* foo= */ - """ + /* foo= */ """ foo """, - /* bar= */ - """ + /* bar= */ """ bar """); """ @@ -87,11 +85,10 @@ ipsum bar """; String t = -""" + """ foo """ - + -""" + + """ bar """; String u = From beacc083963eb2da09349cf5db9238085fad40bf Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 09:58:30 -0800 Subject: [PATCH 13/19] Explicitly test that reformatting after formatting and string wrapping is idempotent There's some indirect coverage of this from line ending tests, but they don't exercise string wrapping. PiperOrigin-RevId: 695382884 --- .../java/FormatterIntegrationTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 3e4e175e..ef6bef08 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java @@ -144,6 +144,19 @@ public void format() { } } + @Test + public void idempotent() { + try { + Formatter formatter = new Formatter(); + String formatted = formatter.formatSource(input); + formatted = StringWrapper.wrap(formatted, formatter); + String reformatted = formatter.formatSource(formatted); + assertEquals("bad output for " + name, formatted, reformatted); + } catch (FormatterException e) { + fail(String.format("Formatter crashed on %s: %s", name, e.getMessage())); + } + } + @Test public void idempotentLF() { try { From e35f60eaf3eae8661e1e3251a573a194cf861ada Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 11 Nov 2024 12:56:43 -0800 Subject: [PATCH 14/19] Reformat the formatter To avoid spurious diffs during other development. PiperOrigin-RevId: 695452895 --- .../java/com/google/googlejavaformat/Newlines.java | 2 +- .../java/com/google/googlejavaformat/OpsBuilder.java | 10 +++++----- .../googlejavaformat/java/JavaCommentsHelper.java | 1 - .../com/google/googlejavaformat/java/JavaOutput.java | 2 +- .../googlejavaformat/java/filer/FormattingFiler.java | 4 +++- .../googlejavaformat/java/javadoc/JavadocWriter.java | 1 + .../googlejavaformat/java/CommandLineFlagsTest.java | 4 +--- .../googlejavaformat/java/JavadocFormattingTest.java | 4 ++-- .../googlejavaformat/java/GoogleJavaFormatter.java | 4 +++- .../intellij/GoogleJavaFormatConfigurable.java | 4 +++- 10 files changed, 20 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/Newlines.java b/core/src/main/java/com/google/googlejavaformat/Newlines.java index dbb82d3c..86335981 100644 --- a/core/src/main/java/com/google/googlejavaformat/Newlines.java +++ b/core/src/main/java/com/google/googlejavaformat/Newlines.java @@ -135,7 +135,7 @@ private void advance() { if (idx + 1 < input.length() && input.charAt(idx + 1) == '\n') { idx++; } - // falls through + // falls through case '\n': idx++; curr = idx; diff --git a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java index a45e83b9..7f0fabb3 100644 --- a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java +++ b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java @@ -38,7 +38,7 @@ */ public final class OpsBuilder { - /** @return the actual size of the AST node at position, including comments. */ + /** Returns the actual size of the AST node at position, including comments. */ public int actualSize(int position, int length) { Token startToken = input.getPositionTokenMap().get(position); int start = startToken.getTok().getPosition(); @@ -57,7 +57,7 @@ public int actualSize(int position, int length) { return end - start; } - /** @return the start column of the token at {@code position}, including leading comments. */ + /** Returns the start column of the token at {@code position}, including leading comments. */ public Integer actualStartColumn(int position) { Token startToken = input.getPositionTokenMap().get(position); int start = startToken.getTok().getPosition(); @@ -316,7 +316,7 @@ public final void guessToken(String token) { token, Doc.Token.RealOrImaginary.IMAGINARY, ZERO, - /* breakAndIndentTrailingComment= */ Optional.empty()); + /* breakAndIndentTrailingComment= */ Optional.empty()); } public final void token( @@ -359,7 +359,7 @@ public final void op(String op) { op.substring(i, i + 1), Doc.Token.RealOrImaginary.REAL, ZERO, - /* breakAndIndentTrailingComment= */ Optional.empty()); + /* breakAndIndentTrailingComment= */ Optional.empty()); } } @@ -427,7 +427,7 @@ public final void breakToFill(String flat) { * @param plusIndent extra indent if taken */ public final void breakOp(Doc.FillMode fillMode, String flat, Indent plusIndent) { - breakOp(fillMode, flat, plusIndent, /* optionalTag= */ Optional.empty()); + breakOp(fillMode, flat, plusIndent, /* optionalTag= */ Optional.empty()); } /** 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 d54b2317..9526b892 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java @@ -185,4 +185,3 @@ private static boolean javadocShaped(List lines) { return true; } } - 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 656b65c8..ea373113 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java @@ -140,7 +140,7 @@ public void append(String text, Range range) { if (i + 1 < text.length() && text.charAt(i + 1) == '\n') { i++; } - // falls through + // falls through case '\n': spacesPending = new StringBuilder(); ++newlinesPending; diff --git a/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java b/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java index ebdc8dfe..2f2e33cb 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java +++ b/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java @@ -37,7 +37,9 @@ public final class FormattingFiler implements Filer { private final Formatter formatter = new Formatter(); private final Messager messager; - /** @param delegate filer to decorate */ + /** + * @param delegate filer to decorate + */ public FormattingFiler(Filer delegate) { this(delegate, null); } diff --git a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java index 8a4100e4..5e6af179 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java @@ -40,6 +40,7 @@ final class JavadocWriter { private final int blockIndent; private final StringBuilder output = new StringBuilder(); + /** * Whether we are inside an {@code
  • } element, excluding the case in which the {@code
  • } * contains a {@code
      } or {@code
        } that we are also inside -- unless of course we're diff --git a/core/src/test/java/com/google/googlejavaformat/java/CommandLineFlagsTest.java b/core/src/test/java/com/google/googlejavaformat/java/CommandLineFlagsTest.java index e5fbc9f5..928ce007 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/CommandLineFlagsTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/CommandLineFlagsTest.java @@ -21,9 +21,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** - * Tests for command-line flags. - */ +/** Tests for command-line flags. */ @RunWith(JUnit4.class) public class CommandLineFlagsTest { diff --git a/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java b/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java index aab8ec5d..39d43c2f 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java @@ -1084,7 +1084,7 @@ public void paragraphTag() { @Test public void xhtmlParagraphTag() { String[] input = { - "class Test {", + "class Test {", // " /**", " * hello

        world", " */", @@ -1093,7 +1093,7 @@ public void xhtmlParagraphTag() { "}", }; String[] expected = { - "class Test {", + "class Test {", // " /**", " * hello", " *", diff --git a/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java b/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java index c300514b..68ff908c 100644 --- a/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java +++ b/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java @@ -106,7 +106,9 @@ private List> rangesFromRegions(IRegion[] regions) { return ranges; } - /** @return {@code true} if input and output texts are equal, else {@code false}. */ + /** + * @return {@code true} if input and output texts are equal, else {@code false}. + */ private boolean idempotent(String source, IRegion[] regions, List replacements) { // This implementation only checks for single replacement. if (replacements.size() == 1) { diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java index 759decc0..3a98c2eb 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java @@ -199,7 +199,9 @@ private void createUIComponents() { false)); } - /** @noinspection ALL */ + /** + * @noinspection ALL + */ public JComponent $$$getRootComponent$$$() { return panel; } From 453ad0853c766d2ea74f95341948b875570195c9 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 12 Nov 2024 16:30:43 -0800 Subject: [PATCH 15/19] Add some more test cases for pattern guards PiperOrigin-RevId: 695910986 --- .../java/testdata/SwitchGuardClause.input | 48 ++++++++++++++++++ .../java/testdata/SwitchGuardClause.output | 50 +++++++++++++++++++ 2 files changed, 98 insertions(+) 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 index 25df5809..36eb21b1 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input @@ -6,4 +6,52 @@ class SwitchGuardClause { default -> true; }; } + + { + switch (o) { + case TypeWithVeryVeryVeryVeryLongName variableWithVeryLongName when variableWithVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, + TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, + TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> + System.err.println(); + default -> {} + } + } } 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 index 25df5809..1a81422c 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output @@ -6,4 +6,54 @@ class SwitchGuardClause { default -> true; }; } + + { + switch (o) { + case TypeWithVeryVeryVeryVeryLongName variableWithVeryLongName when variableWithVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, + TypeWithVeryVeryVeryVeryLongName + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } + switch (o) { + case TypeWithVeryVeryVeryVeryLongName + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, + TypeWithVeryVeryVeryVeryLongName + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> + System.err.println(); + default -> {} + } + } } From 7e5fb80fec99ddb523f74a39bfbf40e4bd277210 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 13 Nov 2024 10:50:01 -0800 Subject: [PATCH 16/19] Allow breaks before pattern guards Previously there were never breaks before `when` pattern guards, which produced surprising results. There's possible future work here around evaluating how many level of continuation indents to take to differentiate between syntactic levels, but I think this is an improvement on the status quo. PiperOrigin-RevId: 696193971 --- .../java/java17/Java17InputAstVisitor.java | 16 ++---- .../java/testdata/SwitchGuardClause.input | 40 +++++++++---- .../java/testdata/SwitchGuardClause.output | 56 ++++++++++++------- 3 files changed, 70 insertions(+), 42 deletions(-) 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 a0037edb..31552358 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 @@ -82,7 +82,6 @@ public Void visitBindingPattern(BindingPatternTree node, Void unused) { } private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) { - builder.open(plusFour); declareOne( DeclarationKind.PARAMETER, Direction.HORIZONTAL, @@ -95,7 +94,6 @@ private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) /* trailing= */ Optional.empty(), /* receiverExpression= */ Optional.empty(), /* typeWithDims= */ Optional.empty()); - builder.close(); } @Override @@ -227,16 +225,12 @@ public Void visitCase(CaseTree node, Void unused) { List 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); + builder.open(node.getCaseKind().equals(CaseTree.CaseKind.RULE) ? plusFour : ZERO); if (isDefault) { token("default", ZERO); } else { token("case", ZERO); - builder.open(labels.size() > 1 ? plusFour : ZERO); + builder.open(ZERO); builder.space(); boolean afterFirstToken = false; for (Tree expression : labels) { @@ -252,7 +246,7 @@ public Void visitCase(CaseTree node, Void unused) { final ExpressionTree guard = getGuard(node); if (guard != null) { - builder.space(); + builder.breakToFill(" "); token("when"); builder.space(); scan(guard, null); @@ -264,12 +258,14 @@ public Void visitCase(CaseTree node, Void unused) { builder.open(plusTwo); visitStatements(node.getStatements()); builder.close(); + builder.close(); break; case RULE: builder.space(); token("-"); token(">"); if (node.getBody().getKind() == Tree.Kind.BLOCK) { + builder.close(); builder.space(); // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. visitBlock( @@ -280,13 +276,13 @@ public Void visitCase(CaseTree node, Void unused) { } else { builder.breakOp(" "); scan(node.getBody(), null); + builder.close(); } builder.guessToken(";"); break; default: throw new AssertionError(node.getCaseKind()); } - builder.close(); return null; } 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 index 36eb21b1..0f4b485b 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.input @@ -27,7 +27,7 @@ class SwitchGuardClause { } switch (o) { case TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName - when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName .methodWithVeryVeryVeryVeryLongNameReturnThis() .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { System.err.println(); @@ -35,18 +35,34 @@ class SwitchGuardClause { default -> {} } switch (o) { - case TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, - TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName - when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName - .methodWithVeryVeryVeryVeryLongNameReturnThis() - .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { - System.err.println(); - } - default -> {} - } + case SwitchRecord( + int one, + int two, + int three, + int four, + int five, + int six, + int seven, + int eight, + int variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName) + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + System.err.println(); + } + default -> {} + } switch (o) { - case TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, - TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + case SwitchRecord( + int one, + int two, + int three, + int four, + int five, + int six, + int seven, + int eight, + int variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName) when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName .methodWithVeryVeryVeryVeryLongNameReturnThis() .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> 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 index 1a81422c..ac0961d0 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/SwitchGuardClause.output @@ -9,47 +9,63 @@ class SwitchGuardClause { { switch (o) { - case TypeWithVeryVeryVeryVeryLongName variableWithVeryLongName when variableWithVeryLongName - .methodWithVeryVeryVeryVeryLongNameReturnThis() - .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + case TypeWithVeryVeryVeryVeryLongName variableWithVeryLongName + when variableWithVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { System.err.println(); } default -> {} } switch (o) { - case TypeWithVeryVeryVeryVeryLongName - variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName - .methodWithVeryVeryVeryVeryLongNameReturnThis() - .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + case TypeWithVeryVeryVeryVeryLongName variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { System.err.println(); } default -> {} } switch (o) { case TypeWithVeryVeryVeryVeryLongName - variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName - .methodWithVeryVeryVeryVeryLongNameReturnThis() - .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { System.err.println(); } default -> {} } switch (o) { - case TypeWithVeryVeryVeryVeryLongName - variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, - TypeWithVeryVeryVeryVeryLongName - variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName - .methodWithVeryVeryVeryVeryLongNameReturnThis() - .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { + case SwitchRecord( + int one, + int two, + int three, + int four, + int five, + int six, + int seven, + int eight, + int variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName) + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + .methodWithVeryVeryVeryVeryLongNameReturnThis() + .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> { System.err.println(); } default -> {} } switch (o) { - case TypeWithVeryVeryVeryVeryLongName - variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName, - TypeWithVeryVeryVeryVeryLongName - variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName + case SwitchRecord( + int one, + int two, + int three, + int four, + int five, + int six, + int seven, + int eight, + int variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName) + when variableWithVeryVeryVeryVeryVeryVeryVeryVeryVeryLongName .methodWithVeryVeryVeryVeryLongNameReturnThis() .methodWithVeryVeryVeryVeryLongNameReturnBoolean() -> System.err.println(); From 3356bd342c53a84acaff74768ff5fb40b1f296e3 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 18 Nov 2024 14:20:52 -0800 Subject: [PATCH 17/19] Remove Java17InputAstVisitor JDk 17 is now the lowest supported version. https://github.com/google/google-java-format/issues/1159 PiperOrigin-RevId: 697756606 --- .../googlejavaformat/java/Formatter.java | 4 - .../java/JavaInputAstVisitor.java | 198 +++++++++++- .../java/java17/Java17InputAstVisitor.java | 292 ------------------ .../java/java21/Java21InputAstVisitor.java | 6 +- .../META-INF/native-image/reflect-config.json | 9 - 5 files changed, 186 insertions(+), 323 deletions(-) delete mode 100644 core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java 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 5aa7a123..e3b2b323 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java @@ -155,10 +155,6 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept 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()); } 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 01f9a3e0..dcaa930e 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -14,6 +14,7 @@ package com.google.googlejavaformat.java; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.googlejavaformat.Doc.FillMode.INDEPENDENT; @@ -84,8 +85,10 @@ import com.sun.source.tree.AssertTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.BinaryTree; +import com.sun.source.tree.BindingPatternTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.BreakTree; +import com.sun.source.tree.CaseLabelTree; import com.sun.source.tree.CaseTree; import com.sun.source.tree.CatchTree; import com.sun.source.tree.ClassTree; @@ -125,6 +128,7 @@ import com.sun.source.tree.RequiresTree; import com.sun.source.tree.ReturnTree; import com.sun.source.tree.StatementTree; +import com.sun.source.tree.SwitchExpressionTree; import com.sun.source.tree.SwitchTree; import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.ThrowTree; @@ -138,11 +142,13 @@ import com.sun.source.tree.VariableTree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.tree.WildcardTree; +import com.sun.source.tree.YieldTree; import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeScanner; import java.util.ArrayDeque; import java.util.ArrayList; @@ -410,7 +416,17 @@ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) { return null; } - protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) {} + protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) { + ModuleTree module = node.getModule(); + if (module != null) { + if (afterFirstToken) { + builder.blankLineWanted(YES); + } + markForPartialFormat(); + visitModule(module, null); + builder.forcedBreak(); + } + } /** Skips over extra semi-colons at the top-level, or in a class member declaration lists. */ protected void dropEmptyDeclarations() { @@ -436,6 +452,9 @@ public Void visitClass(ClassTree tree, Void unused) { case ENUM: visitEnumDeclaration(tree); break; + case RECORD: + visitRecordDeclaration(tree); + break; default: throw new AssertionError(tree.getKind()); } @@ -928,6 +947,69 @@ public boolean visitEnumDeclaration(ClassTree node) { return false; } + public void visitRecordDeclaration(ClassTree node) { + sync(node); + typeDeclarationModifiers(node.getModifiers()); + Verify.verify(node.getExtendsClause() == null); + boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); + token("record"); + builder.space(); + visit(node.getSimpleName()); + if (!node.getTypeParameters().isEmpty()) { + token("<"); + } + builder.open(plusFour); + { + if (!node.getTypeParameters().isEmpty()) { + typeParametersRest(node.getTypeParameters(), hasSuperInterfaceTypes ? plusFour : ZERO); + } + ImmutableList parameters = JavaInputAstVisitor.recordVariables(node); + token("("); + if (!parameters.isEmpty()) { + // Break before args. + builder.breakToFill(""); + } + // record headers can't declare receiver parameters + visitFormals(/* receiver= */ Optional.empty(), parameters); + token(")"); + if (hasSuperInterfaceTypes) { + builder.breakToFill(" "); + builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); + token("implements"); + builder.space(); + boolean afterFirstToken = false; + for (Tree superInterfaceType : node.getImplementsClause()) { + if (afterFirstToken) { + token(","); + builder.breakOp(" "); + } + scan(superInterfaceType, null); + afterFirstToken = true; + } + builder.close(); + } + } + builder.close(); + if (node.getMembers() == null) { + token(";"); + } else { + ImmutableList members = + node.getMembers().stream() + .filter(t -> (TreeInfo.flags((JCTree) t) & Flags.GENERATED_MEMBER) == 0) + .collect(toImmutableList()); + addBodyDeclarations(members, BracesOrNot.YES, FirstDeclarationsOrNot.YES); + } + dropEmptyDeclarations(); + } + + private static ImmutableList recordVariables(ClassTree node) { + return node.getMembers().stream() + .filter(JCTree.JCVariableDecl.class::isInstance) + .map(JCTree.JCVariableDecl.class::cast) + .filter(m -> (m.mods.flags & RECORD) == RECORD) + .collect(toImmutableList()); + } + @Override public Void visitMemberReference(MemberReferenceTree node, Void unused) { builder.open(plusFour); @@ -1199,7 +1281,11 @@ public Void visitInstanceOf(InstanceOfTree node, Void unused) { builder.open(ZERO); token("instanceof"); builder.breakOp(" "); - scan(node.getType(), null); + if (node.getPattern() != null) { + scan(node.getPattern(), null); + } else { + scan(node.getType(), null); + } builder.close(); builder.close(); return null; @@ -1874,18 +1960,69 @@ public Void visitCase(CaseTree node, Void unused) { sync(node); markForPartialFormat(); builder.forcedBreak(); - if (node.getExpression() == null) { + List labels = node.getLabels(); + boolean isDefault = + labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL"); + builder.open(node.getCaseKind().equals(CaseTree.CaseKind.RULE) ? plusFour : ZERO); + if (isDefault) { token("default", ZERO); - token(":"); } else { token("case", ZERO); + builder.open(ZERO); builder.space(); - scan(node.getExpression(), null); - token(":"); + boolean afterFirstToken = false; + for (Tree expression : labels) { + if (afterFirstToken) { + token(","); + builder.breakOp(" "); + } + scan(expression, null); + afterFirstToken = true; + } + builder.close(); + } + + final ExpressionTree guard = getGuard(node); + if (guard != null) { + builder.breakToFill(" "); + token("when"); + builder.space(); + scan(guard, null); + } + + switch (node.getCaseKind()) { + case STATEMENT: + token(":"); + builder.open(plusTwo); + visitStatements(node.getStatements()); + builder.close(); + builder.close(); + break; + case RULE: + builder.space(); + token("-"); + token(">"); + if (node.getBody().getKind() == BLOCK) { + builder.close(); + builder.space(); + // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. + visitBlock( + (BlockTree) node.getBody(), + CollapseEmptyOrNot.YES, + AllowLeadingBlankLine.NO, + AllowTrailingBlankLine.NO); + } else { + builder.breakOp(" "); + scan(node.getBody(), null); + builder.close(); + } + builder.guessToken(";"); + break; } - builder.open(plusTwo); - visitStatements(node.getStatements()); - builder.close(); + return null; + } + + protected ExpressionTree getGuard(final CaseTree node) { return null; } @@ -2022,7 +2159,7 @@ public Void visitTry(TryTree node, Void unused) { public void visitClassDeclaration(ClassTree node) { sync(node); typeDeclarationModifiers(node.getModifiers()); - List permitsTypes = getPermitsClause(node); + List permitsTypes = node.getPermitsClause(); boolean hasSuperclassType = node.getExtendsClause() != null; boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); boolean hasPermitsTypes = !permitsTypes.isEmpty(); @@ -3800,11 +3937,6 @@ protected void addBodyDeclarations( } } - /** Gets the permits clause for the given node. This is only available in Java 15 and later. */ - protected List getPermitsClause(ClassTree node) { - return ImmutableList.of(); - } - private void classDeclarationTypeList(String token, List types) { if (types.isEmpty()) { return; @@ -3966,4 +4098,40 @@ final BreakTag genSym() { public final String toString() { return MoreObjects.toStringHelper(this).add("builder", builder).toString(); } + + @Override + public Void visitBindingPattern(BindingPatternTree node, Void unused) { + sync(node); + VariableTree variableTree = node.getVariable(); + declareOne( + DeclarationKind.PARAMETER, + Direction.HORIZONTAL, + Optional.of(variableTree.getModifiers()), + variableTree.getType(), + variableTree.getName(), + /* op= */ "", + /* equals= */ "", + /* initializer= */ Optional.empty(), + /* trailing= */ Optional.empty(), + /* receiverExpression= */ Optional.empty(), + /* typeWithDims= */ Optional.empty()); + return null; + } + + @Override + public Void visitYield(YieldTree node, Void aVoid) { + sync(node); + token("yield"); + builder.space(); + scan(node.getValue(), null); + token(";"); + return null; + } + + @Override + public Void visitSwitchExpression(SwitchExpressionTree node, Void aVoid) { + sync(node); + visitSwitch(node.getExpression(), node.getCases()); + return null; + } } 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 deleted file mode 100644 index 31552358..00000000 --- a/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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. - */ - -package com.google.googlejavaformat.java.java17; - -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; - -import com.google.common.base.Verify; -import com.google.common.collect.ImmutableList; -import com.google.googlejavaformat.OpsBuilder; -import com.google.googlejavaformat.OpsBuilder.BlankLineWanted; -import com.google.googlejavaformat.java.JavaInputAstVisitor; -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; -import com.sun.source.tree.SwitchExpressionTree; -import com.sun.source.tree.Tree; -import com.sun.source.tree.VariableTree; -import com.sun.source.tree.YieldTree; -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.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 in - * Java 17. - */ -public class Java17InputAstVisitor extends JavaInputAstVisitor { - - public Java17InputAstVisitor(OpsBuilder builder, int indentMultiplier) { - super(builder, indentMultiplier); - } - - @Override - protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) { - ModuleTree module = node.getModule(); - if (module != null) { - if (afterFirstToken) { - builder.blankLineWanted(BlankLineWanted.YES); - } - markForPartialFormat(); - visitModule(module, null); - builder.forcedBreak(); - } - } - - @Override - protected List getPermitsClause(ClassTree node) { - return node.getPermitsClause(); - } - - @Override - public Void visitBindingPattern(BindingPatternTree node, Void unused) { - sync(node); - VariableTree variableTree = node.getVariable(); - visitBindingPattern( - variableTree.getModifiers(), variableTree.getType(), variableTree.getName()); - return null; - } - - private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) { - declareOne( - DeclarationKind.PARAMETER, - Direction.HORIZONTAL, - Optional.of(modifiers), - type, - name, - /* op= */ "", - /* equals= */ "", - /* initializer= */ Optional.empty(), - /* trailing= */ Optional.empty(), - /* receiverExpression= */ Optional.empty(), - /* typeWithDims= */ Optional.empty()); - } - - @Override - public Void visitYield(YieldTree node, Void aVoid) { - sync(node); - token("yield"); - builder.space(); - scan(node.getValue(), null); - token(";"); - return null; - } - - @Override - public Void visitSwitchExpression(SwitchExpressionTree node, Void aVoid) { - sync(node); - visitSwitch(node.getExpression(), node.getCases()); - return null; - } - - @Override - public Void visitClass(ClassTree tree, Void unused) { - switch (tree.getKind()) { - case ANNOTATION_TYPE: - visitAnnotationType(tree); - break; - case CLASS: - case INTERFACE: - visitClassDeclaration(tree); - break; - case ENUM: - visitEnumDeclaration(tree); - break; - case RECORD: - visitRecordDeclaration(tree); - break; - default: - throw new AssertionError(tree.getKind()); - } - return null; - } - - public void visitRecordDeclaration(ClassTree node) { - sync(node); - typeDeclarationModifiers(node.getModifiers()); - Verify.verify(node.getExtendsClause() == null); - boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); - token("record"); - builder.space(); - visit(node.getSimpleName()); - if (!node.getTypeParameters().isEmpty()) { - token("<"); - } - builder.open(plusFour); - { - if (!node.getTypeParameters().isEmpty()) { - typeParametersRest(node.getTypeParameters(), hasSuperInterfaceTypes ? plusFour : ZERO); - } - ImmutableList parameters = recordVariables(node); - token("("); - if (!parameters.isEmpty()) { - // Break before args. - builder.breakToFill(""); - } - // record headers can't declare receiver parameters - visitFormals(/* receiver= */ Optional.empty(), parameters); - token(")"); - if (hasSuperInterfaceTypes) { - builder.breakToFill(" "); - builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); - token("implements"); - builder.space(); - boolean afterFirstToken = false; - for (Tree superInterfaceType : node.getImplementsClause()) { - if (afterFirstToken) { - token(","); - builder.breakOp(" "); - } - scan(superInterfaceType, null); - afterFirstToken = true; - } - builder.close(); - } - } - builder.close(); - if (node.getMembers() == null) { - token(";"); - } else { - List members = - node.getMembers().stream() - .filter(t -> (TreeInfo.flags((JCTree) t) & Flags.GENERATED_MEMBER) == 0) - .collect(toImmutableList()); - addBodyDeclarations(members, BracesOrNot.YES, FirstDeclarationsOrNot.YES); - } - dropEmptyDeclarations(); - } - - private static ImmutableList recordVariables(ClassTree node) { - return node.getMembers().stream() - .filter(JCVariableDecl.class::isInstance) - .map(JCVariableDecl.class::cast) - .filter(m -> (m.mods.flags & RECORD) == RECORD) - .collect(toImmutableList()); - } - - @Override - public Void visitInstanceOf(InstanceOfTree node, Void unused) { - sync(node); - builder.open(plusFour); - scan(node.getExpression(), null); - builder.breakOp(" "); - builder.open(ZERO); - token("instanceof"); - builder.breakOp(" "); - if (node.getPattern() != null) { - scan(node.getPattern(), null); - } else { - scan(node.getType(), null); - } - builder.close(); - builder.close(); - return null; - } - - @Override - public Void visitCase(CaseTree node, Void unused) { - sync(node); - markForPartialFormat(); - builder.forcedBreak(); - List labels = node.getLabels(); - boolean isDefault = - labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL"); - builder.open(node.getCaseKind().equals(CaseTree.CaseKind.RULE) ? plusFour : ZERO); - if (isDefault) { - token("default", ZERO); - } else { - token("case", ZERO); - builder.open(ZERO); - builder.space(); - boolean afterFirstToken = false; - for (Tree expression : labels) { - if (afterFirstToken) { - token(","); - builder.breakOp(" "); - } - scan(expression, null); - afterFirstToken = true; - } - builder.close(); - } - - final ExpressionTree guard = getGuard(node); - if (guard != null) { - builder.breakToFill(" "); - token("when"); - builder.space(); - scan(guard, null); - } - - switch (node.getCaseKind()) { - case STATEMENT: - token(":"); - builder.open(plusTwo); - visitStatements(node.getStatements()); - builder.close(); - builder.close(); - break; - case RULE: - builder.space(); - token("-"); - token(">"); - if (node.getBody().getKind() == Tree.Kind.BLOCK) { - builder.close(); - builder.space(); - // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. - visitBlock( - (BlockTree) node.getBody(), - CollapseEmptyOrNot.YES, - AllowLeadingBlankLine.NO, - AllowTrailingBlankLine.NO); - } else { - builder.breakOp(" "); - scan(node.getBody(), null); - builder.close(); - } - builder.guessToken(";"); - break; - default: - throw new AssertionError(node.getCaseKind()); - } - return null; - } - - 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 index 859c9c0c..029da8e0 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 @@ -15,7 +15,7 @@ package com.google.googlejavaformat.java.java21; import com.google.googlejavaformat.OpsBuilder; -import com.google.googlejavaformat.java.java17.Java17InputAstVisitor; +import com.google.googlejavaformat.java.JavaInputAstVisitor; import com.sun.source.tree.CaseTree; import com.sun.source.tree.ConstantCaseLabelTree; import com.sun.source.tree.DeconstructionPatternTree; @@ -28,10 +28,10 @@ import javax.lang.model.element.Name; /** - * Extends {@link Java17InputAstVisitor} with support for AST nodes that were added or modified in + * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified in * Java 21. */ -public class Java21InputAstVisitor extends Java17InputAstVisitor { +public class Java21InputAstVisitor extends JavaInputAstVisitor { public Java21InputAstVisitor(OpsBuilder builder, int indentMultiplier) { super(builder, indentMultiplier); 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 4d30840f..89577c03 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 @@ -3,15 +3,6 @@ "name": "com.sun.tools.javac.parser.UnicodeReader", "allDeclaredMethods": true }, - { - "name": "com.google.googlejavaformat.java.java17.Java17InputAstVisitor", - "methods": [ - { - "name": "", - "parameterTypes": ["com.google.googlejavaformat.OpsBuilder", "int"] - } - ] - }, { "name": "com.google.googlejavaformat.java.java21.Java21InputAstVisitor", "methods": [ From 52696257a3f71158e4730ba47b569d7d6eae75a4 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 18 Nov 2024 15:54:34 -0800 Subject: [PATCH 18/19] Fix logic for checking if text blocks are already deindented Check the last line instead of the first line of the text block contents. Empty lines in the text block have no leading whitespace but do not indicate the contents are deindented, only non-empty lines should be considered. The last line will never be empty, it contains at least the close delimiter. PiperOrigin-RevId: 697785256 --- .../googlejavaformat/java/StringWrapper.java | 3 ++- .../java/StringWrapperTest.java | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) 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 42099d6b..c3a36ab5 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -207,7 +207,8 @@ private void indentTextBlocks( String stripped = stripIndent(initialLines.stream().skip(1).collect(joining(separator))); ImmutableList lines = stripped.lines().collect(toImmutableList()); int deindent = - initialLines.get(1).stripTrailing().length() - lines.get(0).stripTrailing().length(); + getLast(initialLines).stripTrailing().length() + - getLast(lines).stripTrailing().length(); String prefix = (deindent == 0 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 7854126c..339ed134 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java @@ -174,6 +174,33 @@ public void textBlockSpaceTabMix() throws Exception { assertThat(actual).isEqualTo(expected); } + @Test + public void leadingBlankLine() throws Exception { + assumeTrue(Runtime.version().feature() >= 15); + String input = + lines( + "public class T {", + " String s =", + " \"\"\"", + "", + " lorem", + " ipsum", + " \"\"\";", + "}"); + String expected = + lines( + "public class T {", + " String s =", + " \"\"\"", + "", + " lorem", + " ipsum", + " \"\"\";", + "}"); + String actual = StringWrapper.wrap(100, input, new Formatter()); + assertThat(actual).isEqualTo(expected); + } + private static String lines(String... line) { return Joiner.on('\n').join(line) + '\n'; } From bd336629a3f538ecaab8af475e745450535775e3 Mon Sep 17 00:00:00 2001 From: cushon Date: Tue, 19 Nov 2024 00:48:59 +0000 Subject: [PATCH 19/19] Release google-java-format 1.25.0 --- core/pom.xml | 2 +- eclipse_plugin/META-INF/MANIFEST.MF | 2 +- eclipse_plugin/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 5b6bbb4d..ad9b453d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,7 +22,7 @@ com.google.googlejavaformat google-java-format-parent - HEAD-SNAPSHOT + 1.25.0 google-java-format diff --git a/eclipse_plugin/META-INF/MANIFEST.MF b/eclipse_plugin/META-INF/MANIFEST.MF index 91324539..775ba94c 100644 --- a/eclipse_plugin/META-INF/MANIFEST.MF +++ b/eclipse_plugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: google-java-format Bundle-SymbolicName: google-java-format-eclipse-plugin;singleton:=true Bundle-Vendor: Google -Bundle-Version: 1.13.0 +Bundle-Version: 1.25.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.jdt.core;bundle-version="3.10.0", org.eclipse.jface, diff --git a/eclipse_plugin/pom.xml b/eclipse_plugin/pom.xml index b2c6e368..aebc5e54 100644 --- a/eclipse_plugin/pom.xml +++ b/eclipse_plugin/pom.xml @@ -22,7 +22,7 @@ com.google.googlejavaformat google-java-format-eclipse-plugin eclipse-plugin - 1.13.0 + 1.25.0 Google Java Format Plugin for Eclipse 4.5+ diff --git a/pom.xml b/pom.xml index c3134022..32d5ba26 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ com.google.googlejavaformat google-java-format-parent pom - HEAD-SNAPSHOT + 1.25.0 core