From ce3cb59a8d649359a8e6e7fcc5f2f21bb79b3df1 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 22 Feb 2024 15:52:03 -0800 Subject: [PATCH 1/9] Re-indent text blocks PiperOrigin-RevId: 609526784 --- .../java/JavaInputAstVisitor.java | 3 + .../googlejavaformat/java/StringWrapper.java | 199 ++++++++++++++---- .../java/StringWrapperTest.java | 89 +++++++- .../googlejavaformat/java/testdata/RSLs.input | 39 +++- .../java/testdata/RSLs.output | 54 ++++- 5 files changed, 327 insertions(+), 57 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 1123ac01c..c23acbdb8 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -1667,6 +1667,9 @@ public Void visitMemberSelect(MemberSelectTree node, Void unused) { public Void visitLiteral(LiteralTree node, Void unused) { sync(node); String sourceForNode = getSourceForNode(node, getCurrentPath()); + if (sourceForNode.startsWith("\"\"\"")) { + builder.forcedBreak(); + } 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 f241ae47f..81714db64 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.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 java.lang.Math.min; import static java.nio.charset.StandardCharsets.UTF_8; @@ -44,6 +45,7 @@ import com.sun.tools.javac.util.Position; import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayDeque; import java.util.ArrayList; @@ -59,6 +61,7 @@ import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardLocation; +import org.checkerframework.checker.nullness.qual.Nullable; /** Wraps string literals that exceed the column limit. */ public final class StringWrapper { @@ -72,7 +75,7 @@ public static String wrap(String input, Formatter formatter) throws FormatterExc */ static String wrap(final int columnLimit, String input, Formatter formatter) throws FormatterException { - if (!longLines(columnLimit, input)) { + if (!needWrapping(columnLimit, input)) { // fast path return input; } @@ -111,13 +114,48 @@ static String wrap(final int columnLimit, String input, Formatter formatter) private static TreeRangeMap getReflowReplacements( int columnLimit, final String input) throws FormatterException { - JCTree.JCCompilationUnit unit = parse(input, /* allowStringFolding= */ false); - String separator = Newlines.guessLineSeparator(input); + return new Reflower(columnLimit, input).getReflowReplacements(); + } + + private static class Reflower { + + private final String input; + private final int columnLimit; + private final String separator; + private final JCTree.JCCompilationUnit unit; + private final Position.LineMap lineMap; + + Reflower(int columnLimit, String input) throws FormatterException { + this.columnLimit = columnLimit; + this.input = input; + this.separator = Newlines.guessLineSeparator(input); + this.unit = parse(input, /* allowStringFolding= */ false); + this.lineMap = unit.getLineMap(); + } + + TreeRangeMap getReflowReplacements() { + // Paths to string literals that extend past the column limit. + List longStringLiterals = new ArrayList<>(); + // Paths to text blocks to be re-indented. + List textBlocks = new ArrayList<>(); + new LongStringsAndTextBlockScanner(longStringLiterals, textBlocks) + .scan(new TreePath(unit), null); + TreeRangeMap replacements = TreeRangeMap.create(); + indentTextBlocks(replacements, textBlocks); + wrapLongStrings(replacements, longStringLiterals); + return replacements; + } + + private class LongStringsAndTextBlockScanner extends TreePathScanner { + + private final List longStringLiterals; + private final List textBlocks; + + LongStringsAndTextBlockScanner(List longStringLiterals, List textBlocks) { + this.longStringLiterals = longStringLiterals; + this.textBlocks = textBlocks; + } - // Paths to string literals that extend past the column limit. - List toFix = new ArrayList<>(); - final Position.LineMap lineMap = unit.getLineMap(); - new TreePathScanner() { @Override public Void visitLiteral(LiteralTree literalTree, Void aVoid) { if (literalTree.getKind() != Kind.STRING_LITERAL) { @@ -125,6 +163,7 @@ public Void visitLiteral(LiteralTree literalTree, Void aVoid) { } int pos = getStartPosition(literalTree); if (input.substring(pos, min(input.length(), pos + 3)).equals("\"\"\"")) { + textBlocks.add(literalTree); return null; } Tree parent = getCurrentPath().getParentPath().getLeaf(); @@ -140,44 +179,114 @@ public Void visitLiteral(LiteralTree literalTree, Void aVoid) { if (lineMap.getColumnNumber(lineEnd) - 1 <= columnLimit) { return null; } - toFix.add(getCurrentPath()); + longStringLiterals.add(getCurrentPath()); return null; } - }.scan(new TreePath(unit), null); - - TreeRangeMap replacements = TreeRangeMap.create(); - for (TreePath path : toFix) { - // Find the outermost contiguous enclosing concatenation expression - TreePath enclosing = path; - while (enclosing.getParentPath().getLeaf().getKind() == Tree.Kind.PLUS) { - enclosing = enclosing.getParentPath(); + } + + private void indentTextBlocks( + TreeRangeMap replacements, List textBlocks) { + for (Tree tree : textBlocks) { + int startPosition = getStartPosition(tree); + int endPosition = getEndPosition(unit, tree); + String text = input.substring(startPosition, endPosition); + + // 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 + // whitespace. + ImmutableList initialLines = text.lines().collect(toImmutableList()); + 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(); + + int startColumn = lineMap.getColumnNumber(startPosition); + String prefix = + (deindent == 0 || lines.stream().anyMatch(x -> x.length() + startColumn > columnLimit)) + ? "" + : " ".repeat(startColumn - 1); + + StringBuilder output = new StringBuilder("\"\"\""); + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + String trimmed = line.stripLeading().stripTrailing(); + output.append(separator); + if (!trimmed.isEmpty()) { + // 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 + // whitespace has no effect, and results in a javac text-blocks warning that 'trailing + // white space will be removed'. + output.append("\"\"\""); + } else { + output.append(line); + } + } + replacements.put(Range.closedOpen(startPosition, endPosition), output.toString()); } - // Is the literal being wrapped the first in a chain of concatenation expressions? - // i.e. `ONE + TWO + THREE` - // We need this information to handle continuation indents. - AtomicBoolean first = new AtomicBoolean(false); - // Finds the set of string literals in the concat expression that includes the one that needs - // to be wrapped. - List flat = flatten(input, unit, path, enclosing, first); - // Zero-indexed start column - int startColumn = lineMap.getColumnNumber(getStartPosition(flat.get(0))) - 1; - - // Handling leaving trailing non-string tokens at the end of the literal, - // e.g. the trailing `);` in `foo("...");`. - int end = getEndPosition(unit, getLast(flat)); - int lineEnd = end; - while (Newlines.hasNewlineAt(input, lineEnd) == -1) { - lineEnd++; + } + + private void wrapLongStrings( + TreeRangeMap replacements, List longStringLiterals) { + for (TreePath path : longStringLiterals) { + // Find the outermost contiguous enclosing concatenation expression + TreePath enclosing = path; + while (enclosing.getParentPath().getLeaf().getKind() == Kind.PLUS) { + enclosing = enclosing.getParentPath(); + } + // Is the literal being wrapped the first in a chain of concatenation expressions? + // i.e. `ONE + TWO + THREE` + // We need this information to handle continuation indents. + AtomicBoolean first = new AtomicBoolean(false); + // Finds the set of string literals in the concat expression that includes the one that + // needs + // to be wrapped. + List flat = flatten(input, unit, path, enclosing, first); + // Zero-indexed start column + int startColumn = lineMap.getColumnNumber(getStartPosition(flat.get(0))) - 1; + + // Handling leaving trailing non-string tokens at the end of the literal, + // e.g. the trailing `);` in `foo("...");`. + int end = getEndPosition(unit, getLast(flat)); + int lineEnd = end; + while (Newlines.hasNewlineAt(input, lineEnd) == -1) { + lineEnd++; + } + int trailing = lineEnd - end; + + // Get the original source text of the string literals, excluding `"` and `+`. + ImmutableList components = stringComponents(input, unit, flat); + replacements.put( + Range.closedOpen(getStartPosition(flat.get(0)), getEndPosition(unit, getLast(flat))), + reflow(separator, columnLimit, startColumn, trailing, components, first.get())); } - int trailing = lineEnd - end; + } + } + + private static final Method STRIP_INDENT = getStripIndent(); + + private static @Nullable Method getStripIndent() { + if (Runtime.version().feature() < 15) { + return null; + } + try { + return String.class.getMethod("stripIndent"); + } catch (NoSuchMethodException e) { + throw new LinkageError(e.getMessage(), e); + } + } - // Get the original source text of the string literals, excluding `"` and `+`. - ImmutableList components = stringComponents(input, unit, flat); - replacements.put( - Range.closedOpen(getStartPosition(flat.get(0)), getEndPosition(unit, getLast(flat))), - reflow(separator, columnLimit, startColumn, trailing, components, first.get())); + private static String stripIndent(String input) { + if (STRIP_INDENT == null) { + return input; + } + try { + return (String) STRIP_INDENT.invoke(input); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); } - return replacements; } /** @@ -364,13 +473,16 @@ private static int getStartPosition(Tree tree) { return ((JCTree) tree).getStartPosition(); } - /** Returns true if any lines in the given Java source exceed the column limit. */ - private static boolean longLines(int columnLimit, String input) { + /** + * Returns true if any lines in the given Java source exceed the column limit, or contain a {@code + * """} that could indicate a text block. + */ + private static boolean needWrapping(int columnLimit, String input) { // TODO(cushon): consider adding Newlines.lineIterable? Iterator it = Newlines.lineIterator(input); while (it.hasNext()) { String line = it.next(); - if (line.length() > columnLimit) { + if (line.length() > columnLimit || line.contains("\"\"\"")) { return true; } } @@ -385,7 +497,6 @@ private static JCTree.JCCompilationUnit parse(String source, boolean allowString context.put(DiagnosticListener.class, diagnostics); Options.instance(context).put("--enable-preview", "true"); Options.instance(context).put("allowStringFolding", Boolean.toString(allowStringFolding)); - JCTree.JCCompilationUnit unit; JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8); try { fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.of()); @@ -404,7 +515,7 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) { JavacParser parser = parserFactory.newParser( source, /* keepDocComments= */ true, /* keepEndPos= */ true, /* keepLineMap= */ true); - unit = parser.parseCompilationUnit(); + JCTree.JCCompilationUnit unit = parser.parseCompilationUnit(); unit.sourcefile = sjfo; Iterable> errorDiagnostics = Iterables.filter(diagnostics.getDiagnostics(), Formatter::errorDiagnostic); 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 f7be369f1..5ef7cb51a 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperTest.java @@ -60,19 +60,90 @@ public void textBlock() throws Exception { lines( "package com.mypackage;", "public class ReproBug {", - " private String myString;", - " private ReproBug() {", - " String str =", - " \"\"\"", - " " - + " {\"sourceEndpoint\":\"ri.something.1-1.object-internal.1\",\"targetEndpoint\":\"ri.some" - + "thing.1-1.object-internal.2\",\"typeId\":\"typeId\"}\"\"\";", - " myString = str;", - " }", + " 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\"}\"\"\";", + " myString = str;", + " }", "}"); assertThat(StringWrapper.wrap(100, input, new Formatter())).isEqualTo(input); } + // Test that whitespace handling on text block lines only removes spaces, not other control + // characters. + @Test + public void textBlockControlCharacter() throws Exception { + assumeTrue(Runtime.version().feature() >= 15); + // We want an actual control character in the Java source being formatted, not a unicode escape, + // i.e. the escape below doesn't need to be double-escaped. + String input = + lines( + "package p;", + "public class T {", + " String s =", + " \"\"\"", + " \u0007lorem", + " \u0007", + " ipsum", + " \"\"\";", + "}"); + String actual = StringWrapper.wrap(100, input, new Formatter()); + assertThat(actual).isEqualTo(input); + } + + @Test + public void textBlockTrailingWhitespace() 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); + } + + @Test + public void textBlockSpaceTabMix() throws Exception { + assumeTrue(Runtime.version().feature() >= 15); + String input = + lines( + "public class T {", + " String s =", + " \"\"\"", + " lorem", + " \tipsum", + " \"\"\";", + "}"); + 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'; } 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 9c18f0def..f401285bc 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 @@ -1,6 +1,43 @@ class RSLs { - String s = """ + String a = """ lorem ipsum """; + String b = """ + lorem + ipsum + """; + String c = """ + lorem + ipsum + """; + String d = """ + ipsum + """; + String e = """ + """; + String f = """ + ipsum"""; + String g = """ + lorem\ + ipsum + """; + String h = """ + lorem\ + ipsum\ + """; + String i = """ + lorem + + 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 +"""; } 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 9c18f0def..c6051d1fd 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 @@ -1,6 +1,54 @@ class RSLs { - String s = """ - lorem + String a = + """ + lorem + ipsum + """; + String b = + """ + lorem ipsum - """; + """; + String c = + """ + lorem + ipsum + """; + String d = + """ + ipsum + """; + String e = + """ + """; + String f = + """ + ipsum"""; + String g = + """ + lorem\ + ipsum + """; + String h = + """ + lorem\ + ipsum\ + """; + String i = + """ + lorem + + 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 +"""; } From e946e82801eb5bbd52bea00355ba20450bc0725c Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 23 Feb 2024 08:14:44 -0800 Subject: [PATCH 2/9] Work around a crash on comments inside string template arguments PiperOrigin-RevId: 609732753 --- .../googlejavaformat/java/JavacTokens.java | 62 +++++++++++++++++-- .../java/FormatterIntegrationTest.java | 3 +- .../java/testdata/TextBlockTemplates.input | 11 ++++ .../java/testdata/TextBlockTemplates.output | 14 +++++ 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.input create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.output diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java index da77cf82c..690c924f1 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java @@ -15,6 +15,7 @@ package com.google.googlejavaformat.java; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkElementIndex; import static java.util.Arrays.stream; import com.google.common.collect.ImmutableList; @@ -28,6 +29,7 @@ import com.sun.tools.javac.parser.Tokens.TokenKind; import com.sun.tools.javac.parser.UnicodeReader; import com.sun.tools.javac.util.Context; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -35,9 +37,10 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; /** A wrapper around javac's lexer. */ -class JavacTokens { +final class JavacTokens { /** The lexer eats terminal comments, so feed it one we don't care about. */ // TODO(b/33103797): fix javac and remove the work-around @@ -51,6 +54,8 @@ static class RawTok { private final int endPos; RawTok(String stringVal, TokenKind kind, int pos, int endPos) { + checkElementIndex(pos, endPos, "pos"); + checkArgument(pos < endPos, "expected pos (%s) < endPos (%s)", pos, endPos); this.stringVal = stringVal; this.kind = kind; this.pos = pos; @@ -136,13 +141,30 @@ public static ImmutableList getTokens( int last = 0; for (Token t : javacTokens) { if (t.comments != null) { + // javac accumulates comments in reverse order for (Comment c : Lists.reverse(t.comments)) { - if (last < c.getSourcePos(0)) { - tokens.add(new RawTok(null, null, last, c.getSourcePos(0))); + int pos = c.getSourcePos(0); + int length; + if (pos == -1) { + // We've found a comment whose position hasn't been recorded. Deduce its position as the + // first `/` character after the end of the previous token. + // + // javac creates a new JavaTokenizer to process string template arguments, so + // CommentSavingTokenizer doesn't get a chance to preprocess those comments and save + // their text and positions. + // + // TODO: consider always using this approach once the minimum supported JDK is 16 and + // we can assume BasicComment#getRawCharacters is always available. + pos = source.indexOf('/', last); + length = CommentSavingTokenizer.commentLength(c); + } else { + length = c.getText().length(); } - tokens.add( - new RawTok(null, null, c.getSourcePos(0), c.getSourcePos(0) + c.getText().length())); - last = c.getSourcePos(0) + c.getText().length(); + if (last < pos) { + tokens.add(new RawTok(null, null, last, pos)); + } + tokens.add(new RawTok(null, null, pos, pos + length)); + last = pos + length; } } if (stopTokens.contains(t.kind)) { @@ -181,6 +203,32 @@ public static ImmutableList getTokens( /** A {@link JavaTokenizer} that saves comments. */ static class CommentSavingTokenizer extends JavaTokenizer { + + private static final Method GET_RAW_CHARACTERS_METHOD = getRawCharactersMethod(); + + private static @Nullable Method getRawCharactersMethod() { + try { + // This is a method in PositionTrackingReader, but that class is not public. + return BasicComment.class.getMethod("getRawCharacters"); + } catch (NoSuchMethodException e) { + return null; + } + } + + static int commentLength(Comment comment) { + if (comment instanceof BasicComment && GET_RAW_CHARACTERS_METHOD != null) { + // If we've seen a BasicComment instead of a CommentWithTextAndPosition, getText() will + // be null, so we deduce the length using getRawCharacters. See also the comment at the + // usage of this method in getTokens. + try { + return ((char[]) GET_RAW_CHARACTERS_METHOD.invoke(((BasicComment) comment))).length; + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } + return comment.getText().length(); + } + CommentSavingTokenizer(ScannerFactory fac, char[] buffer, int length) { super(fac, buffer, length); } @@ -288,4 +336,6 @@ protected AccessibleReader(ScannerFactory fac, char[] buffer, int length) { super(fac, buffer, length); } } + + private JavacTokens() {} } 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 15a6e75f9..d31218e28 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java @@ -63,7 +63,8 @@ public class FormatterIntegrationTest { "I981", "StringTemplate", "I1020", - "I1037") + "I1037", + "TextBlockTemplates") .build(); @Parameters(name = "{index}: {0}") diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.input new file mode 100644 index 000000000..2d00d3300 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.input @@ -0,0 +1,11 @@ +abstract class RSLs { + abstract String f(); + + String a = STR.""" + lorem + foo\{ /** a method */ f( // line comment + /* TODO */ + )}bar + ipsum + """; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.output new file mode 100644 index 000000000..36736724f --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TextBlockTemplates.output @@ -0,0 +1,14 @@ +abstract class RSLs { + abstract String f(); + + String a = + STR.""" + lorem + foo\{ + /** a method */ + f( // line comment + /* TODO */ + )}bar + ipsum + """; +} From d8216e8c1d15d225223a308c04612d4a5eb60c09 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 23 Feb 2024 10:02:43 -0800 Subject: [PATCH 3/9] Migrate google-java-format to JSpecify PiperOrigin-RevId: 609760882 --- core/pom.xml | 4 ++-- .../google/googlejavaformat/java/FormatFileCallable.java | 2 +- .../google/googlejavaformat/java/JavaInputAstVisitor.java | 4 ++-- .../com/google/googlejavaformat/java/JavacTokens.java | 2 +- .../com/google/googlejavaformat/java/StringWrapper.java | 2 +- .../googlejavaformat/java/filer/FormattingFiler.java | 2 +- .../java/filer/FormattingJavaFileObject.java | 2 +- .../googlejavaformat/java/testdata/TypeAnnotations.input | 2 +- .../googlejavaformat/java/testdata/TypeAnnotations.output | 2 +- .../intellij/GoogleJavaFormatConfigurable.java | 2 +- .../intellij/GoogleJavaFormatSettings.java | 2 +- pom.xml | 8 ++++---- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 7b4a1d1f2..c3753a150 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -42,8 +42,8 @@ - org.checkerframework - checker-qual + org.jspecify + jspecify true diff --git a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java index 3d68a23f3..7cec1fca8 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java +++ b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java @@ -20,7 +20,7 @@ import com.google.common.collect.TreeRangeSet; import java.nio.file.Path; import java.util.concurrent.Callable; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Encapsulates information about a file to be formatted, including which parts of the file to 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 c23acbdb8..6dc82f40c 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -158,7 +158,7 @@ import java.util.regex.Pattern; import java.util.stream.Stream; import javax.lang.model.element.Name; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An AST visitor that builds a stream of {@link Op}s to format from the given {@link @@ -287,7 +287,7 @@ private static ImmutableSetMultimap typeAnnotations() { ImmutableList.of( "org.jspecify.annotations.NonNull", "org.jspecify.annotations.Nullable", - "org.jspecify.nullness.Nullable", + "org.jspecify.annotations.Nullable", "org.checkerframework.checker.nullness.qual.NonNull", "org.checkerframework.checker.nullness.qual.Nullable")) { String simpleName = annotation.substring(annotation.lastIndexOf('.') + 1); diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java index 690c924f1..986db3912 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java @@ -37,7 +37,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** A wrapper around javac's lexer. */ final class JavacTokens { 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 81714db64..6814054a2 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -61,7 +61,7 @@ import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardLocation; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** Wraps string literals that exceed the column limit. */ public final class StringWrapper { 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 d38d84eca..ebdc8dfec 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 @@ -24,7 +24,7 @@ import javax.tools.FileObject; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A decorating {@link Filer} implementation which formats Java source files with a {@link diff --git a/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingJavaFileObject.java b/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingJavaFileObject.java index c8ae80769..b65258b1a 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingJavaFileObject.java +++ b/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingJavaFileObject.java @@ -26,7 +26,7 @@ import javax.tools.Diagnostic; import javax.tools.ForwardingJavaFileObject; import javax.tools.JavaFileObject; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** A {@link JavaFileObject} decorator which {@linkplain Formatter formats} source code. */ final class FormattingJavaFileObject extends ForwardingJavaFileObject { diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.input index ddaa8f1ad..d6483bbf2 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.input @@ -1,4 +1,4 @@ -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; class TypeAnnotations { diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.output index 8dd5d4efc..6d0c3017d 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.output @@ -1,4 +1,4 @@ -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; class TypeAnnotations { 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 2f34b7471..cbb0ee10b 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 @@ -31,9 +31,9 @@ import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; -import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; class GoogleJavaFormatConfigurable extends BaseConfigurable implements SearchableConfigurable { diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java index ee187c00d..572d9ba6d 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java @@ -21,8 +21,8 @@ import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.project.Project; -import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; @State( name = "GoogleJavaFormatSettings", diff --git a/pom.xml b/pom.xml index 2b426cee1..152fdfde2 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ 1.8 32.1.3-jre 1.4.0 - 3.21.2 + 0.3.0 2.16 1.9 1.0.1 @@ -107,9 +107,9 @@ - org.checkerframework - checker-qual - ${checker.version} + org.jspecify + jspecify + ${jspecify.version} com.google.errorprone From 29b7f93237fbda5f4bbada2cf16e97c9917ca59d Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Mon, 26 Feb 2024 17:29:07 +0100 Subject: [PATCH 4/9] Remove an un-used portion of CI YAML --- .github/workflows/ci.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab0ebf024..9dca726b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,15 +65,6 @@ jobs: java-version: ${{ matrix.java }} 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 From 32d14f0e5b0413e9f64dc2f9116d921ed7adec99 Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Mon, 26 Feb 2024 16:46:20 +0000 Subject: [PATCH 5/9] Build native on Ubuntu 20.04 instead of latest 22.04 (re. #1072). This MAY (?) help re. libc.so.6 (GLIBC_2.34) for https://github.com/google/google-java-format/issues/1072. --- .github/workflows/ci.yml | 5 ++++- .github/workflows/release.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9dca726b1..c623efe38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,10 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + # Use "oldest" available ubuntu-* instead of -latest, + # see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories; + # due to https://github.com/google/google-java-format/issues/1072. + os: [ubuntu-20.04, macos-latest, windows-latest] runs-on: ${{ matrix.os }} continue-on-error: true steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f309026b..e8f50b503 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,7 +86,10 @@ jobs: needs: build-maven-jars strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + # Use "oldest" available ubuntu-* instead of -latest, + # see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories; + # due to https://github.com/google/google-java-format/issues/1072. + os: [ubuntu-20.04, 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' || '' }} From cea3782400689132f3f84178a8848b9d7cb4a9e5 Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Mon, 26 Feb 2024 18:04:13 +0100 Subject: [PATCH 6/9] Update release.yml --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8f50b503..a390df249 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,7 +91,8 @@ jobs: # due to https://github.com/google/google-java-format/issues/1072. os: [ubuntu-20.04, macos-latest, windows-latest] env: - SUFFIX: ${{fromJson('{"ubuntu-latest":"linux-x86-64", "macos-latest":"darwin-arm64", "windows-latest":"windows-x86-64"}')[matrix.os]}} + # NB: Must keep the keys in this inline JSON below in line with the os: above! + SUFFIX: ${{fromJson('{"ubuntu-20.04":"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" From 74c510a03f218bdae157ebf06f030afd98eafc39 Mon Sep 17 00:00:00 2001 From: Michael Plump Date: Tue, 27 Feb 2024 10:40:25 -0800 Subject: [PATCH 7/9] Update the IntelliJ plugin to gfj 1.20.0. PiperOrigin-RevId: 610801461 --- idea_plugin/build.gradle.kts | 6 +++--- .../intellij/GoogleJavaFormatConfigurable.java | 2 +- .../googlejavaformat/intellij/GoogleJavaFormatSettings.java | 2 +- idea_plugin/src/main/resources/META-INF/plugin.xml | 2 ++ 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/idea_plugin/build.gradle.kts b/idea_plugin/build.gradle.kts index 2247893c1..0c3ae944e 100644 --- a/idea_plugin/build.gradle.kts +++ b/idea_plugin/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ -plugins { id("org.jetbrains.intellij") version "1.16.1" } +plugins { id("org.jetbrains.intellij") version "1.17.2" } apply(plugin = "org.jetbrains.intellij") @@ -22,7 +22,7 @@ apply(plugin = "java") repositories { mavenCentral() } -val googleJavaFormatVersion = "1.19.2" +val googleJavaFormatVersion = "1.20.0" java { sourceCompatibility = JavaVersion.VERSION_11 @@ -62,5 +62,5 @@ tasks { dependencies { implementation("com.google.googlejavaformat:google-java-format:${googleJavaFormatVersion}") testImplementation("junit:junit:4.13.2") - testImplementation("com.google.truth:truth:1.2.0") + testImplementation("com.google.truth:truth:1.4.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 cbb0ee10b..759decc0f 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 @@ -33,7 +33,7 @@ import javax.swing.JPanel; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; -import org.jspecify.annotations.Nullable; +import org.jetbrains.annotations.Nullable; class GoogleJavaFormatConfigurable extends BaseConfigurable implements SearchableConfigurable { diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java index 572d9ba6d..4546f68a3 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java @@ -22,7 +22,7 @@ import com.intellij.openapi.components.Storage; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; -import org.jspecify.annotations.Nullable; +import org.jetbrains.annotations.Nullable; @State( name = "GoogleJavaFormatSettings", diff --git a/idea_plugin/src/main/resources/META-INF/plugin.xml b/idea_plugin/src/main/resources/META-INF/plugin.xml index a5600b00d..c426a1ff1 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.20.0.0
+
Updated to use google-java-format 1.20.0.
1.19.2.0
Updated to use google-java-format 1.19.2.
1.17.0.0
From f20d393babf0fb489a90a1d7913d7a43e5f8cff1 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 29 Feb 2024 13:06:44 -0800 Subject: [PATCH 8/9] Bump the version number for native image builds Fixes https://github.com/google/google-java-format/issues/1068 --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a390df249..d2ce2beae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,6 +105,8 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} native-image-job-reports: "true" cache: "maven" + - name: Bump Version Number + run: mvn --no-transfer-progress versions:set versions:commit -DnewVersion="${{ github.event.inputs.version }}" - name: "Native" run: mvn -Pnative -DskipTests package -pl core -am - name: "Move outputs" From ee72f3ad23868d15c48fbf5ad2121e85a338fd28 Mon Sep 17 00:00:00 2001 From: cushon Date: Tue, 5 Mar 2024 23:27:29 +0000 Subject: [PATCH 9/9] Release google-java-format 1.21.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 c3753a150..f09e0dbaa 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,7 +22,7 @@ com.google.googlejavaformat google-java-format-parent - HEAD-SNAPSHOT + 1.21.0 google-java-format diff --git a/eclipse_plugin/META-INF/MANIFEST.MF b/eclipse_plugin/META-INF/MANIFEST.MF index 913245393..b7d60b267 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.21.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 9e6acdac0..bb96fe428 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.21.0 Google Java Format Plugin for Eclipse 4.5+ diff --git a/pom.xml b/pom.xml index 152fdfde2..c52fab4cf 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ com.google.googlejavaformat google-java-format-parent pom - HEAD-SNAPSHOT + 1.21.0 core