Skip to content

Commit 8169884

Browse files
committed
cache parent indentation
1 parent ab9282c commit 8169884

File tree

1 file changed

+67
-49
lines changed

1 file changed

+67
-49
lines changed

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

Lines changed: 67 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.sun.source.tree.BinaryTree;
3131
import com.sun.source.tree.LiteralTree;
3232
import com.sun.source.tree.MemberSelectTree;
33+
import com.sun.source.tree.MethodInvocationTree;
3334
import com.sun.source.tree.Tree;
3435
import com.sun.source.tree.Tree.Kind;
3536
import com.sun.source.util.TreePath;
@@ -43,11 +44,12 @@
4344
import java.util.ArrayDeque;
4445
import java.util.ArrayList;
4546
import java.util.Deque;
47+
import java.util.HashMap;
4648
import java.util.Iterator;
4749
import java.util.List;
4850
import java.util.Map;
49-
import java.util.Optional;
5051
import java.util.concurrent.atomic.AtomicBoolean;
52+
import java.util.stream.Collectors;
5153

5254
/** Wraps string literals that exceed the column limit. */
5355
public final class StringWrapper {
@@ -193,6 +195,9 @@ public Void visitLiteral(LiteralTree literalTree, Void aVoid) {
193195
}
194196

195197
private void indentTextBlocks(TreeRangeMap<Integer, String> replacements, List<TreePath> textBlocks) {
198+
// pjf specific - compute textBlock's parents & store info about the indentation\
199+
Map<TreePath, String> textBlockToIndent = computeCustomTextBlocksIndent(textBlocks);
200+
196201
for (TreePath treePath : textBlocks) {
197202
Tree tree = treePath.getLeaf();
198203
int startPosition = getStartPosition(tree);
@@ -212,8 +217,9 @@ private void indentTextBlocks(TreeRangeMap<Integer, String> replacements, List<T
212217
.stripIndent();
213218
ImmutableList<String> lines = stripped.lines().collect(ImmutableList.toImmutableList());
214219

215-
String prefix = maybeComputeCustomPrefix(treePath)
216-
.orElseGet(() -> (lineStartPosition + startColumn + 4 > startPosition ? "" : " ".repeat(4))
220+
String prefix = textBlockToIndent.getOrDefault(
221+
treePath,
222+
(lineStartPosition + startColumn + 4 > startPosition ? "" : " ".repeat(4))
217223
+ " ".repeat(startColumn));
218224
StringBuilder output = new StringBuilder(initialLines.get(0));
219225
for (int i = 0; i < lines.size(); i++) {
@@ -244,39 +250,42 @@ private void indentTextBlocks(TreeRangeMap<Integer, String> replacements, List<T
244250
}
245251
}
246252

247-
private Optional<String> maybeComputeCustomPrefix(TreePath treePath) {
248-
if (treePath.getParentPath().getLeaf().getKind() == Kind.PLUS) {
249-
return Optional.of(computePrefixForForTreeKind(treePath));
250-
} else if (treePath.getParentPath().getLeaf().getKind() == Kind.METHOD_INVOCATION) {
251-
return Optional.of(computePrefixForForMethod(treePath));
252-
}
253-
return Optional.empty();
254-
}
255-
256-
private String computePrefixForForMethod(TreePath path) {
257-
List<Tree> allArguments =
258-
new ArrayList<>(((JCMethodInvocation) path.getParentPath().getLeaf()).getArguments());
259-
return computePrefixIndentation(path.getParentPath().getLeaf(), allArguments);
260-
}
261-
262-
private String computePrefixForForTreeKind(TreePath path) {
263-
while (path.getParentPath().getLeaf().getKind() == Kind.PLUS) {
264-
path = path.getParentPath();
265-
}
266-
ArrayDeque<Tree> todo = new ArrayDeque<>();
267-
todo.add(path.getLeaf());
268-
List<Tree> arguments = new ArrayList<>();
269-
while (!todo.isEmpty()) {
270-
Tree first = todo.removeFirst();
271-
if (first.getKind() == Tree.Kind.PLUS) {
272-
BinaryTree bt = (BinaryTree) first;
273-
todo.addFirst(bt.getRightOperand());
274-
todo.addFirst(bt.getLeftOperand());
275-
} else {
276-
arguments.add(first);
253+
/**
254+
* Pjf specific: When the AST needs to break a concantenation expression ('ONE'+'TWO') or the parameters of a method,
255+
* the indentation will be set to 8, in which case we need to align the text blocks to the correct indentation.
256+
* In order to compute the indentation value we need to do:
257+
* 1. for each textBlock find the enclosing block/parent (only for concat expressions/method invocations)
258+
* 2. for each parent, find the arguments/concatenated expressions and find the max indentation level
259+
* 3. store the mapping between the textBlock and the indentation level computed before.
260+
*/
261+
private Map<TreePath, String> computeCustomTextBlocksIndent(List<TreePath> textBlocks) {
262+
Map<Tree, String> parentToIndent = new HashMap<>();
263+
Map<TreePath, Tree> textBlockToParent = new HashMap<>();
264+
for (TreePath textBlock : textBlocks) {
265+
TreePath parentPath = textBlock.getParentPath();
266+
Tree parent = parentPath.getLeaf();
267+
if (parent instanceof MethodInvocationTree) {
268+
textBlockToParent.put(textBlock, parent);
269+
if (parentToIndent.containsKey(parent)) {
270+
continue;
271+
}
272+
List<Tree> allArguments = new ArrayList<>(((JCMethodInvocation) parent).getArguments());
273+
parentToIndent.put(parent, computePrefixIndentation(parent, allArguments));
274+
} else if (parent.getKind() == Kind.PLUS) {
275+
while (parentPath.getParentPath().getLeaf().getKind() == Kind.PLUS) {
276+
parentPath = parentPath.getParentPath();
277+
}
278+
parent = parentPath.getLeaf();
279+
textBlockToParent.put(textBlock, parent);
280+
if (parentToIndent.containsKey(parent)) {
281+
continue;
282+
}
283+
parentToIndent.put(parent, computePrefixIndentation(parent, flattenExpressionTree(parent)));
277284
}
278285
}
279-
return computePrefixIndentation(path.getParentPath().getLeaf(), arguments);
286+
287+
return textBlockToParent.entrySet().stream()
288+
.collect(Collectors.toMap(Map.Entry::getKey, e -> parentToIndent.get(e.getValue())));
280289
}
281290

282291
private String computePrefixIndentation(Tree parentPath, List<Tree> childExpressions) {
@@ -499,21 +508,7 @@ private static String reflow(
499508
*/
500509
private static List<Tree> flatten(
501510
String input, JCTree.JCCompilationUnit unit, TreePath path, TreePath parent, AtomicBoolean firstInChain) {
502-
List<Tree> flat = new ArrayList<>();
503-
504-
// flatten the expression tree with a pre-order traversal
505-
ArrayDeque<Tree> todo = new ArrayDeque<>();
506-
todo.add(parent.getLeaf());
507-
while (!todo.isEmpty()) {
508-
Tree first = todo.removeFirst();
509-
if (first.getKind() == Tree.Kind.PLUS) {
510-
BinaryTree bt = (BinaryTree) first;
511-
todo.addFirst(bt.getRightOperand());
512-
todo.addFirst(bt.getLeftOperand());
513-
} else {
514-
flat.add(first);
515-
}
516-
}
511+
List<Tree> flat = flattenExpressionTree(parent.getLeaf());
517512

518513
int idx = flat.indexOf(path.getLeaf());
519514
Verify.verify(idx != -1);
@@ -536,6 +531,29 @@ && noComments(input, unit, flat.get(endIdx - 1), flat.get(endIdx))) {
536531
return ImmutableList.copyOf(flat.subList(startIdx, endIdx));
537532
}
538533

534+
/**
535+
* Returns the flatten expression tree with a pre-order traversal
536+
* @param parent a {@link com.sun.tools.javac.tree.JCTree.JCExpression}
537+
* @return the list of concatenated parameters in order.
538+
*/
539+
private static List<Tree> flattenExpressionTree(Tree parent) {
540+
// flatten the expression tree with a pre-order traversal
541+
List<Tree> flat = new ArrayList<>();
542+
ArrayDeque<Tree> todo = new ArrayDeque<>();
543+
todo.add(parent);
544+
while (!todo.isEmpty()) {
545+
Tree first = todo.removeFirst();
546+
if (first.getKind() == Tree.Kind.PLUS) {
547+
BinaryTree bt = (BinaryTree) first;
548+
todo.addFirst(bt.getRightOperand());
549+
todo.addFirst(bt.getLeftOperand());
550+
} else {
551+
flat.add(first);
552+
}
553+
}
554+
return flat;
555+
}
556+
539557
private static boolean noComments(String input, JCTree.JCCompilationUnit unit, Tree one, Tree two) {
540558
return STRING_CONCAT_DELIMITER.matchesAllOf(
541559
input.subSequence(getEndPosition(unit, one), getStartPosition(two)));

0 commit comments

Comments
 (0)