From ad899a88ace08081283f2b63d5e4bbf26a021412 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 10 Apr 2015 16:59:05 +0300 Subject: [PATCH] Add method isEmpty() in PySequenceExpression, simplify several usages of getElements() --- .../python/psi/PySequenceExpression.java | 8 +++++ .../jetbrains/python/formatter/PyBlock.java | 30 +++++++++---------- .../PyDictDuplicateKeysInspection.java | 5 ++-- .../PyReplaceTupleWithListQuickFix.java | 2 +- .../python/psi/impl/PyBaseElementImpl.java | 7 +++++ .../psi/impl/PySequenceExpressionImpl.java | 8 ++++- .../validation/AssignTargetAnnotator.java | 4 +-- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java b/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java index 030c5cf560ff..0a070be196ab 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java @@ -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(); } diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index ae2fad7ba858..2bb0291fafb0 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -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 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; } } diff --git a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java index 571e4f6d5fcf..bb913ad16b56 100644 --- a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java @@ -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 map = new HashMap(); - 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) { diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java index e786dcec1a31..d480d339bb36 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java @@ -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 + "]"); diff --git a/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java index 72a2a64528b3..cb155cebd8f1 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java @@ -110,6 +110,13 @@ public class PyBaseElementImpl extends StubBasedPsiElemen return (T)node.getPsi(); } + @Nullable + protected T childToPsi(@NotNull TokenSet elTypes) { + final ASTNode node = getNode().findChildByType(elTypes); + //noinspection unchecked + return node != null ? (T)node.getPsi() : null; + } + @NotNull protected T childToPsiNotNull(TokenSet filterSet, int index) { final PyElement child = childToPsi(filterSet, index); diff --git a/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java index 1eb6ea0f4cb6..b76c73e24474 100644 --- a/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java @@ -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; + } } diff --git a/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java b/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java index a648f37b8ec1..a55011e37693 100644 --- a/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java +++ b/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java @@ -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) {