diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java index efe1552ab..fc9526862 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java @@ -56,8 +56,25 @@ public final class StringWrapper { public static final String TEXT_BLOCK_DELIMITER = "\"\"\""; + /** + * How many times {@link #wrapOnce} may be re-run while it is still changing the source. The indentation chosen + * for a text block is derived from the layout around it, which this pass can itself move, so a single round is + * not always a fixed point and the formatter would not be idempotent. See + * #1343. + */ + private static final int MAX_ROUNDS = 5; + /** Reflows string literals in the given Java source code that extend past the given column limit. */ static String wrap(final int columnLimit, String input, Formatter formatter) throws FormatterException { + String result = wrapOnce(columnLimit, input, formatter); + for (int round = 1; round < MAX_ROUNDS && !result.equals(input); round++) { + input = result; + result = wrapOnce(columnLimit, input, formatter); + } + return result; + } + + private static String wrapOnce(final int columnLimit, String input, Formatter formatter) throws FormatterException { if (!needWrapping(columnLimit, input)) { // fast path return input; diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-argument.input b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-argument.input new file mode 100644 index 000000000..ceaabe7d1 --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-argument.input @@ -0,0 +1,8 @@ +class T { + void f() { + LOGGER.warning(String.format(""" + @Gauge is configured on a bean %s that is neither ApplicationScoped nor \ + Singleton. This is most likely a bug. You may set 'metrics.warn-dependent' \ + configuration option to 'false' to remove this warning.""", clazz.getName())); + } +} diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-argument.output b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-argument.output new file mode 100644 index 000000000..f3be4f87c --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-argument.output @@ -0,0 +1,9 @@ +class T { + void f() { + LOGGER.warning(String.format(""" + @Gauge is configured on a bean %s that is neither ApplicationScoped nor \ + Singleton. This is most likely a bug. You may set 'metrics.warn-dependent' \ + configuration option to 'false' to remove this warning.\ + """, clazz.getName())); + } +} diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-ternary.input b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-ternary.input new file mode 100644 index 000000000..323fb518c --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-ternary.input @@ -0,0 +1,11 @@ +class TernaryBug { + boolean flag = true; + + String a = flag + ? """ + yes + """ + : """ + no + """; +} diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-ternary.output b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-ternary.output new file mode 100644 index 000000000..967170e72 --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-34-text-block-ternary.output @@ -0,0 +1,9 @@ +class TernaryBug { + boolean flag = true; + + String a = flag ? """ + yes + """ : """ + no + """; +} diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/palantir-issue-1343-text-block-indent-stable.input b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/palantir-issue-1343-text-block-indent-stable.input new file mode 100644 index 000000000..162408a62 --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/palantir-issue-1343-text-block-indent-stable.input @@ -0,0 +1,35 @@ +class PalantirIssue1343TextBlockIndentStable { + String[][] withCommentFirstLine = { + { + """ + // tmp fix + @Deprecated + class Test {} + """ + }, + { + """ + + @Deprecated + class Test {} + """ + } + }; + + String[][] blankLineInTheMiddle = { + { + """ + import java.util.List; + + @Deprecated + class Test {} + """ + }, + { + """ + + class Test {} + """ + } + }; +} diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/palantir-issue-1343-text-block-indent-stable.output b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/palantir-issue-1343-text-block-indent-stable.output new file mode 100644 index 000000000..30b3a9625 --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/palantir-issue-1343-text-block-indent-stable.output @@ -0,0 +1,21 @@ +class PalantirIssue1343TextBlockIndentStable { + String[][] withCommentFirstLine = {{""" + // tmp fix + @Deprecated + class Test {} + """}, {""" + + @Deprecated + class Test {} + """}}; + + String[][] blankLineInTheMiddle = {{""" + import java.util.List; + + @Deprecated + class Test {} + """}, {""" + + class Test {} + """}}; +}