Skip to content

Commit 009154c

Browse files
dansanduleaciamdanfox
authored andcommitted
Improve string reflowing (#35)
* Better displayName * StringWrapper formats & wraps once more, getting rid of hack from Level * fix MainTest * Add generated changelog entries * better
1 parent 16bb99f commit 009154c

File tree

6 files changed

+46
-20
lines changed

6 files changed

+46
-20
lines changed

changelog/@unreleased/pr-35.v2.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type: fix
2+
fix:
3+
description: Reflow long strings more thoroughly, taking into account that once
4+
a string is broken, its first chunk might fit on the starting line and thus it
5+
needs to be reflowed again.
6+
links:
7+
- https://github.com/palantir/palantir-java-format/pull/35

palantir-java-format/src/main/java/com/palantir/javaformat/doc/Level.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public State computeBreaks(CommentsHelper commentsHelper, int maxWidth, State st
195195

196196
// Allow long strings to stay on the same line, expecting that StringWrapper will
197197
// reflow them later.
198-
if (prefixFits || isSingleString()) {
198+
if (prefixFits) {
199199
State newState = state.withNoIndent();
200200
if (breakBehaviour == BreakBehaviour.BREAK_ONLY_IF_INNER_LEVELS_THEN_FIT_ON_ONE_LINE) {
201201
newState = newState.withIndentIncrementedBy(plusIndent);
@@ -207,18 +207,6 @@ public State computeBreaks(CommentsHelper commentsHelper, int maxWidth, State st
207207
return state.updateAfterLevel(broken);
208208
}
209209

210-
/**
211-
* Trick to cooperate with StringWrapper. If the value is a single element (e.g. a String), then allow it to stay on
212-
* this line, and rely on the StringWrapper to break it accordingly.
213-
*
214-
* <p>This is to prevent StringWrapperIntegrationTest#idemponent from breaking.
215-
*/
216-
private boolean isSingleString() {
217-
return !this.docs.stream().anyMatch(doc -> doc instanceof Level)
218-
&& this.docs.stream().filter(doc -> doc instanceof Break).count() == 1
219-
&& getFlat().startsWith(" \"");
220-
}
221-
222210
private Optional<State> tryBreakLastLevel(
223211
CommentsHelper commentsHelper, int maxWidth, State state, boolean recursive) {
224212
if (docs.isEmpty() || !(getLast(docs) instanceof Level)) {

palantir-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.google.common.base.Strings;
2323
import com.google.common.base.Verify;
2424
import com.google.common.collect.ImmutableList;
25+
import com.google.common.collect.ImmutableSet;
2526
import com.google.common.collect.Iterables;
2627
import com.google.common.collect.Range;
2728
import com.google.common.collect.TreeRangeMap;
@@ -35,6 +36,7 @@
3536
import java.util.Iterator;
3637
import java.util.List;
3738
import java.util.Map;
39+
import java.util.Map.Entry;
3840
import java.util.concurrent.atomic.AtomicBoolean;
3941
import java.util.stream.Stream;
4042
import org.openjdk.javax.tools.Diagnostic;
@@ -81,6 +83,14 @@ static String wrap(final int columnLimit, String input, Formatter formatter) thr
8183

8284
String result = applyReplacements(input, replacements);
8385

86+
// Format again, because broken strings might now fit on the first line in case of assignments
87+
String secondPass = formatter.formatSource(result, rangesAfterAppliedReplacements(replacements));
88+
89+
if (!secondPass.equals(result)) {
90+
replacements = getReflowReplacements(columnLimit, secondPass);
91+
result = applyReplacements(secondPass, replacements);
92+
}
93+
8494
{
8595
// We really don't want bugs in this pass to change the behaviour of programs we're
8696
// formatting, so check that the pretty-printed AST is the same before and after reformatting.
@@ -98,6 +108,26 @@ static String wrap(final int columnLimit, String input, Formatter formatter) thr
98108
return result;
99109
}
100110

111+
@SuppressWarnings("ResultOfMethodCallIgnored")
112+
private static ImmutableSet<Range<Integer>> rangesAfterAppliedReplacements(
113+
TreeRangeMap<Integer, String> replacements) {
114+
ImmutableSet.Builder<Range<Integer>> outputRanges = ImmutableSet.builder();
115+
int offset = 0;
116+
for (Entry<Range<Integer>, String> entry : replacements.asMapOfRanges().entrySet()) {
117+
Range<Integer> range = entry.getKey();
118+
String replacement = entry.getValue();
119+
120+
int lower = offset + range.lowerEndpoint();
121+
int upper = lower + replacement.length();
122+
outputRanges.add(Range.closedOpen(lower, upper));
123+
124+
int originalLength = range.upperEndpoint() - range.lowerEndpoint();
125+
int newLength = upper - lower;
126+
offset += newLength - originalLength;
127+
}
128+
return outputRanges.build();
129+
}
130+
101131
private static TreeRangeMap<Integer, String> getReflowReplacements(int columnLimit, final String input)
102132
throws FormatterException {
103133
JCTree.JCCompilationUnit unit = parse(input, /* allowStringFolding= */ false);
@@ -412,8 +442,7 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
412442
}
413443

414444
/** Applies replacements to the given string. */
415-
private static String applyReplacements(String javaInput, TreeRangeMap<Integer, String> replacementMap)
416-
throws FormatterException {
445+
private static String applyReplacements(String javaInput, TreeRangeMap<Integer, String> replacementMap) {
417446
// process in descending order so the replacement ranges aren't perturbed if any replacements
418447
// differ in size from the input
419448
Map<Range<Integer>, String> ranges = replacementMap.asDescendingMapOfRanges();

palantir-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,8 @@ public void reflowLongStrings() throws Exception {
478478
};
479479
String[] expected = {
480480
"class T {",
481-
" String s = \"one long incredibly unbroken sentence moving from topic to topic so that no" + " one had\"",
482-
" + \" a chance to interrupt\";",
481+
" String s = \"one long incredibly unbroken sentence moving from topic to topic so that no one had a\"",
482+
" + \" chance to interrupt\";",
483483
"}",
484484
"",
485485
};
@@ -503,7 +503,8 @@ public void noReflowLongStrings() throws Exception {
503503
};
504504
String[] expected = {
505505
"class T {",
506-
" String s = \"one long incredibly unbroken sentence moving from topic to topic so that no"
506+
" String s =",
507+
" \"one long incredibly unbroken sentence moving from topic to topic so that no"
507508
+ " one had a chance to interrupt\";",
508509
"}",
509510
"",

palantir-java-format/src/test/java/com/palantir/javaformat/java/StringWrapperIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class StringWrapperIntegrationTest {
3232

3333
private static FileBasedTests tests = new FileBasedTests(StringWrapperIntegrationTest.class);
3434

35-
@ParameterizedClass.Parameters(name = "{index}: {0}")
35+
@ParameterizedClass.Parameters(name = "{0}")
3636
public static List<Object[]> parameters() throws IOException {
3737
return tests.paramsAsNameInputOutput();
3838
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
class T {
2-
String s = "onelongincrediblyunbrokensentencemovingfromtopictotopicsothatnoonehadachancetointerrupt_____________________)_";
2+
String s =
3+
"onelongincrediblyunbrokensentencemovingfromtopictotopicsothatnoonehadachancetointerrupt_____________________)_";
34
}

0 commit comments

Comments
 (0)