Skip to content

Commit

Permalink
Avoid SourcePrinter to calculate newline while updating cursor Position
Browse files Browse the repository at this point in the history
... by disallow passing strings with newlines to the sourceprinter.
  • Loading branch information
tarilabs committed Dec 21, 2017
1 parent 1717c5f commit e135adb
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 53 deletions.
Expand Up @@ -53,16 +53,35 @@
public class PrettyPrintVisitor implements VoidVisitor<Void> { public class PrettyPrintVisitor implements VoidVisitor<Void> {
protected final PrettyPrinterConfiguration configuration; protected final PrettyPrinterConfiguration configuration;
protected final SourcePrinter printer; protected final SourcePrinter printer;
private Deque<Position> methodChainPositions = new LinkedList<>();


public PrettyPrintVisitor(PrettyPrinterConfiguration prettyPrinterConfiguration) { public PrettyPrintVisitor(PrettyPrinterConfiguration prettyPrinterConfiguration) {
configuration = prettyPrinterConfiguration; configuration = prettyPrinterConfiguration;
printer = new SourcePrinter(configuration.getIndent(), configuration.getEndOfLineCharacter()); 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() { public String getSource() {
return printer.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<Modifier> modifiers) { private void printModifiers(final EnumSet<Modifier> modifiers) {
if (modifiers.size() > 0) { if (modifiers.size() > 0) {
printer.print(modifiers.stream().map(Modifier::asString).collect(Collectors.joining(" ")) + " "); printer.print(modifiers.stream().map(Modifier::asString).collect(Collectors.joining(" ")) + " ");
Expand Down Expand Up @@ -700,18 +719,18 @@ public void visit(final MethodCallExpr n, final Void arg) {
n.getScope().get().accept(this, arg); n.getScope().get().accept(this, arg);
if (configuration.isColumnAlignFirstMethodChain()) { if (configuration.isColumnAlignFirstMethodChain()) {
if (!(n.getScope().get() instanceof MethodCallExpr) || (!((MethodCallExpr) n.getScope().get()).getScope().isPresent())) { if (!(n.getScope().get() instanceof MethodCallExpr) || (!((MethodCallExpr) n.getScope().get()).getScope().isPresent())) {
printer.resetMethodChainPosition(printer.getCursor()); resetMethodChainPosition(printer.getCursor());
} else { } else {
printer.wrapToColumn(printer.peekMethodChainPosition().column); printer.wrapToColumn(peekMethodChainPosition().column);
} }
} }
printer.print("."); printer.print(".");
} }
printTypeArgs(n, arg); printTypeArgs(n, arg);
n.getName().accept(this, arg); n.getName().accept(this, arg);
printer.pushMethodChainPosition(printer.getCursor()); pushMethodChainPosition(printer.getCursor());
printArguments(n.getArguments(), arg); printArguments(n.getArguments(), arg);
printer.popMethodChainPosition(); popMethodChainPosition();
} }


@Override @Override
Expand Down Expand Up @@ -1348,10 +1367,15 @@ public void visit(final BlockComment n, final Void arg) {
if (configuration.isIgnoreComments()) { if (configuration.isIgnoreComments()) {
return; return;
} }
printer final String commentContent = normalizeEolInTextBlock(n.getContent(), configuration.getEndOfLineCharacter());
.print("/*") String[] lines = commentContent.split("\\R", -1); // as BlockComment should not be formatted, -1 to preserve any trailing empty line if present
.print(normalizeEolInTextBlock(n.getContent(), configuration.getEndOfLineCharacter())) printer.print("/*");
.println("*/"); for (int i = 0; i < (lines.length - 1); i++) {
printer.print(lines[i]);
printer.print(configuration.getEndOfLineCharacter()); // Avoids introducing indentation in blockcomments. ie: do not use println() as it would trigger indentation at the next print call.
}
printer.print(lines[lines.length - 1]); // last line is not followed by a newline, and simply terminated with `*/`
printer.println("*/");
} }


@Override @Override
Expand Down
Expand Up @@ -21,30 +21,23 @@


package com.github.javaparser.printer; package com.github.javaparser.printer;


import java.text.Normalizer;
import java.util.Deque;
import java.util.LinkedList;
import java.util.regex.Pattern;

import com.github.javaparser.Position; import com.github.javaparser.Position;
import com.github.javaparser.utils.Utils; import com.github.javaparser.utils.Utils;


public class SourcePrinter { public class SourcePrinter {


private static final Pattern NEWLINE_PATTERN = Pattern.compile("\r\n|\r|\n");

private final String indentation; private final String indentation;
private final int indentationLength;
private final String endOfLineCharacter; private final String endOfLineCharacter;
private int level = 0; private int level = 0;
private boolean indented = false; private boolean indented = false;
private final StringBuilder buf = new StringBuilder(); private final StringBuilder buf = new StringBuilder();
private Position cursor = new Position(1, 0); private Position cursor = new Position(1, 0);
private Deque<Position> methodChainPositions = new LinkedList<>();


SourcePrinter(final String indentation, final String endOfLineCharacter) { SourcePrinter(final String indentation, final String endOfLineCharacter) {
this.indentation = indentation; this.indentation = indentation;
this.indentationLength = indentation.length();
this.endOfLineCharacter = endOfLineCharacter; this.endOfLineCharacter = endOfLineCharacter;
pushMethodChainPosition(cursor); // initialize a default position for methodChainPositions, it is expected by method #resetMethodChainPosition()
} }


public SourcePrinter indent() { public SourcePrinter indent() {
Expand All @@ -59,68 +52,77 @@ public SourcePrinter unindent() {


private void makeIndent() { private void makeIndent() {
for (int i = 0; i < level; i++) { for (int i = 0; i < level; i++) {
bufAppend(indentation); buf.append(indentation);
cursor = Position.pos(cursor.line, cursor.column + indentationLength);
} }
} }


/**
* Append the source string passed as argument to the buffer.
* If this is being appended at the beginning of a line, performs indentation first.
* <p>
* 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 {@link #println(String)}
* @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
*/
public SourcePrinter print(final String arg) { public SourcePrinter print(final String arg) {
if (!indented) { if (!indented) {
makeIndent(); makeIndent();
indented = true; indented = true;
} }
bufAppend(arg); buf.append(arg);
cursor = Position.pos(cursor.line, cursor.column + arg.length());
return this; return this;
} }


/**
* 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.
* <p>
* 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.
*
* @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
*/
public SourcePrinter println(final String arg) { public SourcePrinter println(final String arg) {
print(arg); print(arg);
println(); println();
return this; return this;
} }


/**
* Append a newline to the buffer.
*
* @return this instance, for nesting calls to method as fluent interface
*/
public SourcePrinter println() { public SourcePrinter println() {
bufAppend(endOfLineCharacter); buf.append(endOfLineCharacter);
cursor = Position.pos(cursor.line + 1, 0);
indented = false; indented = false;
return this; return this;
} }


private StringBuilder bufAppend(final String arg) { /**
updateCursor(arg); * Return the current cursor position (line, column) in the source printer buffer.
return buf.append(arg); * <p>
} * 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)}
private void updateCursor(String arg) { * has been respected through all method calls, meaning the source string passed as argument to those method
String[] lines = NEWLINE_PATTERN.split(arg); * calls did not contain newline/carriage-return characters.
if ( lines.length == 0 ) { *
cursor = Position.pos(cursor.line + 1, 0); * @return the current cursor position (line, column).
} else if ( lines.length == 1 ) { */
cursor = Position.pos(cursor.line, cursor.column + Normalizer.normalize(lines[0],Normalizer.Form.NFC).length() );
} else {
cursor = Position.pos(cursor.line + (lines.length -1), 0 + Normalizer.normalize(lines[lines.length-1],Normalizer.Form.NFC).length());
}
}

public Position getCursor() { public Position getCursor() {
return cursor; return cursor;
} }


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();
}

/** /**
* Performs a new line and indent, then prints enough space characters until aligned to the specified column. * Performs a new line and indent, then prints enough space characters until aligned to the specified column.
* @param column the column to align to * @param column the column to align to
Expand Down

0 comments on commit e135adb

Please sign in to comment.