org.openrewrite.python.internal.PythonPrinter Maven / Gradle / Ivy
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.python.internal;
import lombok.AllArgsConstructor;
import org.openrewrite.Cursor;
import org.openrewrite.PrintOutputCapture;
import org.openrewrite.Tree;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.marker.OmitParentheses;
import org.openrewrite.java.tree.*;
import org.openrewrite.java.tree.J.Import;
import org.openrewrite.java.tree.Space.Location;
import org.openrewrite.marker.Marker;
import org.openrewrite.marker.Markers;
import org.openrewrite.python.PythonVisitor;
import org.openrewrite.python.marker.*;
import org.openrewrite.python.tree.*;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.UnaryOperator;
import static java.util.Objects.requireNonNull;
import static org.openrewrite.python.marker.GroupedStatement.StatementGroup;
import static org.openrewrite.python.tree.PySpace.reindent;
public class PythonPrinter
extends PythonVisitor> {
@lombok.experimental.Delegate
final AdaptedMethods pyMethods = new AdaptedMethods<>(new PythonPrinterAdapter<>(
this::visitSpace,
PyLeftPadded.Location::getBeforeLocation,
PyRightPadded.Location::getAfterLocation,
PyContainer.Location::getBeforeLocation,
PyContainer.Location::getElementLocation
));
@lombok.experimental.Delegate
final AdaptedMethods jMethods = new AdaptedMethods<>(new PythonPrinterAdapter<>(
this::visitSpace,
JLeftPadded.Location::getBeforeLocation,
JRightPadded.Location::getAfterLocation,
JContainer.Location::getBeforeLocation,
JContainer.Location::getElementLocation
));
@AllArgsConstructor
private class AdaptedMethods<
TTree extends J,
TLoc,
TLeftLoc,
TRtLoc,
TContLoc> {
private PythonPrinterAdapter adapter;
void visitSpace(Space prefix, TLoc loc, PrintOutputCapture p) {
adapter.spaceVisitor.visitSpace(prefix, loc, p);
}
public void beforeSyntax(J tree, TLoc loc, PrintOutputCapture
p) {
beforeSyntax(tree.getPrefix(), tree.getMarkers(), loc, p);
}
public void beforeSyntax(
Space prefix,
Markers markers,
@Nullable TLoc loc,
PrintOutputCapture
p
) {
for (Marker marker : markers.getMarkers()) {
p.append(p.getMarkerPrinter().beforePrefix(marker, new Cursor(getCursor(), marker), PYTHON_MARKER_WRAPPER));
}
if (loc != null) {
visitSpace(prefix, loc, p);
}
visitMarkers(markers, p);
for (Marker marker : markers.getMarkers()) {
p.append(p.getMarkerPrinter().beforeSyntax(marker, new Cursor(getCursor(), marker), PYTHON_MARKER_WRAPPER));
}
}
public void visitRightPadded(
List extends JRightPadded extends J>> nodes,
TRtLoc loc,
String suffixBetween,
PrintOutputCapture
p
) {
for (int i = 0; i < nodes.size(); i++) {
JRightPadded extends J> node = nodes.get(i);
visit(node.getElement(), p);
visitSpace(node.getAfter(), adapter.getAfterLocation.apply(loc), p);
if (i < nodes.size() - 1) {
p.append(suffixBetween);
}
}
}
public void visitRightPadded(@Nullable JRightPadded extends J> rightPadded, TRtLoc loc, @Nullable String suffix, PrintOutputCapture
p) {
if (rightPadded != null) {
beforeSyntax(Space.EMPTY, rightPadded.getMarkers(), null, p);
visit(rightPadded.getElement(), p);
afterSyntax(rightPadded.getMarkers(), p);
visitSpace(rightPadded.getAfter(), adapter.getAfterLocation.apply(loc), p);
if (suffix != null) {
p.append(suffix);
}
}
}
public void visitLeftPadded(@Nullable String prefix, @Nullable JLeftPadded extends J> leftPadded, TLeftLoc loc, PrintOutputCapture
p) {
if (leftPadded != null) {
beforeSyntax(leftPadded.getBefore(), leftPadded.getMarkers(), adapter.getBeforeLocation.apply(loc), p);
if (prefix != null) {
p.append(prefix);
}
visit(leftPadded.getElement(), p);
afterSyntax(leftPadded.getMarkers(), p);
}
}
public void visitContainer(String before, @Nullable JContainer extends J> container, TContLoc loc,
String suffixBetween, @Nullable String after, PrintOutputCapture
p) {
if (container == null) {
return;
}
beforeSyntax(container.getBefore(), container.getMarkers(), adapter.getContainerBeforeLocation.apply(loc), p);
p.append(before);
visitRightPadded(container.getPadding().getElements(), adapter.getElementLocation.apply(loc), suffixBetween, p);
afterSyntax(container.getMarkers(), p);
p.append(after == null ? "" : after);
}
}
private static final String BLOCK_INDENT_KEY = "BLOCK_INDENT";
private static final String STATEMENT_GROUP_CURSOR_KEY = "STATEMENT_GROUP";
private static final String STATEMENT_GROUP_INDEX_CURSOR_KEY = "STATEMENT_GROUP_INDEX";
private static final UnaryOperator PYTHON_MARKER_WRAPPER =
out -> "/*~~" + out + (out.isEmpty() ? "" : "~~") + ">*/";
protected void afterSyntax(J j, PrintOutputCapture p) {
afterSyntax(j.getMarkers(), p);
}
protected void afterSyntax(Markers markers, PrintOutputCapture
p) {
for (Marker marker : markers.getMarkers()) {
p.append(p.getMarkerPrinter().afterSyntax(marker, new Cursor(getCursor(), marker), PYTHON_MARKER_WRAPPER));
}
}
private @Nullable T reindentPrefix(@Nullable T element) {
if (element == null) return null;
return element.withPrefix(reindentPrefix(element.getPrefix()));
}
private @Nullable JLeftPadded reindentBefore(@Nullable JLeftPadded padded) {
if (padded == null) return null;
return padded.withBefore(reindentPrefix(padded.getBefore()));
}
private @Nullable JRightPadded reindentPrefix(@Nullable JRightPadded padded) {
if (padded == null) return null;
return padded.withElement(reindentPrefix(padded.getElement()));
}
private Space reindentPrefix(Space space) {
String indent = getCursor().getNearestMessage(BLOCK_INDENT_KEY);
if (indent == null) {
indent = "";
}
return reindent(
space,
indent,
PySpace.IndentStartMode.LINE_START,
PySpace.IndentEndMode.STATEMENT_START
);
}
@Override
public J visitCompilationUnit(Py.CompilationUnit cu, PrintOutputCapture p) {
beforeSyntax(cu, Location.COMPILATION_UNIT_PREFIX, p);
for (JRightPadded anImport : cu.getPadding().getImports()) {
visitRightPadded(anImport, PyRightPadded.Location.TOP_LEVEL_STATEMENT_SUFFIX, p);
}
for (JRightPadded statement : cu.getPadding().getStatements()) {
visitRightPadded(statement, PyRightPadded.Location.TOP_LEVEL_STATEMENT_SUFFIX, p);
}
visitSpace(cu.getEof(), Location.COMPILATION_UNIT_EOF, p);
if (cu.getMarkers().findFirst(SuppressNewline.class).isPresent()) {
if (lastCharIs(p, '\n')) {
p.out.setLength(p.out.length() - 1);
}
}
afterSyntax(cu, p);
return cu;
}
@Override
public J visitIdentifier(J.Identifier ident, PrintOutputCapture p) {
beforeSyntax(ident, Space.Location.IDENTIFIER_PREFIX, p);
p.append(ident.getSimpleName());
afterSyntax(ident, p);
return ident;
}
@Override
public J visitFieldAccess(J.FieldAccess fieldAccess, PrintOutputCapture
p) {
beforeSyntax(fieldAccess, Space.Location.FIELD_ACCESS_PREFIX, p);
visit(fieldAccess.getTarget(), p);
visitLeftPadded(".", fieldAccess.getPadding().getName(), JLeftPadded.Location.FIELD_ACCESS_NAME, p);
afterSyntax(fieldAccess, p);
return fieldAccess;
}
@Override
public J visitArrayDimension(J.ArrayDimension arrayDimension, PrintOutputCapture
p) {
beforeSyntax(arrayDimension, Space.Location.DIMENSION_PREFIX, p);
p.append("[");
visitRightPadded(arrayDimension.getPadding().getIndex(), JRightPadded.Location.ARRAY_INDEX, "]", p);
afterSyntax(arrayDimension, p);
return arrayDimension;
}
@Override
public J visitDictLiteral(Py.DictLiteral dict, PrintOutputCapture
p) {
beforeSyntax(dict, PySpace.Location.DICT_LITERAL_PREFIX, p);
if (dict.getElements().isEmpty()) {
p.append("{");
visitPythonExtraPadding(dict, PythonExtraPadding.Location.EMPTY_INITIALIZER, p);
p.append("}");
} else {
visitContainer("{", dict.getPadding().getElements(), PyContainer.Location.DICT_LITERAL_ELEMENTS, ",", "}", p);
}
afterSyntax(dict, p);
return dict;
}
@Override
public J visitKeyValue(Py.KeyValue keyValue, PrintOutputCapture
p) {
beforeSyntax(keyValue, PySpace.Location.KEY_VALUE_PREFIX, p);
visitRightPadded(keyValue.getPadding().getKey(), PyRightPadded.Location.KEY_VALUE_KEY_SUFFIX, p);
p.append(':');
visit(keyValue.getValue(), p);
afterSyntax(keyValue, p);
return keyValue;
}
@Override
public J visitAssignment(J.Assignment assignment, PrintOutputCapture
p) {
final String symbol;
if (getCursor().getParentTreeCursor().getValue() instanceof J.Block) {
symbol = "=";
} else {
symbol = ":=";
}
beforeSyntax(assignment, Space.Location.ASSIGNMENT_PREFIX, p);
visit(assignment.getVariable(), p);
visitLeftPadded(symbol, assignment.getPadding().getAssignment(), JLeftPadded.Location.ASSIGNMENT, p);
afterSyntax(assignment, p);
return assignment;
}
@Override
public J visitAssignmentOperation(J.AssignmentOperation assignOp, PrintOutputCapture
p) {
String keyword = "";
switch (assignOp.getOperator()) {
case Addition:
keyword = "+=";
break;
case Subtraction:
keyword = "-=";
break;
case MatrixMultiplication:
keyword = "@=";
break;
case Multiplication:
keyword = "*=";
break;
case Division:
keyword = "/=";
break;
case Exponentiation:
keyword = "**=";
break;
case FloorDivision:
keyword = "//=";
break;
case Modulo:
keyword = "%=";
break;
case BitAnd:
keyword = "&=";
break;
case BitOr:
keyword = "|=";
break;
case BitXor:
keyword = "^=";
break;
case LeftShift:
keyword = "<<=";
break;
case RightShift:
keyword = ">>=";
break;
case UnsignedRightShift:
keyword = ">>>=";
break;
}
beforeSyntax(assignOp, Space.Location.ASSIGNMENT_OPERATION_PREFIX, p);
visit(assignOp.getVariable(), p);
visitSpace(assignOp.getPadding().getOperator().getBefore(), Space.Location.ASSIGNMENT_OPERATION_OPERATOR, p);
p.append(keyword);
visit(assignOp.getAssignment(), p);
afterSyntax(assignOp, p);
return assignOp;
}
@Override
public J visitBinary(J.Binary binary, PrintOutputCapture
p) {
String keyword = "";
switch (binary.getOperator()) {
case Addition:
keyword = "+";
break;
case Subtraction:
keyword = "-";
break;
case Multiplication:
keyword = "*";
break;
case Division:
keyword = "/";
break;
case Modulo:
keyword = "%";
break;
case LessThan:
keyword = "<";
break;
case GreaterThan:
keyword = ">";
break;
case LessThanOrEqual:
keyword = "<=";
break;
case GreaterThanOrEqual:
keyword = ">=";
break;
case Equal:
keyword = "is";
break;
case NotEqual:
keyword = "is not";
break;
case BitAnd:
keyword = "&";
break;
case BitOr:
keyword = "|";
break;
case BitXor:
keyword = "^";
break;
case LeftShift:
keyword = "<<";
break;
case RightShift:
keyword = ">>";
break;
case UnsignedRightShift:
keyword = ">>>";
break;
case Or:
keyword = "or";
break;
case And:
keyword = "and";
break;
}
beforeSyntax(binary, Space.Location.BINARY_PREFIX, p);
visit(binary.getLeft(), p);
visitSpace(binary.getPadding().getOperator().getBefore(), Space.Location.BINARY_OPERATOR, p);
int spaceIndex = keyword.indexOf(' ');
if (spaceIndex >= 0) {
p.append(keyword.substring(0, spaceIndex));
visitPythonExtraPadding(binary, PythonExtraPadding.Location.WITHIN_OPERATOR_NAME, p);
p.append(keyword.substring(spaceIndex + 1));
} else {
p.append(keyword);
}
visit(binary.getRight(), p);
afterSyntax(binary, p);
return binary;
}
@Override
public J visitClassDeclaration(J.ClassDeclaration classDecl, PrintOutputCapture
p) {
beforeSyntax(classDecl, Space.Location.CLASS_DECLARATION_PREFIX, p);
visitSpace(Space.EMPTY, Space.Location.ANNOTATIONS, p);
visit(classDecl.getLeadingAnnotations(), p);
visit(classDecl.getPadding().getKind().getAnnotations(), p);
visitSpace(
reindentPrefix(classDecl.getPadding().getKind().getPrefix()),
Space.Location.CLASS_KIND,
p
);
p.append("class");
visit(classDecl.getName(), p);
if (classDecl.getPadding().getImplements() != null) {
boolean omitParens = classDecl.getPadding().getImplements().getMarkers().findFirst(OmitParentheses.class).isPresent();
visitContainer(omitParens ? "" : "(", classDecl.getPadding().getImplements(), JContainer.Location.IMPLEMENTS,
",", omitParens ? "" : ")", p);
}
visit(classDecl.getBody(), p);
afterSyntax(classDecl, p);
return classDecl;
}
@Override
public J visitControlParentheses(J.ControlParentheses controlParens, PrintOutputCapture p) {
beforeSyntax(controlParens, Space.Location.CONTROL_PARENTHESES_PREFIX, p);
visitRightPadded(controlParens.getPadding().getTree(), JRightPadded.Location.PARENTHESES, "", p);
afterSyntax(controlParens, p);
return controlParens;
}
@Override
public J visitElse(J.If.Else else_, PrintOutputCapture
p) {
beforeSyntax(reindentPrefix(else_), Space.Location.ELSE_PREFIX, p);
if (getCursor().getParentTreeCursor().getValue() instanceof J.If &&
else_.getBody() instanceof J.If) {
p.append("el");
} else {
p.append("else");
}
visit(else_.getBody(), p);
afterSyntax(else_, p);
return else_;
}
@Override
public J visitBlock(J.Block block, PrintOutputCapture
p) {
visitPythonExtraPadding(block, PythonExtraPadding.Location.BEFORE_COMPOUND_BLOCK_COLON, p);
String parentIndent = getCursor().getNearestMessage(BLOCK_INDENT_KEY);
String newIndent;
Space prefixWithoutIndent;
if (block.getPrefix().getLastWhitespace().contains("\n")) {
String blockIndent = block.getPrefix().getIndent();
newIndent = parentIndent == null ? blockIndent : parentIndent + blockIndent;
prefixWithoutIndent = PySpace.stripIndent(block.getPrefix(), "\n" + blockIndent);
} else {
// inline block
newIndent = "";
prefixWithoutIndent = block.getPrefix();
}
getCursor().putMessage(BLOCK_INDENT_KEY, newIndent);
if (parentIndent != null) {
p.append(":");
}
beforeSyntax(prefixWithoutIndent, block.getMarkers(), Space.Location.BLOCK_PREFIX, p);
visitStatements(block.getPadding().getStatements(), p);
visitSpace(block.getEnd(), Space.Location.BLOCK_END, p);
afterSyntax(block, p);
return block;
}
protected void visitStatements(List> statements, PrintOutputCapture p) {
final String indent = getCursor().getNearestMessage(BLOCK_INDENT_KEY);
@Nullable StatementGroup statementGroup = null;
boolean isFirst = true;
for (int i = 0; i < statements.size(); i++) {
JRightPadded paddedStat = statements.get(i);
if (statementGroup == null || !statementGroup.containsIndex(i)) {
statementGroup = GroupedStatement.findCurrentStatementGroup(statements, i);
getCursor().putMessage(STATEMENT_GROUP_CURSOR_KEY, statementGroup == null ? null : statementGroup.getStatements());
}
getCursor().putMessage(STATEMENT_GROUP_INDEX_CURSOR_KEY, statementGroup == null ? null : i - statementGroup.getFirstIndex());
Statement statement = paddedStat.getElement();
// anything with decorators
final boolean delegateReindentToElement =
statement instanceof J.ClassDeclaration
|| statement instanceof J.MethodDeclaration;
if (statementGroup == null || !statementGroup.containsIndex(i + 1)) {
if (!isFirst && !lastCharIs(p, '\n')) {
p.append(";");
} else if (indent != null && !delegateReindentToElement) {
paddedStat = paddedStat.withElement(reindentPrefix(paddedStat.getElement()));
}
visitRightPadded(paddedStat, JRightPadded.Location.LANGUAGE_EXTENSION, p);
isFirst = false;
} else {
visit(paddedStat.getElement(), p);
}
}
}
@Override
public J visitLambda(J.Lambda lambda, PrintOutputCapture p) {
beforeSyntax(lambda, Space.Location.LAMBDA_PREFIX, p);
p.append("lambda");
visitSpace(lambda.getParameters().getPrefix(), Space.Location.LAMBDA_PARAMETERS_PREFIX, p);
visitMarkers(lambda.getParameters().getMarkers(), p);
visitRightPadded(lambda.getParameters().getPadding().getParams(), JRightPadded.Location.LAMBDA_PARAM, ",", p);
visitSpace(lambda.getArrow(), Space.Location.LAMBDA_ARROW_PREFIX, p);
p.append(":");
visit(lambda.getBody(), p);
afterSyntax(lambda, p);
return lambda;
}
public J visitSwitch(J.Switch switch_, PrintOutputCapture
p) {
beforeSyntax(switch_, Space.Location.SWITCH_PREFIX, p);
p.append("match");
visit(switch_.getSelector(), p);
visit(switch_.getCases(), p);
afterSyntax(switch_, p);
return switch_;
}
@Override
public J visitCase(J.Case case_, PrintOutputCapture
p) {
beforeSyntax(case_, Space.Location.CASE_PREFIX, p);
Expression elem = case_.getExpressions().get(0);
if (!(elem instanceof J.Identifier) || !((J.Identifier) elem).getSimpleName().equals("default")) {
p.append("case");
}
visitContainer("", case_.getPadding().getExpressions(), JContainer.Location.CASE_EXPRESSION, ",", "", p);
visitSpace(case_.getPadding().getStatements().getBefore(), Space.Location.CASE, p);
visitStatements(case_.getPadding().getStatements().getPadding()
.getElements(), p);
if (case_.getBody() instanceof Statement) {
visitRightPadded(case_.getPadding().getBody(), JRightPadded.Location.LANGUAGE_EXTENSION, p);
} else {
visitRightPadded(case_.getPadding().getBody(), JRightPadded.Location.CASE_BODY, ";", p);
}
afterSyntax(case_, p);
return case_;
}
@Override
public J visitTernary(J.Ternary ternary, PrintOutputCapture
p) {
beforeSyntax(ternary, Space.Location.TERNARY_PREFIX, p);
visit(ternary.getTruePart(), p);
visitSpace(ternary.getPadding().getTruePart().getBefore(), Location.TERNARY_TRUE, p);
p.append("if");
visit(ternary.getCondition(), p);
visitLeftPadded("else", ternary.getPadding().getFalsePart(), JLeftPadded.Location.TERNARY_FALSE, p);
afterSyntax(ternary, p);
return ternary;
}
@Override
public J visitForEachLoop(J.ForEachLoop forEachLoop, PrintOutputCapture
p) {
beforeSyntax(forEachLoop, Space.Location.FOR_EACH_LOOP_PREFIX, p);
p.append("for");
visit(forEachLoop.getControl(), p);
visit(forEachLoop.getBody(), p);
afterSyntax(forEachLoop, p);
return forEachLoop;
}
@Override
public J visitForEachControl(J.ForEachLoop.Control control, PrintOutputCapture
p) {
beforeSyntax(control, Space.Location.FOR_EACH_CONTROL_PREFIX, p);
visitRightPadded(control.getPadding().getVariable(), JRightPadded.Location.FOREACH_VARIABLE, p);
p.append("in");
visitRightPadded(control.getPadding().getIterable(), JRightPadded.Location.FOREACH_ITERABLE, p);
afterSyntax(control, p);
return control;
}
@Override
public J visitLiteral(J.Literal literal, PrintOutputCapture
p) {
if (literal.getValue() == null) {
if (literal.getMarkers().findFirst(ImplicitNone.class).isPresent()) {
literal = literal.withValueSource("");
} else {
literal = literal.withValueSource("None");
}
}
beforeSyntax(literal, Space.Location.LITERAL_PREFIX, p);
List unicodeEscapes = literal.getUnicodeEscapes();
if (unicodeEscapes == null) {
p.append(literal.getValueSource());
} else if (literal.getValueSource() != null) {
Iterator surrogateIter = unicodeEscapes.iterator();
J.Literal.UnicodeEscape surrogate = surrogateIter.hasNext() ?
surrogateIter.next() : null;
int i = 0;
if (surrogate != null && surrogate.getValueSourceIndex() == 0) {
p.append("\\u").append(surrogate.getCodePoint());
if (surrogateIter.hasNext()) {
surrogate = surrogateIter.next();
}
}
char[] valueSourceArr = literal.getValueSource().toCharArray();
for (char c : valueSourceArr) {
p.append(c);
if (surrogate != null && surrogate.getValueSourceIndex() == ++i) {
while (surrogate != null && surrogate.getValueSourceIndex() == i) {
p.append("\\u").append(surrogate.getCodePoint());
surrogate = surrogateIter.hasNext() ? surrogateIter.next() : null;
}
}
}
}
afterSyntax(literal, p);
return literal;
}
protected void visitModifier(J.Modifier mod, PrintOutputCapture p) {
String keyword = null;
switch (mod.getType()) {
case Default:
keyword = "def";
break;
case Async:
keyword = "async";
break;
}
if (keyword != null) {
visit(mod.getAnnotations(), p);
beforeSyntax(mod, Space.Location.MODIFIER_PREFIX, p);
p.append(keyword);
afterSyntax(mod, p);
}
}
@Override
public J visitMethodDeclaration(J.MethodDeclaration method, PrintOutputCapture
p) {
beforeSyntax(method, Space.Location.METHOD_DECLARATION_PREFIX, p);
visitSpace(Space.EMPTY, Space.Location.ANNOTATIONS, p);
visit(method.getLeadingAnnotations(), p);
List modifiers = ListUtils.mapFirst(
method.getModifiers(),
mod -> reindentPrefix(mod)
);
for (J.Modifier m : modifiers) {
visitModifier(m, p);
}
visit(method.getName(), p);
visitContainer("(", method.getPadding().getParameters(), JContainer.Location.METHOD_DECLARATION_PARAMETERS, ",", ")", p);
visit(method.getReturnTypeExpression(), p);
visit(method.getBody(), p);
afterSyntax(method, p);
return method;
}
@Override
public J visitVariableDeclarations(J.VariableDeclarations multiVariable, PrintOutputCapture p) {
beforeSyntax(multiVariable, Space.Location.VARIABLE_DECLARATIONS_PREFIX, p);
visitSpace(Space.EMPTY, Space.Location.ANNOTATIONS, p);
visit(multiVariable.getLeadingAnnotations(), p);
for (J.Modifier m : multiVariable.getModifiers()) {
visitModifier(m, p);
}
visitRightPadded(multiVariable.getPadding().getVariables(), JRightPadded.Location.NAMED_VARIABLE, ",", p);
afterSyntax(multiVariable, p);
return multiVariable;
}
@Override
public J visitVariable(J.VariableDeclarations.NamedVariable variable, PrintOutputCapture
p) {
beforeSyntax(variable, Space.Location.VARIABLE_PREFIX, p);
J.VariableDeclarations vd = getCursor().getParentTreeCursor().getValue();
TypeTree type = vd.getTypeExpression();
if (type instanceof Py.SpecialParameter) {
Py.SpecialParameter special = (Py.SpecialParameter) type;
visit(special, p);
type = special.getTypeHint();
}
if (variable.getName().getSimpleName().isEmpty()) {
visit(variable.getInitializer(), p);
} else {
visit(variable.getName(), p);
visit(type, p);
visitLeftPadded("=", variable.getPadding().getInitializer(), JLeftPadded.Location.VARIABLE_INITIALIZER, p);
}
afterSyntax(variable, p);
return variable;
}
private void visitMagicMethodDesugar(J.MethodInvocation method, boolean negate, PrintOutputCapture
p) {
String magicMethodName = method.getSimpleName();
if ("__call__".equals(magicMethodName)) {
beforeSyntax(method, Space.Location.METHOD_INVOCATION_PREFIX, p);
visitRightPadded(method.getPadding().getSelect(), JRightPadded.Location.METHOD_SELECT, p);
visitContainer("(", method.getPadding().getArguments(), JContainer.Location.METHOD_INVOCATION_ARGUMENTS, ",", ")", p);
afterSyntax(method, p);
return;
}
if (method.getArguments().size() != 1) {
throw new IllegalStateException(String.format(
"expected de-sugared magic method call `%s` to have exactly one argument; found %d",
magicMethodName,
method.getArguments().size()
));
}
String operator = PythonOperatorLookup.operatorForMagicMethod(magicMethodName);
if (operator == null) {
throw new IllegalStateException(String.format(
"expected method call `%s` to be a de-sugared operator, but it does not match known operators",
magicMethodName
));
}
if (negate) {
if (!"in".equals(operator)) {
throw new IllegalStateException(String.format(
"found method call `%s` as a de-sugared operator, but it is marked as negated (which it does not support)",
magicMethodName
));
}
}
boolean reverseOperandOrder = PythonOperatorLookup.doesMagicMethodReverseOperands(magicMethodName);
Expression lhs = requireNonNull(method.getSelect());
Expression rhs = method.getArguments().get(0);
J.MethodInvocation.Padding padding = method.getPadding();
Space beforeOperator = requireNonNull(padding.getSelect()).getAfter();
Space afterOperator = rhs.getPrefix();
if (reverseOperandOrder) {
Expression tmp = lhs;
lhs = rhs;
rhs = tmp;
}
beforeSyntax(method, Space.Location.BINARY_PREFIX, p);
visit((Expression) lhs.withPrefix(Space.EMPTY), p);
visitSpace(beforeOperator, Space.Location.BINARY_OPERATOR, p);
if (negate) {
p.append("not");
visitPythonExtraPadding(method, PythonExtraPadding.Location.WITHIN_OPERATOR_NAME, p);
}
p.append(operator);
visit((Expression) rhs.withPrefix(afterOperator), p);
afterSyntax(method, p);
}
private void visitBuiltinDesugar(J.MethodInvocation method, PrintOutputCapture
p) {
Expression select = method.getSelect();
if (!(select instanceof J.Identifier)) {
throw new IllegalStateException("expected builtin desugar to select from an Identifier");
} else if (!"__builtins__".equals(((J.Identifier) select).getSimpleName())) {
throw new IllegalStateException("expected builtin desugar to select from __builtins__");
}
visitSpace(method.getPrefix(), Location.LANGUAGE_EXTENSION, p);
String builtinName = requireNonNull(method.getName()).getSimpleName();
switch (builtinName) {
case "slice":
visitContainer(
"",
method.getPadding().getArguments(),
JContainer.Location.LANGUAGE_EXTENSION,
":",
"",
p
);
return;
case "set":
case "tuple": {
if (method.getArguments().size() != 1) {
throw new IllegalStateException(String.format("builtin `%s` should have exactly one argument", builtinName));
}
Expression arg = method.getArguments().get(0);
if (!(arg instanceof J.NewArray)) {
throw new IllegalStateException(String.format("builtin `%s` should have exactly one argument, a J.NewArray", builtinName));
}
J.NewArray argList = (J.NewArray) arg;
int argCount = 0;
for (Expression argExpr : requireNonNull(argList.getInitializer())) {
if (!(argExpr instanceof J.Empty)) {
argCount++;
}
}
String before;
String after;
if (method.getMarkers().findFirst(OmitParentheses.class).isPresent()) {
before = "";
after = "";
} else if ("set".equals(builtinName)) {
before = "{";
after = "}";
} else {
before = "(";
after = argCount == 1 ? ",)" : ")";
}
visitContainer(
before,
argList.getPadding().getInitializer(),
JContainer.Location.LANGUAGE_EXTENSION,
",",
after,
p
);
return;
}
default:
throw new IllegalStateException(
String.format("builtin desugar doesn't support `%s`", builtinName)
);
}
}
@Override
public J visitMethodInvocation(J.MethodInvocation method, PrintOutputCapture
p) {
if (method.getMarkers().findFirst(MagicMethodDesugar.class).isPresent()) {
visitMagicMethodDesugar(method, false, p);
} else if (method.getMarkers().findFirst(BuiltinDesugar.class).isPresent()) {
visitBuiltinDesugar(method, p);
} else {
beforeSyntax(method, Space.Location.METHOD_INVOCATION_PREFIX, p);
visitRightPadded(method.getPadding().getSelect(), JRightPadded.Location.METHOD_SELECT, ".", p);
visitContainer("<", method.getPadding().getTypeParameters(), JContainer.Location.TYPE_PARAMETERS, ",", ">", p);
visit(method.getName(), p);
String before = "(";
String after = ")";
if (method.getMarkers().findFirst(OmitParentheses.class).isPresent()) {
before = "";
after = "";
}
visitContainer(before, method.getPadding().getArguments(), JContainer.Location.METHOD_INVOCATION_ARGUMENTS, ",", after, p);
afterSyntax(method, p);
}
return method;
}
@Override
public J visitNewArray(J.NewArray newArray, PrintOutputCapture
p) {
beforeSyntax(newArray, Space.Location.NEW_ARRAY_PREFIX, p);
visitContainer("[", newArray.getPadding().getInitializer(), JContainer.Location.NEW_ARRAY_INITIALIZER, ",", "]", p);
afterSyntax(newArray, p);
return newArray;
}
@Override
public J visitAnnotation(J.Annotation annotation, PrintOutputCapture
p) {
beforeSyntax(reindentPrefix(annotation), Space.Location.ANNOTATION_PREFIX, p);
p.append("@");
visit(annotation.getAnnotationType(), p);
visitContainer("(", annotation.getPadding().getArguments(), JContainer.Location.ANNOTATION_ARGUMENTS, ",", ")", p);
visitPythonExtraPadding(
annotation,
PythonExtraPadding.Location.AFTER_DECORATOR,
PySpace.IndentStartMode.AFTER_STATEMENT,
PySpace.IndentEndMode.REST_OF_LINE,
p
);
afterSyntax(annotation, p);
return annotation;
}
@Override
public J visitThrow(J.Throw thrown, PrintOutputCapture
p) {
beforeSyntax(thrown, Space.Location.THROW_PREFIX, p);
p.append("raise");
visit(thrown.getException(), p);
afterSyntax(thrown, p);
return thrown;
}
@Override
public J visitTry(J.Try tryable, PrintOutputCapture
p) {
boolean isWithStatement = tryable.getResources() != null && !tryable.getResources().isEmpty();
beforeSyntax(tryable, Space.Location.TRY_PREFIX, p);
if (isWithStatement) {
p.append("with");
} else {
p.append("try");
}
if (isWithStatement) {
visitSpace(tryable.getPadding().getResources().getBefore(), Space.Location.TRY_RESOURCES, p);
List> resources = tryable.getPadding().getResources().getPadding().getElements();
boolean first = true;
for (JRightPadded resource : resources) {
if (!first) {
p.append(",");
} else {
first = false;
}
visitSpace(resource.getElement().getPrefix(), Space.Location.TRY_RESOURCE, p);
visitMarkers(resource.getElement().getMarkers(), p);
TypedTree decl = resource.getElement().getVariableDeclarations();
if (!(decl instanceof J.Assignment)) {
throw new IllegalArgumentException(
String.format(
"with-statement resource should be an Assignment; found: %s",
decl.getClass().getSimpleName()
)
);
}
J.Assignment assignment = (J.Assignment) decl;
visit(assignment.getAssignment(), p);
if (!(assignment.getVariable() instanceof J.Empty)) {
visitSpace(assignment.getPadding().getAssignment().getBefore(), Location.LANGUAGE_EXTENSION, p);
p.append("as");
visit(assignment.getVariable(), p);
}
visitSpace(resource.getAfter(), Space.Location.TRY_RESOURCE_SUFFIX, p);
}
}
J.Block tryBody = tryable.getBody();
JRightPadded elseBody = null;
List> tryStatements = tryable.getBody().getPadding().getStatements();
if (tryStatements.get(tryStatements.size() - 1).getElement() instanceof J.Block) {
tryBody = tryBody.getPadding().withStatements(tryStatements.subList(0, tryStatements.size() - 1));
elseBody = tryStatements.get(tryStatements.size() - 1);
}
visit(tryBody, p);
visit(tryable.getCatches(), p);
if (elseBody != null) {
// padding is reversed for the `else` part because it's wrapped as though it were a normal statement,
// so its extra padding (which acts as JLeftPadding) is stored in a JRightPadding
visitSpace(reindentPrefix(elseBody.getAfter()), Location.LANGUAGE_EXTENSION, p);
p.append("else");
visit(elseBody.getElement(), p);
}
visitLeftPadded("finally", reindentBefore(tryable.getPadding().getFinally()), JLeftPadded.Location.TRY_FINALLY, p);
afterSyntax(tryable, p);
return tryable;
}
@Override
public J visitCatch(J.Try.Catch catch_, PrintOutputCapture p) {
beforeSyntax(reindentPrefix(catch_), Space.Location.CATCH_PREFIX, p);
p.append("except");
J.VariableDeclarations multiVariable = catch_.getParameter().getTree();
beforeSyntax(multiVariable, Space.Location.VARIABLE_DECLARATIONS_PREFIX, p);
visit(multiVariable.getTypeExpression(), p);
for (JRightPadded paddedVariable : multiVariable.getPadding().getVariables()) {
J.VariableDeclarations.NamedVariable variable = paddedVariable.getElement();
if (variable.getName().getSimpleName().isEmpty()) {
continue;
}
visitSpace(paddedVariable.getAfter(), Location.LANGUAGE_EXTENSION, p);
beforeSyntax(variable, Space.Location.VARIABLE_PREFIX, p);
p.append("as");
visit(variable.getName(), p);
afterSyntax(variable, p);
}
afterSyntax(multiVariable, p);
visit(catch_.getBody(), p);
afterSyntax(catch_, p);
return catch_;
}
@Override
public J visitUnary(J.Unary unary, PrintOutputCapture p) {
if (unary.getMarkers().findFirst(MagicMethodDesugar.class).isPresent()) {
if (unary.getOperator() != J.Unary.Type.Not) {
throw new IllegalStateException(String.format(
"found a unary operator (%s) marked as a magic method de-sugar, but only negation is supported",
unary.getOperator()
));
}
Expression expression = unary.getExpression();
while (expression instanceof J.Parentheses) {
expression = expression.unwrap();
}
if (!(expression instanceof J.MethodInvocation)) {
throw new IllegalStateException(String.format(
"found a unary operator (%s) marked as a magic method de-sugar, but its expression is not a magic method invocation",
unary.getOperator()
));
}
visitMagicMethodDesugar((J.MethodInvocation) expression, true, p);
return unary;
}
beforeSyntax(unary, Space.Location.UNARY_PREFIX, p);
switch (unary.getOperator()) {
case Not:
p.append("not");
break;
case Positive:
p.append("+");
break;
case Negative:
p.append("-");
break;
case Complement:
p.append("~");
break;
}
visit(unary.getExpression(), p);
afterSyntax(unary, p);
return unary;
}
@Override
public J visitImport(Import import_, PrintOutputCapture
p) {
List statementGroup = getCursor().getParentTreeCursor().getMessage(STATEMENT_GROUP_CURSOR_KEY);
if (statementGroup != null) {
Integer statementGroupIndex = getCursor().getParentTreeCursor().getMessage(STATEMENT_GROUP_INDEX_CURSOR_KEY);
if (statementGroupIndex == null) {
throw new IllegalStateException();
}
if (statementGroupIndex != statementGroup.size() - 1) {
return import_;
}
}
if (statementGroup == null) {
statementGroup = Collections.singletonList(import_);
}
boolean printParens = import_.getMarkers().findFirst(ImportParens.class).isPresent();
// boolean hasNewline;
// {
// AtomicBoolean hasNewlineHolder = new AtomicBoolean(false);
// ContainsNewlineVisitor hasNewlineVisitor = new ContainsNewlineVisitor();
// for (Import inGroup : statementGroup) {
// inGroup.acceptJava(hasNewlineVisitor, hasNewlineHolder);
// if (hasNewlineHolder.get()) {
// break;
// }
// }
// hasNewline = hasNewlineHolder.get();
// }
beforeSyntax(import_, Space.Location.IMPORT_PREFIX, p);
boolean isFrom = "".equals(import_.getQualid().getSimpleName());
if (isFrom) {
p.append("import");
} else {
p.append("from");
visit(import_.getQualid().getTarget(), p);
visitSpace(import_.getQualid().getPadding().getName().getBefore(), Location.LANGUAGE_EXTENSION, p);
p.append("import");
}
if (printParens) {
visitPythonExtraPadding(import_, PythonExtraPadding.Location.IMPORT_PARENS_PREFIX, p);
p.append("(");
}
for (int i = 0; i < statementGroup.size(); i++) {
Import inGroup = statementGroup.get(i);
if (i != 0) {
p.append(",");
}
if (isFrom) {
visit(inGroup.getQualid().getTarget(), p);
} else {
visit(inGroup.getQualid().getName(), p);
}
if (inGroup.getAlias() != null) {
visitSpace(inGroup.getPadding().getAlias().getBefore(), Space.Location.LANGUAGE_EXTENSION, p);
p.append("as");
visit(inGroup.getAlias(), p);
}
}
if (printParens) {
visitPythonExtraPadding(import_, PythonExtraPadding.Location.IMPORT_PARENS_SUFFIX, p);
p.append(")");
}
afterSyntax(import_, p);
return import_;
}
// }
private void visitPythonExtraPadding(
Tree tree,
PythonExtraPadding.Location loc,
PySpace.IndentStartMode startMode,
PySpace.IndentEndMode endMode,
PrintOutputCapture p
) {
String indent = getCursor().getNearestMessage(BLOCK_INDENT_KEY);
if (indent == null) {
indent = "";
}
Space space = PythonExtraPadding.getOrDefault(tree, loc);
space = reindent(space, indent, startMode, endMode);
visitSpace(
space,
Location.LANGUAGE_EXTENSION,
p
);
}
private void visitPythonExtraPadding(Tree tree, PythonExtraPadding.Location loc, PrintOutputCapture
p) {
Space space = PythonExtraPadding.getOrDefault(tree, loc);
if (space == null) {
return;
}
visitSpace(
space,
Location.LANGUAGE_EXTENSION,
p
);
}
@Override
public Space visitSpace(Space space, PySpace.Location loc, PrintOutputCapture
p) {
// return delegate.visitSpace(space, Space.Location.LANGUAGE_EXTENSION, p);
p.append(space.getWhitespace());
for (Comment comment : space.getComments()) {
visitMarkers(comment.getMarkers(), p);
comment.printComment(getCursor(), p);
p.append(comment.getSuffix());
}
return space;
}
@Override
public Space visitSpace(Space space, Space.Location loc, PrintOutputCapture
p) {
// return delegate.visitSpace(space, loc, p);
p.append(space.getWhitespace());
for (Comment comment : space.getComments()) {
visitMarkers(comment.getMarkers(), p);
comment.printComment(getCursor(), p);
p.append(comment.getSuffix());
}
return space;
}
@Override
public J visitParentheses(J.Parentheses parens, PrintOutputCapture p) {
beforeSyntax(parens, Space.Location.PARENTHESES_PREFIX, p);
p.append("(");
visitRightPadded(parens.getPadding().getTree(), JRightPadded.Location.PARENTHESES, ")", p);
afterSyntax(parens, p);
return parens;
}
@Override
public J visitWhileLoop(J.WhileLoop whileLoop, PrintOutputCapture
p) {
beforeSyntax(whileLoop, Space.Location.WHILE_PREFIX, p);
p.append("while");
visit(whileLoop.getCondition(), p);
visitRightPadded(whileLoop.getPadding().getBody(), JRightPadded.Location.WHILE_BODY, p);
afterSyntax(whileLoop, p);
return whileLoop;
}
@Override
public J visitIf(J.If iff, PrintOutputCapture
p) {
beforeSyntax(iff, Space.Location.IF_PREFIX, p);
p.append("if");
visit(iff.getIfCondition(), p);
visitRightPadded(iff.getPadding().getThenPart(), JRightPadded.Location.IF_THEN, p);
visit(iff.getElsePart(), p);
afterSyntax(iff, p);
return iff;
}
@Override
public J visitContinue(J.Continue continueStatement, PrintOutputCapture
p) {
beforeSyntax(continueStatement, Space.Location.CONTINUE_PREFIX, p);
p.append("continue");
visit(continueStatement.getLabel(), p);
afterSyntax(continueStatement, p);
return continueStatement;
}
@Override
public J visitReturn(J.Return return_, PrintOutputCapture
p) {
beforeSyntax(return_, Space.Location.RETURN_PREFIX, p);
p.append("return");
visit(return_.getExpression(), p);
afterSyntax(return_, p);
return return_;
}
@Override
public J visitBreak(J.Break breakStatement, PrintOutputCapture
p) {
beforeSyntax(breakStatement, Space.Location.BREAK_PREFIX, p);
p.append("break");
visit(breakStatement.getLabel(), p);
afterSyntax(breakStatement, p);
return breakStatement;
}
@Override
public J visitPassStatement(Py.PassStatement pass, PrintOutputCapture
p) {
beforeSyntax(pass, PySpace.Location.PASS_PREFIX, p);
p.append("pass");
afterSyntax(pass, p);
return pass;
}
@Override
public J visitComprehensionExpression(Py.ComprehensionExpression comp, PrintOutputCapture
p) {
beforeSyntax(comp, PySpace.Location.COMPREHENSION_PREFIX, p);
String open;
String close;
switch (comp.getKind()) {
case DICT:
case SET:
open = "{";
close = "}";
break;
case LIST:
open = "[";
close = "]";
break;
case GENERATOR:
open = "(";
close = ")";
break;
default:
throw new IllegalStateException();
}
p.append(open);
visit(comp.getResult(), p);
for (Py.ComprehensionExpression.Clause clause : comp.getClauses()) {
visit(clause, p);
}
visitSpace(comp.getSuffix(), PySpace.Location.COMPREHENSION_SUFFIX, p);
p.append(close);
afterSyntax(comp, p);
return comp;
}
@Override
public J visitComprehensionClause(Py.ComprehensionExpression.Clause clause, PrintOutputCapture
p) {
visitSpace(clause.getPrefix(), PySpace.Location.COMPREHENSION_CLAUSE_PREFIX, p);
p.append("for");
visit(clause.getIteratorVariable(), p);
visitSpace(clause.getPadding().getIteratedList().getBefore(), PySpace.Location.COMPREHENSION_IN, p);
p.append("in");
visit(clause.getIteratedList(), p);
if (clause.getConditions() != null) {
for (Py.ComprehensionExpression.Condition condition : clause.getConditions()) {
visit(condition, p);
}
}
return clause;
}
@Override
public J visitComprehensionCondition(Py.ComprehensionExpression.Condition condition, PrintOutputCapture
p) {
visitSpace(condition.getPrefix(), PySpace.Location.COMPREHENSION_CONDITION_PREFIX, p);
p.append("if");
visit(condition.getExpression(), p);
return condition;
}
@Override
public J visitAwaitExpression(Py.AwaitExpression await, PrintOutputCapture
p) {
visitSpace(await.getPrefix(), PySpace.Location.AWAIT_PREFIX, p);
p.append("await");
visit(await.getExpression(), p);
return await;
}
@Override
public J visitAssertStatement(Py.AssertStatement assert_, PrintOutputCapture
p) {
visitSpace(assert_.getPrefix(), PySpace.Location.ASSERT_PREFIX, p);
p.append("assert");
visitRightPadded(
assert_.getPadding().getExpressions(),
PyRightPadded.Location.ASSERT_ELEMENT,
",",
p
);
return assert_;
}
@Override
public J visitYieldExpression(Py.YieldExpression yield, PrintOutputCapture
p) {
visitSpace(yield.getPrefix(), PySpace.Location.YIELD_PREFIX, p);
p.append("yield");
if (yield.isFrom()) {
visitLeftPadded(yield.getPadding().getFrom(), PyLeftPadded.Location.YIELD_FROM, p);
p.append("from");
}
visitRightPadded(
yield.getPadding().getExpressions(),
PyRightPadded.Location.YIELD_ELEMENT,
",",
p
);
return yield;
}
@Override
public J visitVariableScopeStatement(Py.VariableScopeStatement scope, PrintOutputCapture
p) {
visitSpace(scope.getPrefix(), PySpace.Location.VARIABLE_SCOPE_PREFIX, p);
switch (scope.getKind()) {
case GLOBAL:
p.append("global");
break;
case NONLOCAL:
p.append("nonlocal");
break;
}
visitRightPadded(
scope.getPadding().getNames(),
PyRightPadded.Location.VARIABLE_SCOPE_ELEMENT,
",",
p
);
return scope;
}
@Override
public J visitDelStatement(Py.DelStatement del, PrintOutputCapture
p) {
visitSpace(del.getPrefix(), PySpace.Location.DEL_PREFIX, p);
p.append("del");
visitRightPadded(
del.getPadding().getTargets(),
PyRightPadded.Location.DEL_ELEMENT,
",",
p
);
return del;
}
@Override
public J visitExceptionType(Py.ExceptionType type, PrintOutputCapture
p) {
beforeSyntax(type, PySpace.Location.EXCEPTION_TYPE_PREFIX, p);
if (type.isExceptionGroup()) {
p.append("*");
}
visit(type.getExpression(), p);
return type;
}
@Override
public J visitErrorFromExpression(Py.ErrorFromExpression expr, PrintOutputCapture
p) {
beforeSyntax(expr, PySpace.Location.ERROR_FROM_PREFIX, p);
visit(expr.getError(), p);
visitSpace(expr.getPadding().getFrom().getBefore(), PySpace.Location.ERROR_FROM_SOURCE, p);
p.append("from");
visit(expr.getFrom(), p);
return expr;
}
@Override
public J visitMatchCase(Py.MatchCase match, PrintOutputCapture
p) {
beforeSyntax(match, PySpace.Location.MATCH_CASE_PREFIX, p);
visit(match.getPattern(), p);
if (match.getPadding().getGuard() != null) {
visitSpace(match.getPadding().getGuard().getBefore(), PySpace.Location.MATCH_CASE_GUARD, p);
p.append("if");
visit(match.getGuard(), p);
}
return match;
}
@Override
public J visitMatchCasePattern(Py.MatchCase.Pattern pattern, PrintOutputCapture
p) {
beforeSyntax(pattern, PySpace.Location.MATCH_PATTERN_PREFIX, p);
JContainer children = pattern.getPadding().getChildren();
switch (pattern.getKind()) {
case AS:
visitContainer(
"",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
"as",
"",
p
);
break;
case CAPTURE:
visitContainer(
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
p
);
break;
case CLASS:
visitSpace(children.getBefore(), PySpace.Location.MATCH_PATTERN_ELEMENT_PREFIX, p);
visitRightPadded(children.getPadding().getElements().get(0), PyRightPadded.Location.MATCH_PATTERN_ELEMENT, p);
visitContainer(
"(",
JContainer.build(children.getPadding().getElements().subList(1, children.getElements().size())),
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
",",
")",
p
);
break;
case DOUBLE_STAR:
visitContainer(
"**",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
"",
"",
p
);
break;
case GROUP:
visitContainer(
"(",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
",",
")",
p
);
break;
case KEY_VALUE:
visitContainer(
"",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
":",
"",
p
);
break;
case KEYWORD:
visitContainer(
"",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
"=",
"",
p
);
break;
case LITERAL:
visitContainer(
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
p
);
break;
case MAPPING:
visitContainer(
"{",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
",",
"}",
p
);
break;
case OR:
visitContainer(
"",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
"|",
"",
p
);
break;
case SEQUENCE:
visitContainer(
"",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
",",
"",
p
);
break;
case SEQUENCE_LIST:
visitContainer(
"[",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
",",
"]",
p
);
break;
case SEQUENCE_TUPLE:
visitContainer(
"(",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
",",
")",
p
);
break;
case STAR:
visitContainer(
"*",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
"",
"",
p
);
break;
case VALUE:
visitContainer(
"",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
"",
"",
p
);
break;
case WILDCARD:
visitContainer(
"_",
children,
PyContainer.Location.MATCH_PATTERN_ELEMENTS,
"",
"",
p
);
break;
}
return pattern;
}
@Override
public J visitSpecialParameter(Py.SpecialParameter param, PrintOutputCapture p) {
beforeSyntax(param, PySpace.Location.SPECIAL_PARAM_PREFIX, p);
switch (param.getKind()) {
case ARGS:
p.append("*");
break;
case KWARGS:
p.append("**");
break;
}
afterSyntax(param, p);
return param;
}
@Override
public J visitNamedArgument(Py.NamedArgument arg, PrintOutputCapture
p) {
beforeSyntax(arg, PySpace.Location.NAMED_ARGUMENT, p);
visit(arg.getName(), p);
visitLeftPadded("=", arg.getPadding().getValue(), PyLeftPadded.Location.NAMED_ARGUMENT, p);
return arg;
}
@Override
public J visitSpecialArgument(Py.SpecialArgument arg, PrintOutputCapture
p) {
beforeSyntax(arg, PySpace.Location.SPECIAL_ARG_PREFIX, p);
switch (arg.getKind()) {
case ARGS:
p.append("*");
break;
case KWARGS:
p.append("**");
break;
}
visit(arg.getExpression(), p);
afterSyntax(arg, p);
return arg;
}
@Override
public J visitTrailingElseWrapper(Py.TrailingElseWrapper wrapper, PrintOutputCapture
p) {
visit(reindentPrefix(wrapper.getStatement()), p);
visitSpace(
reindentPrefix(wrapper.getPadding().getElseBlock().getBefore()),
Location.ELSE_PREFIX,
p
);
p.append("else");
visit(wrapper.getElseBlock(), p);
return wrapper;
}
private static class ContainsNewlineVisitor extends JavaVisitor {
@Override
public Space visitSpace(Space space, Location loc, AtomicBoolean hasNewline) {
space = super.visitSpace(space, loc, hasNewline);
if (space.getWhitespace().contains("\n")) {
hasNewline.set(true);
} else if (!space.getComments().isEmpty()) {
hasNewline.set(true);
}
return space;
}
@Override
public J visitImport(Import import_, AtomicBoolean hasNewline) {
import_ = (Import) super.visitImport(import_, hasNewline);
visitLeftPadded(import_.getPadding().getAlias(), JLeftPadded.Location.LANGUAGE_EXTENSION, hasNewline);
return import_;
}
}
@Override
public J visitTypeHint(Py.TypeHint type, PrintOutputCapture p) {
beforeSyntax(type, PySpace.Location.TYPE_HINT_PREFIX, p);
switch (type.getKind()) {
case VARIABLE_TYPE:
p.append(":");
break;
case RETURN_TYPE:
p.append("->");
break;
}
visit(type.getExpression(), p);
afterSyntax(type, p);
return type;
}
@Override
public J visitTypeHintedExpression(Py.TypeHintedExpression expr, PrintOutputCapture
p) {
beforeSyntax(expr, PySpace.Location.TYPE_HINTED_EXPRESSION_PREFIX, p);
visit(expr.getExpression(), p);
visit(expr.getTypeHint(), p);
afterSyntax(expr, p);
return expr;
}
private static boolean lastCharIs(PrintOutputCapture> p, char c) {
return p.out.length() != 0 && p.out.charAt(p.out.length() - 1) == c;
}
}