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> {
protected final PrettyPrinterConfiguration configuration;
protected final SourcePrinter printer;
private Deque<Position> 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<Modifier> modifiers) {
if (modifiers.size() > 0) {
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);
if (configuration.isColumnAlignFirstMethodChain()) {
if (!(n.getScope().get() instanceof MethodCallExpr) || (!((MethodCallExpr) n.getScope().get()).getScope().isPresent())) {
printer.resetMethodChainPosition(printer.getCursor());
resetMethodChainPosition(printer.getCursor());
} else {
printer.wrapToColumn(printer.peekMethodChainPosition().column);
printer.wrapToColumn(peekMethodChainPosition().column);
}
}
printer.print(".");
}
printTypeArgs(n, arg);
n.getName().accept(this, arg);
printer.pushMethodChainPosition(printer.getCursor());
pushMethodChainPosition(printer.getCursor());
printArguments(n.getArguments(), arg);
printer.popMethodChainPosition();
popMethodChainPosition();
}

@Override
Expand Down Expand Up @@ -1348,10 +1367,15 @@ public void visit(final BlockComment n, final Void arg) {
if (configuration.isIgnoreComments()) {
return;
}
printer
.print("/*")
.print(normalizeEolInTextBlock(n.getContent(), configuration.getEndOfLineCharacter()))
.println("*/");
final String commentContent = normalizeEolInTextBlock(n.getContent(), configuration.getEndOfLineCharacter());
String[] lines = commentContent.split("\\R", -1); // as BlockComment should not be formatted, -1 to preserve any trailing empty line if present
printer.print("/*");
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
Expand Down
Expand Up @@ -21,30 +21,23 @@

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.utils.Utils;

public class SourcePrinter {

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

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

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

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

private void makeIndent() {
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) {
if (!indented) {
makeIndent();
indented = true;
}
bufAppend(arg);
buf.append(arg);
cursor = Position.pos(cursor.line, cursor.column + arg.length());
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) {
print(arg);
println();
return this;
}

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

private StringBuilder bufAppend(final String arg) {
updateCursor(arg);
return buf.append(arg);
}

private void updateCursor(String arg) {
String[] lines = NEWLINE_PATTERN.split(arg);
if ( lines.length == 0 ) {
cursor = Position.pos(cursor.line + 1, 0);
} 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());
}
}

/**
* Return the current cursor position (line, column) in the source printer buffer.
* <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)}
* 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;
}

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.
* @param column the column to align to
Expand Down

0 comments on commit e135adb

Please sign in to comment.