PY-12008 Use another type of block for binary expressions

in order to the structure of nested binary expressions during
formatting. For instance, "x and y or z" and "x or y and z" should
be formatted the same regardless of the precedence of individual
operations.
This commit is contained in:
Mikhail Golubev
2017-10-30 14:09:46 +03:00
parent f6fb3b837c
commit 2d6b2b2727
4 changed files with 86 additions and 11 deletions
@@ -0,0 +1,57 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.formatter;
import com.intellij.formatting.Alignment;
import com.intellij.formatting.Indent;
import com.intellij.formatting.Wrap;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.PyElementTypes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Mikhail Golubev
*/
public class PyBinaryExpressionBlock extends PyBlock {
private List<ASTNode> myChildrenNodes = new ArrayList<>();
public PyBinaryExpressionBlock(@Nullable PyBlock parent,
@NotNull ASTNode node,
@Nullable Alignment alignment,
@NotNull Indent indent,
@Nullable Wrap wrap,
@NotNull PyBlockContext context) {
super(parent, node, alignment, indent, wrap, context);
assert node.getElementType() == PyElementTypes.BINARY_EXPRESSION;
myChildrenNodes = collectChildren();
}
@NotNull
private List<ASTNode> collectChildren() {
List<ASTNode> result = new ArrayList<>();
collectChildren(myNode, result);
return result;
}
private void collectChildren(@NotNull ASTNode node, @NotNull List<ASTNode> result) {
if (node.getElementType() == PyElementTypes.BINARY_EXPRESSION) {
for (ASTNode child : node.getChildren(null)) {
collectChildren(child, result);
}
}
else if (node != this) {
result.add(node);
}
}
@NotNull
@Override
protected Iterable<ASTNode> getSubBlockNodes() {
return myChildrenNodes;
}
}
@@ -81,10 +81,23 @@ public class PyBlock implements ASTBlock {
public static final Key<Boolean> IMPORT_GROUP_BEGIN = Key.create("com.jetbrains.python.formatter.importGroupBegin");
private static final boolean ALIGN_IF_CONDITION_WITHOUT_PARENTHESES = false;
@NotNull
public static PyBlock createBlock(@Nullable PyBlock parent,
@NotNull ASTNode node,
@Nullable Alignment alignment,
@NotNull Indent indent,
@Nullable Wrap wrap,
@NotNull PyBlockContext context) {
if (node.getElementType() == PyElementTypes.BINARY_EXPRESSION) {
return new PyBinaryExpressionBlock(parent, node, alignment, indent, wrap, context);
}
return new PyBlock(parent, node, alignment, indent, wrap, context);
}
private final PyBlock myParent;
private final Alignment myAlignment;
private final Indent myIndent;
private final ASTNode myNode;
protected final ASTNode myNode;
private final Wrap myWrap;
private final PyBlockContext myContext;
private List<PyBlock> mySubBlocks = null;
@@ -97,12 +110,12 @@ public class PyBlock implements ASTBlock {
private Wrap myDictWrapping = null;
private Wrap myFromImportWrapping = null;
public PyBlock(@Nullable PyBlock parent,
@NotNull ASTNode node,
@Nullable Alignment alignment,
@NotNull Indent indent,
@Nullable Wrap wrap,
@NotNull PyBlockContext context) {
protected PyBlock(@Nullable PyBlock parent,
@NotNull ASTNode node,
@Nullable Alignment alignment,
@NotNull Indent indent,
@Nullable Wrap wrap,
@NotNull PyBlockContext context) {
myParent = parent;
myAlignment = alignment;
myIndent = indent;
@@ -163,7 +176,7 @@ public class PyBlock implements ASTBlock {
@NotNull
private Map<ASTNode, PyBlock> buildSubBlocks() {
final Map<ASTNode, PyBlock> blocks = new LinkedHashMap<>();
for (ASTNode child = myNode.getFirstChildNode(); child != null; child = child.getTreeNext()) {
for (ASTNode child: getSubBlockNodes()) {
final IElementType childType = child.getElementType();
@@ -178,6 +191,11 @@ public class PyBlock implements ASTBlock {
return Collections.unmodifiableMap(blocks);
}
@NotNull
protected Iterable<ASTNode> getSubBlockNodes() {
return Arrays.asList(myNode.getChildren(null));
}
@NotNull
private PyBlock buildSubBlock(@NotNull ASTNode child) {
final IElementType parentType = myNode.getElementType();
@@ -405,7 +423,7 @@ public class PyBlock implements ASTBlock {
prev = prev.getTreePrev();
}
return new PyBlock(this, child, childAlignment, childIndent, childWrap, myContext);
return createBlock(this, child, childAlignment, childIndent, childWrap, myContext);
}
private static boolean isParenthesisedIfCondition(@NotNull ASTNode node) {
@@ -51,7 +51,7 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C
printAST(fileNode, 0);
}
final PyBlockContext context = new PyBlockContext(settings, createSpacingBuilder(settings), mode);
final PyBlock block = new PyBlock(null, element.getNode(), null, Indent.getNoneIndent(), null, context);
final PyBlock block = PyBlock.createBlock(null, element.getNode(), null, Indent.getNoneIndent(), null, context);
if (DUMP_FORMATTING_AST) {
FormattingModelDumper.dumpFormattingModel(block, 2, System.out);
}
@@ -1,5 +1,5 @@
if foo and \
bar == 42:
bar == 42:
pass
elif foo and \
bar: