Add method isEmpty() in PySequenceExpression, simplify several usages of getElements()

This commit is contained in:
Mikhail Golubev
2015-04-14 18:47:50 +03:00
parent 6ca140ef39
commit ad899a88ac
7 changed files with 42 additions and 22 deletions
@@ -23,4 +23,12 @@ import org.jetbrains.annotations.NotNull;
public interface PySequenceExpression extends PyExpression{
@NotNull
PyExpression[] getElements();
/**
* Calling {@link #getElements()} may take too much time in case of large literals with thousands of elements. If you only need to
* know whether collection is empty, use this method instead.
*
* @return true if sequence expression contains no elements
*/
boolean isEmpty();
}
@@ -25,6 +25,7 @@ import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
@@ -82,7 +83,7 @@ public class PyBlock implements ASTBlock {
private final PyBlockContext myContext;
private List<PyBlock> mySubBlocks = null;
private Alignment myChildAlignment;
private final boolean myEmptyList;
private final boolean myEmptySequence;
public PyBlock(final PyBlock parent,
final ASTNode node,
@@ -96,7 +97,7 @@ public class PyBlock implements ASTBlock {
myNode = node;
myWrap = wrap;
myContext = context;
myEmptyList = node.getPsi() instanceof PySequenceExpression && ((PySequenceExpression)node.getPsi()).getElements().length == 0;
myEmptySequence = isEmptySequence(node);
}
@NotNull
@@ -133,7 +134,7 @@ public class PyBlock implements ASTBlock {
final IElementType childType = child.getElementType();
if (child.getTextRange().getLength() == 0) continue;
if (child.getTextRange().isEmpty()) continue;
if (childType == TokenType.WHITE_SPACE) {
continue;
@@ -160,7 +161,7 @@ public class PyBlock implements ASTBlock {
while (p != null) {
final ASTNode pNode = p.getNode();
if (ourListElementTypes.contains(pNode.getElementType())) {
if (needListAlignment(child) && !myEmptyList) {
if (needListAlignment(child) && !myEmptySequence) {
childAlignment = p.getChildAlignment();
break;
@@ -198,7 +199,7 @@ public class PyBlock implements ASTBlock {
!isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) {
wrap = Wrap.createWrap(WrapType.NORMAL, true);
}
if (needListAlignment(child) && !myEmptyList) {
if (needListAlignment(child) && !myEmptySequence) {
childAlignment = getAlignmentForChildren();
}
if (childType == PyTokenTypes.END_OF_LINE_COMMENT) {
@@ -349,6 +350,10 @@ public class PyBlock implements ASTBlock {
return new PyBlock(this, child, childAlignment, childIndent, wrap, myContext);
}
private static boolean isEmptySequence(@NotNull ASTNode node) {
return node.getPsi() instanceof PySequenceExpression && ((PySequenceExpression)node.getPsi()).isEmpty();
}
private boolean argumentMayHaveSameIndentAsFollowingStatementList() {
// This check is supposed to prevent PEP8's error: Continuation line with the same indent as next logical line
final PsiElement header = getControlStatementHeader(myNode);
@@ -499,11 +504,8 @@ public class PyBlock implements ASTBlock {
if (PyTokenTypes.OPEN_BRACES.contains(childType)) {
return false;
}
if (PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType)) {
final PsiElement psi = child.getPsi();
if (psi instanceof PySequenceExpression && ((PySequenceExpression)psi).getElements().length == 0) {
return false;
}
if (PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType) && isEmptySequence(child)) {
return false;
}
if (PyTokenTypes.CLOSE_BRACES.contains(childType)) {
final ASTNode prevNonSpace = findPrevNonSpaceNode(child);
@@ -757,12 +759,8 @@ public class PyBlock implements ASTBlock {
return null;
}
if (myNode.getPsi() instanceof PyDictLiteralExpression) {
final PyKeyValueExpression[] elements = ((PyDictLiteralExpression)myNode.getPsi()).getElements();
if (elements.length == 0) {
return null;
}
final PyKeyValueExpression last = elements[elements.length - 1];
if (last.getValue() == null) { // incomplete
final PyKeyValueExpression lastElement = ArrayUtil.getLastElement(((PyDictLiteralExpression)myNode.getPsi()).getElements());
if (lastElement == null || lastElement.getValue() == null /* incomplete */) {
return null;
}
}
@@ -58,9 +58,10 @@ public class PyDictDuplicateKeysInspection extends PyInspection {
@Override
public void visitPyDictLiteralExpression(PyDictLiteralExpression node) {
if (node.getElements().length != 0){
final PyKeyValueExpression[] elements = node.getElements();
if (elements.length != 0){
final Map<String, PyElement> map = new HashMap<String, PyElement>();
for (PyExpression exp : node.getElements()) {
for (PyExpression exp : elements) {
final PyExpression key = ((PyKeyValueExpression)exp).getKey();
if (key instanceof PyNumericLiteralExpression
|| key instanceof PyStringLiteralExpression || key instanceof PyReferenceExpression) {
@@ -59,7 +59,7 @@ public class PyReplaceTupleWithListQuickFix implements LocalQuickFix {
}
private static void replaceWithListLiteral(PsiElement element, PyTupleExpression expression) {
final String expressionText = expression.getElements().length == 0 ? "" :expression.getText();
final String expressionText = expression.isEmpty() ? "" :expression.getText();
final PyExpression literal = PyElementGenerator.getInstance(element.getProject()).
createExpressionFromText(LanguageLevel.forElement(element),
"[" + expressionText + "]");
@@ -110,6 +110,13 @@ public class PyBaseElementImpl<T extends StubElement> extends StubBasedPsiElemen
return (T)node.getPsi();
}
@Nullable
protected <T extends PyElement> T childToPsi(@NotNull TokenSet elTypes) {
final ASTNode node = getNode().findChildByType(elTypes);
//noinspection unchecked
return node != null ? (T)node.getPsi() : null;
}
@NotNull
protected <T extends PyElement> T childToPsiNotNull(TokenSet filterSet, int index) {
final PyElement child = childToPsi(filterSet, index);
@@ -4,12 +4,13 @@ import com.intellij.lang.ASTNode;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PySequenceExpression;
import org.jetbrains.annotations.NotNull;
/**
* @author Mikhail Golubev
*/
public class PySequenceExpressionImpl extends PyElementImpl {
public abstract class PySequenceExpressionImpl extends PyElementImpl implements PySequenceExpression {
public PySequenceExpressionImpl(ASTNode astNode) {
super(astNode);
}
@@ -26,4 +27,9 @@ public class PySequenceExpressionImpl extends PyElementImpl {
public PyExpression[] getElements() {
return childrenToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), PyExpression.EMPTY_ARRAY);
}
@Override
public boolean isEmpty() {
return childToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens()) == null;
}
}
@@ -130,7 +130,7 @@ public class AssignTargetAnnotator extends PyAnnotator {
@Override
public void visitPyTupleExpression(final PyTupleExpression node) {
if (node.getElements().length == 0) {
if (node.isEmpty()) {
getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.parens"));
}
else if (myOp == Operation.AugAssign) {
@@ -153,7 +153,7 @@ public class AssignTargetAnnotator extends PyAnnotator {
@Override
public void visitPyListLiteralExpression(final PyListLiteralExpression node) {
if (node.getElements().length == 0) {
if (node.isEmpty()) {
getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.brackets"));
}
else if (myOp == Operation.AugAssign) {