diff --git a/javaparser-core-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java b/javaparser-core-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java index fef360925e..eb3d57d3bf 100644 --- a/javaparser-core-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java +++ b/javaparser-core-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java @@ -21,6 +21,7 @@ package com.github.javaparser.printer; +import com.github.javaparser.JavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; @@ -98,7 +99,6 @@ public void printUseTestVisitor() { @Test public void prettyColumnAlignParameters_enabled() { PrettyPrinterConfiguration config = new PrettyPrinterConfiguration() - .setIndent("\t") .setColumnAlignParameters(true); final String EOL = config.getEndOfLineCharacter(); @@ -106,13 +106,13 @@ public void prettyColumnAlignParameters_enabled() { String code = "class Example { void foo(Object arg0,Object arg1){ myMethod(1, 2, 3, 5, Object.class); } }"; String expected = "class Example {" + EOL + "" + EOL + - "\tvoid foo(Object arg0, Object arg1) {" + EOL + - "\t\tmyMethod(1," + EOL + - "\t\t 2," + EOL + - "\t\t 3," + EOL + - "\t\t 5," + EOL + - "\t\t Object.class);" + EOL + - "\t}" + EOL + + " void foo(Object arg0, Object arg1) {" + EOL + + " myMethod(1," + EOL + + " 2," + EOL + + " 3," + EOL + + " 5," + EOL + + " Object.class);" + EOL + + " }" + EOL + "}" + EOL + ""; @@ -139,7 +139,6 @@ public void prettyColumnAlignParameters_disabled() { @Test public void prettyAlignMethodCallChains_enabled() { PrettyPrinterConfiguration config = new PrettyPrinterConfiguration() - .setIndent("\t") .setColumnAlignFirstMethodChain(true); final String EOL = config.getEndOfLineCharacter(); @@ -147,13 +146,13 @@ public void prettyAlignMethodCallChains_enabled() { String code = "class Example { void foo() { IntStream.range(0, 10).filter(x -> x % 2 == 0).map(x -> x * IntStream.of(1,3,5,1).sum()).forEach(System.out::println); } }"; String expected = "class Example {" + EOL + "" + EOL + - "\tvoid foo() {" + EOL + - "\t\tIntStream.range(0, 10)" + EOL + - "\t\t .filter(x -> x % 2 == 0)" + EOL + - "\t\t .map(x -> x * IntStream.of(1, 3, 5, 1)" + EOL + - "\t\t .sum())" + EOL + - "\t\t .forEach(System.out::println);" + EOL + - "\t}" + EOL + + " void foo() {" + EOL + + " IntStream.range(0, 10)" + EOL + + " .filter(x -> x % 2 == 0)" + EOL + + " .map(x -> x * IntStream.of(1, 3, 5, 1)" + EOL + + " .sum())" + EOL + + " .forEach(System.out::println);" + EOL + + " }" + EOL + "}" + EOL + ""; @@ -203,4 +202,24 @@ public void printingInconsistentVariables() { assertEquals("double a, b;", fieldDeclaration.toString()); } + + @Test + public void prettyAlignMethodCallChainsIndentsArgumentsWithBlocksCorrectly() { + + CompilationUnit cu = JavaParser.parse("class Foo { void bar() { foo().bar().baz(() -> { boo().baa().bee(); }).bam(); } }"); + String printed = new PrettyPrinter(new PrettyPrinterConfiguration().setColumnAlignFirstMethodChain(true)) + .print(cu); + + assertEqualsNoEol("class Foo {\n" + + "\n" + + " void bar() {\n" + + " foo().bar()\n" + + " .baz(() -> {\n" + + " boo().baa()\n" + + " .bee();\n" + + " })\n" + + " .bam();\n" + + " }\n" + + "}\n", printed); + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java b/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java index 8a1d6b5100..0d731fdd24 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java +++ b/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java @@ -21,7 +21,6 @@ package com.github.javaparser.printer; -import com.github.javaparser.Position; import com.github.javaparser.ast.*; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.comments.BlockComment; @@ -30,6 +29,7 @@ import com.github.javaparser.ast.comments.LineComment; import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.modules.*; +import com.github.javaparser.ast.nodeTypes.NodeWithName; import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments; import com.github.javaparser.ast.nodeTypes.NodeWithVariables; import com.github.javaparser.ast.stmt.*; @@ -42,9 +42,8 @@ import static com.github.javaparser.ast.Node.Parsedness.UNPARSABLE; import static com.github.javaparser.utils.PositionUtils.sortByBeginPosition; -import static com.github.javaparser.utils.Utils.isNullOrEmpty; -import static com.github.javaparser.utils.Utils.normalizeEolInTextBlock; -import static com.github.javaparser.utils.Utils.trimTrailingSpaces; +import static com.github.javaparser.utils.Utils.*; +import static java.util.Comparator.comparingInt; /** * Outputs the AST as formatted Java source code. @@ -54,35 +53,16 @@ public class PrettyPrintVisitor implements VoidVisitor { protected final PrettyPrinterConfiguration configuration; protected final SourcePrinter printer; - private Deque methodChainPositions = new LinkedList<>(); public PrettyPrintVisitor(PrettyPrinterConfiguration prettyPrinterConfiguration) { configuration = prettyPrinterConfiguration; printer = new SourcePrinter(configuration.getIndent(), configuration.getEndOfLineCharacter()); - pushMethodChainPosition(printer.getCursor()); // initialize a default position for methodChainPositions, it is expected by method #resetMethodChainPosition() } public String getSource() { return printer.getSource(); } - public void resetMethodChainPosition(Position position) { - this.methodChainPositions.pop(); - this.methodChainPositions.push(position); - } - - public void pushMethodChainPosition(Position position) { - this.methodChainPositions.push(position); - } - - public Position peekMethodChainPosition() { - return this.methodChainPositions.peek(); - } - - public Position popMethodChainPosition() { - return this.methodChainPositions.pop(); - } - private void printModifiers(final EnumSet modifiers) { if (modifiers.size() > 0) { printer.print(modifiers.stream().map(Modifier::asString).collect(Collectors.joining(" ")) + " "); @@ -152,7 +132,9 @@ private void printTypeParameters(final NodeList args, final Void private void printArguments(final NodeList args, final Void arg) { printer.print("("); - Position cursorRef = printer.getCursor(); + if (configuration.isColumnAlignParameters()) { + printer.indentTo(printer.getCursor().column); + } if (!isNullOrEmpty(args)) { for (final Iterator i = args.iterator(); i.hasNext(); ) { final Expression e = i.next(); @@ -160,7 +142,7 @@ private void printArguments(final NodeList args, final Void arg) { if (i.hasNext()) { printer.print(","); if (configuration.isColumnAlignParameters()) { - printer.wrapToColumn(cursorRef.column); + printer.println(); } else { printer.print(" "); } @@ -168,6 +150,9 @@ private void printArguments(final NodeList args, final Void arg) { } } printer.print(")"); + if (configuration.isColumnAlignParameters()) { + printer.unindent(); + } } private void printPrePostFixOptionalList(final NodeList args, final Void arg, String prefix, String separator, String postfix) { @@ -728,22 +713,23 @@ public void visit(final SuperExpr n, final Void arg) { @Override public void visit(final MethodCallExpr n, final Void arg) { printComment(n.getComment(), arg); - if (n.getScope().isPresent()) { - n.getScope().get().accept(this, arg); + n.getScope().ifPresent(scope -> { + scope.accept(this, arg); if (configuration.isColumnAlignFirstMethodChain()) { - if (!(n.getScope().get() instanceof MethodCallExpr) || (!((MethodCallExpr) n.getScope().get()).getScope().isPresent())) { - resetMethodChainPosition(printer.getCursor()); + if (!(scope instanceof MethodCallExpr) || !((MethodCallExpr) scope).getScope().isPresent()) { + printer.unindent(); + printer.indentToCursor(); } else { - printer.wrapToColumn(peekMethodChainPosition().column); + printer.println(); } } printer.print("."); - } + }); printTypeArgs(n, arg); n.getName().accept(this, arg); - pushMethodChainPosition(printer.getCursor()); + printer.duplicateIndent(); printArguments(n.getArguments(), arg); - popMethodChainPosition(); + printer.unindent(); } @Override @@ -1460,13 +1446,9 @@ public void visit(NodeList n, Void arg) { if (configuration.isOrderImports() && n.size() > 0 && n.get(0) instanceof ImportDeclaration) { //noinspection unchecked NodeList modifiableList = new NodeList<>(n); - modifiableList.sort((left, right) -> { - int sort = Integer.compare(left.isStatic() ? 0 : 1, right.isStatic() ? 0 : 1); - if (sort == 0) { - sort = left.getNameAsString().compareTo(right.getNameAsString()); - } - return sort; - }); + modifiableList.sort( + comparingInt((ImportDeclaration left) -> left.isStatic() ? 0 : 1) + .thenComparing(NodeWithName::getNameAsString)); for (Object node : modifiableList) { ((Node) node).accept(this, arg); } diff --git a/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrinterConfiguration.java b/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrinterConfiguration.java index 1c650d2ec3..651f3b0ca2 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrinterConfiguration.java +++ b/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrinterConfiguration.java @@ -37,19 +37,23 @@ public class PrettyPrinterConfiguration { private boolean printJavadoc = true; private boolean columnAlignParameters = false; private boolean columnAlignFirstMethodChain = false; - private String indent = " "; + private int indent = 4; private String endOfLineCharacter = EOL; private Function visitorFactory = PrettyPrintVisitor::new; private int maxEnumConstantsToAlignHorizontally = DEFAULT_MAX_ENUM_CONSTANTS_TO_ALIGN_HORIZONTALLY; public String getIndent() { - return indent; + StringBuilder indentString = new StringBuilder(); + for(int i=0; i indents = new LinkedList<>(); private final StringBuilder buf = new StringBuilder(); private Position cursor = new Position(1, 0); + private boolean indented=false; SourcePrinter(final String indentation, final String endOfLineCharacter) { this.indentation = indentation; - this.indentationLength = indentation.length(); this.endOfLineCharacter = endOfLineCharacter; + indents.push(""); } public SourcePrinter indent() { - level++; + String currentIndent = indents.peek(); + indents.push(currentIndent + indentation); + return this; + } + + public SourcePrinter indentTo(int column) { + StringBuilder newIndent = new StringBuilder(indents.peek()); + while (newIndent.length() < column) { + newIndent.append(' '); + } + indents.push(newIndent.toString()); return this; } public SourcePrinter unindent() { - level--; + indents.pop(); return this; } + private void append(String arg) { + buf.append(arg); + cursor = Position.pos(cursor.line, cursor.column + arg.length()); + } + private void makeIndent() { - for (int i = 0; i < level; i++) { - buf.append(indentation); - cursor = Position.pos(cursor.line, cursor.column + indentationLength); - } + append(indents.peek()); } /** * Append the source string passed as argument to the buffer. - * If this is being appended at the beginning of a line, performs indentation first. - *

+ * If this is being appended at the beginning of a line, performs indentSize first. + *

* The source line to be printed should not contain newline/carriage-return characters; * use {@link #println(String)} to automatically append a newline at the end of the source string. * If the source line passed as argument contains newline/carriage-return characters would - * impredictably affect a correct computation of the current {@link #getCursor()} position. - * - * @see SourcePrinter#println(String) + * impredictably affect a correct computation of the current {@link #getCursor()} position. + * * @param arg source line to be printed (should not contain newline/carriage-return characters) * @return this instance, for nesting calls to method as fluent interface + * @see SourcePrinter#println(String) */ public SourcePrinter print(final String arg) { if (!indented) { @@ -82,12 +96,12 @@ public SourcePrinter print(final String arg) { /** * Append the source string passed as argument to the buffer, then append a newline. - * If this is being appended at the beginning of a line, performs indentation first. - *

+ * If this is being appended at the beginning of a line, performs indentSize first. + *

* The source line to be printed should not contain newline/carriage-return characters. * If the source line passed as argument contains newline/carriage-return characters would - * impredictably affect a correct computation of the current {@link #getCursor()} position. - * + * impredictably affect a correct computation of the current {@link #getCursor()} position. + * * @param arg source line to be printed (should not contain newline/carriage-return characters) * @return this instance, for nesting calls to method as fluent interface */ @@ -99,7 +113,7 @@ public SourcePrinter println(final String arg) { /** * Append a newline to the buffer. - * + * * @return this instance, for nesting calls to method as fluent interface */ public SourcePrinter println() { @@ -111,33 +125,18 @@ public SourcePrinter println() { /** * Return the current cursor position (line, column) in the source printer buffer. - *

+ *

* Please notice in order to guarantee a correct computation of the cursor position, * this printer expect the contracts of the methods {@link #print(String)} and {@link #println(String)} * has been respected through all method calls, meaning the source string passed as argument to those method * calls did not contain newline/carriage-return characters. - * + * * @return the current cursor position (line, column). */ public Position getCursor() { return cursor; } - /** - * Performs a new line and indent, then prints enough space characters until aligned to the specified column. - * @param column the column to align to - */ - public void wrapToColumn(int column) { - println(); - if (!indented) { - makeIndent(); - indented = true; - } - while ( cursor.column < column ) { - print(" "); - } - } - public String getSource() { return buf.toString(); } @@ -150,4 +149,12 @@ public String toString() { public String normalizeEolInTextBlock(String content) { return Utils.normalizeEolInTextBlock(content, endOfLineCharacter); } + + public void indentToCursor() { + indentTo(cursor.column); + } + + public void duplicateIndent() { + indents.push(indents.peek()); + } }