Fixes PY-71, introduces parts in multi-part statements.

Slight fixes to autoindenter.
This commit is contained in:
Dmitry Cheryasov
2009-04-02 09:51:49 +04:00
parent 321dd54444
commit 3bcabf5876
48 changed files with 1136 additions and 268 deletions
@@ -27,7 +27,7 @@ public interface PyElementTypes {
PyElementType ARGUMENT_LIST = new PyElementType("ARGUMENT_LIST", PyArgumentListImpl.class);
PyElementType IMPORT_ELEMENT = new PyElementType("IMPORT_ELEMENT", PyImportElementImpl.class);
PyElementType STAR_IMPORT_ELEMENT = new PyElementType("STAR_IMPORT_ELEMENT", PyStarImportElementImpl.class);
PyElementType EXCEPT_BLOCK = new PyElementType("EXCEPT_BLOCK", PyExceptBlockImpl.class);
PyElementType EXCEPT_PART = new PyElementType("EXCEPT_PART", PyExceptPartImpl.class);
PyElementType PRINT_TARGET = new PyElementType("PRINT_TARGET", PyPrintTargetImpl.class);
PyElementType DECORATOR = new PyElementType("DECORATOR", PyDecoratorImpl.class);
@@ -100,6 +100,8 @@ public interface PyElementTypes {
REPR_EXPRESSION, GENERATOR_EXPRESSION, CONDITIONAL_EXPRESSION, YIELD_EXPRESSION,
TARGET_EXPRESSION);
TokenSet LIST_LIKE_EXPRESSIONS = TokenSet.create(LIST_LITERAL_EXPRESSION, LIST_COMP_EXPRESSION, TUPLE_EXPRESSION);
TokenSet STATEMENT_LISTS = TokenSet.create(STATEMENT_LIST);
TokenSet BINARY_OPS = TokenSet.create(PyTokenTypes.OR_KEYWORD, PyTokenTypes.AND_KEYWORD, PyTokenTypes.LT, PyTokenTypes.GT,
@@ -108,4 +110,19 @@ public interface PyElementTypes {
PyTokenTypes.IN_KEYWORD, PyTokenTypes.IS_KEYWORD, PyTokenTypes.NOT_KEYWORD, PyTokenTypes.PLUS,
PyTokenTypes.MINUS, PyTokenTypes.MULT, PyTokenTypes.FLOORDIV, PyTokenTypes.DIV, PyTokenTypes.PERC);
// Parts
PyElementType IF_PART_IF = new PyElementType("IF_IF", PyIfPartIfImpl.class);
PyElementType IF_PART_ELIF = new PyElementType("IF_ELIF", PyIfPartElifImpl.class);
PyElementType FOR_PART = new PyElementType("FOR_PART", PyForPartImpl.class);
PyElementType WHILE_PART = new PyElementType("WHILE_PART", PyWhilePartImpl.class);
PyElementType TRY_PART = new PyElementType("TRY_PART", PyTryPartImpl.class);
PyElementType FINALLY_PART = new PyElementType("FINALLY_PART", PyFinallyPartImpl.class);
PyElementType ELSE_PART = new PyElementType("ELSE_PART", PyElsePartImpl.class);
TokenSet PARTS = TokenSet.create(IF_PART_IF, IF_PART_ELIF, FOR_PART, WHILE_PART, TRY_PART, FINALLY_PART, ELSE_PART);
TokenSet ELIFS = TokenSet.create(IF_PART_ELIF);
}
@@ -18,15 +18,19 @@ package com.jetbrains.python.formatter;
import com.intellij.formatting.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.TokenType;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyStatementList;
import com.jetbrains.python.psi.PyStatementPart;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -223,14 +227,35 @@ public class PyBlock implements Block {
public ChildAttributes getChildAttributes(int newChildIndex) {
int statementListsBelow = 0;
if (newChildIndex > 0) {
// always pass decision to a sane block from top level from file or definition
if (_node.getPsi() instanceof PyFile || _node.getElementType() == PyTokenTypes.COLON) {
return ChildAttributes.DELEGATE_TO_PREV_CHILD;
}
PyBlock insertAfterBlock = (PyBlock)_subBlocks.get(newChildIndex - 1);
System.out.println( // XXX debug
"getChildAttributes(" + newChildIndex + "): " + ((PyBlock)insertAfterBlock).getNode().getPsi().toString() +
" " + insertAfterBlock.getTextRange().getStartOffset()
+ ":" + insertAfterBlock.getTextRange().getLength()
);
ASTNode prevNode = insertAfterBlock.getNode();
PsiElement prevElt = prevNode.getPsi();
// stmt lists, parts and definitions should also think for themselves
if (prevElt instanceof PyStatementList || prevElt instanceof PyStatementPart) {
return ChildAttributes.DELEGATE_TO_PREV_CHILD;
}
ASTNode lastChild = insertAfterBlock.getNode();
// HACK? This code fragment is needed to make testClass2() pass,
// but I don't quite understand why it is necessary and why the formatter
// doesn't request childAttributes from the correct block
while (lastChild != null) {
if (lastChild.getElementType() == PyElementTypes.STATEMENT_LIST && hasLineBreakBefore(lastChild)) {
IElementType last_type = lastChild.getElementType();
if ( last_type == PyElementTypes.STATEMENT_LIST && hasLineBreakBefore(lastChild)) {
statementListsBelow++;
}
else if (statementListsBelow > 0 && lastChild.getPsi() instanceof PsiErrorElement) {
@@ -251,11 +276,19 @@ public class PyBlock implements Block {
// the innermost block, so we need to resolve the situation here. Nested
// delegation sometimes causes NPEs in formatter core, so we calculate the
// correct indent manually.
if (statementListsBelow > 1) {
if (statementListsBelow > 0) { // was 1... strange
int indent = _settings.getIndentSize(_language.getAssociatedFileType());
return new ChildAttributes(Indent.getSpaceIndent(indent * statementListsBelow), null);
}
/*
// it might be something like "def foo(): # comment" or "[1, # comment"; jump up to the real thing
if (_node instanceof PsiComment || _node instanceof PsiWhiteSpace) {
get
}
*/
return new ChildAttributes(getChildIndent(newChildIndex), getChildAlignment());
}
@@ -273,6 +306,13 @@ public class PyBlock implements Block {
PyBlock insertAfterBlock = (PyBlock)_subBlocks.get(newChildIndex - 1);
ASTNode afterNode = insertAfterBlock.getNode();
System.out.println( // XXX debug
"getChildIndent(" + newChildIndex + "): " + ((PyBlock)insertAfterBlock).getNode().getPsi().toString() +
" " + insertAfterBlock.getTextRange().getStartOffset()
+ ":" + insertAfterBlock.getTextRange().getLength()
);
// handle pressing Enter after colon and before first statement in
// existing statement list
if (afterNode.getElementType() == PyElementTypes.STATEMENT_LIST || afterNode.getElementType() == PyTokenTypes.COLON) {
@@ -286,8 +326,47 @@ public class PyBlock implements Block {
return Indent.getNormalIndent();
}
}
else if (lastChild != null && PyElementTypes.LIST_LIKE_EXPRESSIONS.contains(lastChild.getElementType())) {
// handle pressing enter at the end of a list literal when there's no closing paren or bracket
ASTNode lastLastChild = lastChild.getLastChildNode();
if (lastLastChild != null && lastLastChild.getPsi() instanceof PsiErrorElement) {
// we're at a place like this: [foo, ... bar, <caret>
// we'd rather align to foo. this may be not a multiple of tabs.
PsiElement expr = lastChild.getPsi();
PsiElement exprItem = expr.getFirstChild();
boolean found = false;
while (exprItem != null) { // find a worthy element to align to
if (exprItem instanceof PyElement) {
found = true; // align to foo in "[foo,"
break;
}
if (exprItem instanceof PsiComment) {
found = true; // align to foo in "[ # foo,"
break;
}
exprItem = exprItem.getNextSibling();
}
if (found) {
PsiDocumentManager docMgr = PsiDocumentManager.getInstance(exprItem.getProject());
Document doc = docMgr.getDocument(exprItem.getContainingFile());
if (doc != null) {
int line_num = doc.getLineNumber(exprItem.getTextOffset());
int item_col = exprItem.getTextOffset() - doc.getLineStartOffset(line_num);
PsiElement here_elt = getNode().getPsi();
line_num = doc.getLineNumber(here_elt.getTextOffset());
int node_col = here_elt.getTextOffset() - doc.getLineStartOffset(line_num);
int padding = item_col - node_col;
if (padding > 0) { // negative is a syntax error, but possible
return Indent.getSpaceIndent(padding);
}
}
}
return Indent.getContinuationIndent(); // a fallback
}
}
if (_listElementTypes.contains(_node.getElementType())) {
// constructs that imply indent for their children
if (_listElementTypes.contains(_node.getElementType()) || _node.getPsi() instanceof PyStatementPart) {
return Indent.getNormalIndent();
}
@@ -483,62 +483,83 @@ public class StatementParsing
private void parseIfStatement() {
assertCurrentToken(PyTokenTypes.IF_KEYWORD);
final PsiBuilder.Marker ifStatement = myBuilder.mark();
final PsiBuilder.Marker ifPart = myBuilder.mark();
myBuilder.advanceLexer();
getExpressionParser().parseExpression();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
ifPart.done(PyElementTypes.IF_PART_IF);
PsiBuilder.Marker elifPart = myBuilder.mark();
while (myBuilder.getTokenType() == PyTokenTypes.ELIF_KEYWORD) {
myBuilder.advanceLexer();
getExpressionParser().parseExpression();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
elifPart.done(PyElementTypes.IF_PART_ELIF);
elifPart = myBuilder.mark();
}
elifPart.drop(); // we always kept an open extra elif
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else elsePart.drop();
ifStatement.done(PyElementTypes.IF_STATEMENT);
}
private void parseForStatement() {
assertCurrentToken(PyTokenTypes.FOR_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker forPart = myBuilder.mark();
myBuilder.advanceLexer();
getExpressionParser().parseExpression(true, true);
checkMatches(PyTokenTypes.IN_KEYWORD, "'in' expected");
getExpressionParser().parseExpression();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
forPart.done(PyElementTypes.FOR_PART);
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else elsePart.drop();
statement.done(PyElementTypes.FOR_STATEMENT);
}
private void parseWhileStatement() {
assertCurrentToken(PyTokenTypes.WHILE_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker whilePart = myBuilder.mark();
myBuilder.advanceLexer();
getExpressionParser().parseExpression();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
whilePart.done(PyElementTypes.WHILE_PART);
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else elsePart.drop();
statement.done(PyElementTypes.WHILE_STATEMENT);
}
private void parseTryStatement() {
assertCurrentToken(PyTokenTypes.TRY_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker tryPart = myBuilder.mark();
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
tryPart.done(PyElementTypes.TRY_PART);
boolean haveExceptClause = false;
if (myBuilder.getTokenType() == PyTokenTypes.EXCEPT_KEYWORD) {
haveExceptClause = true;
@@ -558,23 +579,31 @@ public class StatementParsing
}
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
exceptBlock.done(PyElementTypes.EXCEPT_BLOCK);
exceptBlock.done(PyElementTypes.EXCEPT_PART);
}
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else elsePart.drop();
}
final PsiBuilder.Marker finallyPart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.FINALLY_KEYWORD) {
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
finallyPart.done(PyElementTypes.FINALLY_PART);
}
else if (!haveExceptClause) {
myBuilder.error("'except' or 'finally' expected");
// much better to have a statement of incorrectly determined type
// than "TRY" and "COLON" tokens attached to nothing
else {
finallyPart.drop();
if (!haveExceptClause) {
myBuilder.error("'except' or 'finally' expected");
// much better to have a statement of incorrectly determined type
// than "TRY" and "COLON" tokens attached to nothing
}
}
statement.done(PyElementTypes.TRY_EXCEPT_STATEMENT);
}
@@ -0,0 +1,16 @@
package com.jetbrains.python.psi;
import org.jetbrains.annotations.Nullable;
/**
* A statement part that has a condition before it.
* User: dcheryasov
* Date: Mar 16, 2009 4:44:25 AM
*/
public interface PyConditionalStatementPart extends PyStatementPart {
/**
* @return the condition expression.
*/
@Nullable
PyExpression getCondition();
}
@@ -158,7 +158,7 @@ public class PyElementVisitor extends PsiElementVisitor {
visitPyElement(node);
}
public void visitPyExceptBlock(final PyExceptBlock node) {
public void visitPyExceptBlock(final PyExceptPart node) {
visitPyElement(node);
}
@@ -0,0 +1,16 @@
package com.jetbrains.python.psi;
import org.jetbrains.annotations.Nullable;
/**
* The 'else:' part of various compound statements.
* User: dcheryasov
* Date: Mar 15, 2009 9:34:51 PM
*/
public interface PyElsePart extends PyStatementPart {
/**
* @return the body of the 'else' part.
*/
@Nullable
PyStatementList getStatementList();
}
@@ -17,7 +17,6 @@
package com.jetbrains.python.psi;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -26,10 +25,9 @@ import org.jetbrains.annotations.NotNull;
* Time: 23:37:32
* To change this template use File | Settings | File Templates.
*/
public interface PyExceptBlock extends PyElement, NameDefiner {
PyExceptBlock[] EMPTY_ARRAY = new PyExceptBlock[0];
public interface PyExceptPart extends PyElement, NameDefiner, PyStatementPart {
PyExceptPart[] EMPTY_ARRAY = new PyExceptPart[0];
@Nullable PyExpression getExceptClass();
@Nullable PyExpression getTarget();
@NotNull PyStatementList getStatementList();
}
@@ -0,0 +1,10 @@
package com.jetbrains.python.psi;
/**
* The 'finally' part.
* @see PyTryPart
* User: dcheryasov
* Date: Mar 16, 2009 6:29:22 AM
*/
public interface PyFinallyPart extends PyStatementPart {
}
@@ -0,0 +1,23 @@
package com.jetbrains.python.psi;
import org.jetbrains.annotations.Nullable;
/**
* Main part of a 'for' statement
* User: dcheryasov
* Date: Mar 15, 2009 8:55:22 PM
*/
public interface PyForPart extends PyStatementPart {
/**
* @return target: the "x" in "<code>for x in (1, 2, 3)</code>".
*/
@Nullable
PyExpression getTarget();
/**
* @return source of iteration: the "(1, 2, 3)" in "<code>for x in (1, 2, 3)</code>".
*/
@Nullable
PyExpression getSource();
}
@@ -20,15 +20,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
* User: yole
* Date: 29.05.2005
* Time: 21:20:52
* To change this template use File | Settings | File Templates.
* The 'for/else' statement.
*/
public interface PyForStatement extends PyStatement, NameDefiner {
@NotNull PyStatementList getStatementList();
@Nullable PyStatementList getElseStatementList();
@Nullable PyExpression getTargetExpression();
@Nullable PyExpression getLoopExpression();
@NotNull PyForPart getForPart();
@Nullable PyElsePart getElsePart();
}
@@ -0,0 +1,16 @@
package com.jetbrains.python.psi;
/**
* Branches of an 'if' statement.
* @see PyElsePart
* User: dcheryasov
* Date: Mar 12, 2009 2:16:00 AM
*/
public interface PyIfPart extends PyConditionalStatementPart {
PyIfPart[] EMPTY_ARRAY = new PyIfPart[0];
/**
* @return true for a 'elif' part.
*/
boolean isElif();
}
@@ -20,14 +20,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
* User: yole
* Date: 29.05.2005
* Time: 21:07:48
* To change this template use File | Settings | File Templates.
* The 'if/elif/else' statement.
*/
public interface PyIfStatement extends PyStatement {
@NotNull PyExpression[] getConditions();
@NotNull PyStatementList[] getStatementLists();
@Nullable PyStatementList getElseStatementList();
@NotNull PyIfPart getIfPart();
@NotNull PyIfPart[] getElifParts();
@Nullable PyElsePart getElsePart();
}
@@ -0,0 +1,18 @@
package com.jetbrains.python.psi;
import org.jetbrains.annotations.Nullable;
/**
* Abstract part of a multipart statement.
* User: dcheryasov
* Date: Mar 16, 2009 4:34:59 AM
*/
public interface PyStatementPart extends PyElement {
PyStatementPart[] EMPTY_ARRAY = new PyStatementPart[0];
/**
* @return the body of the part.
*/
@Nullable
PyStatementList getStatementList();
}
@@ -20,22 +20,20 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
* User: yole
* The 'try/except/else/finally' statement.
* Date: 02.06.2005
* Time: 23:14:36
* To change this template use File | Settings | File Templates.
*/
public interface PyTryExceptStatement extends PyStatement {
@NotNull
PyStatementList getTryStatementList();
PyTryPart getTryPart();
@NotNull
PyExceptBlock[] getExceptBlocks();
PyExceptPart[] getExceptParts();
@Nullable
PyStatementList getElseStatementList();
PyElsePart getElsePart();
@Nullable
PyStatementList getFinallyStatementList();
PyFinallyPart getFinallyPart();
}
@@ -0,0 +1,12 @@
package com.jetbrains.python.psi;
/**
* The 'try' part.
* @see PyExceptPart
* @see PyFinallyPart
* @see PyElsePart
* User: dcheryasov
* Date: Mar 16, 2009 6:26:40 AM
*/
public interface PyTryPart extends PyStatementPart {
}
@@ -0,0 +1,10 @@
package com.jetbrains.python.psi;
/**
* The 'while' part of a cycle.
* @see PyElsePart
* User: dcheryasov
* Date: Mar 16, 2009 4:25:59 AM
*/
public interface PyWhilePart extends PyConditionalStatementPart {
}
@@ -20,13 +20,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
* User: yole
* Date: 29.05.2005
* Time: 15:47:19
* To change this template use File | Settings | File Templates.
* The 'while' statement.
*/
public interface PyWhileStatement extends PyLoopStatement {
@NotNull PyStatementList getStatementList();
@Nullable PyStatementList getElseStatementList();
@NotNull PyWhilePart getWhilePart();
@Nullable PyElsePart getElsePart();
}
@@ -0,0 +1,24 @@
package com.jetbrains.python.psi.impl;
import com.jetbrains.python.psi.PyConditionalStatementPart;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.PyElementTypes;
import com.intellij.lang.ASTNode;
/**
* User: dcheryasov
* Date: Mar 16, 2009 4:46:26 AM
*/
public abstract class PyConditionalStatementPartImpl extends PyStatementPartImpl implements PyConditionalStatementPart {
public PyConditionalStatementPartImpl(ASTNode astNode) {
super(astNode);
}
public PyExpression getCondition() {
ASTNode n = getNode().findChildByType(PyElementTypes.EXPRESSIONS);
if (n != null) {
return (PyExpression)n.getPsi();
}
return null;
}
}
@@ -0,0 +1,25 @@
package com.jetbrains.python.psi.impl;
import com.jetbrains.python.psi.PyElsePart;
import com.jetbrains.python.psi.PyStatementList;
import com.jetbrains.python.PyElementTypes;
import com.intellij.lang.ASTNode;
/**
* User: dcheryasov
* Date: Mar 15, 2009 9:40:35 PM
*/
public class PyElsePartImpl extends PyElementImpl implements PyElsePart {
public PyElsePartImpl(ASTNode astNode) {
super(astNode);
}
public PyStatementList getStatementList() {
ASTNode n = getNode().findChildByType(PyElementTypes.STATEMENT_LISTS);
if (n != null) {
return (PyStatementList)n.getPsi();
}
return null;
}
}
@@ -29,8 +29,8 @@ import com.jetbrains.python.psi.*;
* Time: 23:37:49
* To change this template use File | Settings | File Templates.
*/
public class PyExceptBlockImpl extends PyElementImpl implements PyExceptBlock {
public PyExceptBlockImpl(ASTNode astNode) {
public class PyExceptPartImpl extends PyElementImpl implements PyExceptPart {
public PyExceptPartImpl(ASTNode astNode) {
super(astNode);
}
@@ -0,0 +1,14 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.psi.PyFinallyPart;
/**
* User: dcheryasov
* Date: Mar 16, 2009 6:58:03 AM
*/
public class PyFinallyPartImpl extends PyStatementPartImpl implements PyFinallyPart{
public PyFinallyPartImpl(ASTNode astNode) {
super(astNode);
}
}
@@ -0,0 +1,67 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyElementType;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyForPart;
import java.util.List;
/**
* User: dcheryasov
* Date: Mar 15, 2009 9:57:48 PM
*/
public class PyForPartImpl extends PyStatementPartImpl implements PyForPart {
public PyForPartImpl(ASTNode astNode) {
super(astNode);
}
/**
* Checks that given node actually follows a node of given type, skipping whitespace.
* @param node node to check.
* @param eltType type of a node that must precede the node we're checking.
* @return true if node is really a next sibling to a node of eltType type.
*/
protected static boolean followsNodeOfType(ASTNode node, PyElementType eltType) {
if (node != null) {
PsiElement checker = node.getPsi();
checker = checker.getPrevSibling(); // step from the source node
while (checker != null) {
ASTNode ch_node = checker.getNode();
if (ch_node == null) return false;
else {
if (ch_node.getElementType() == eltType) {
return true;
}
else if (!(checker instanceof PsiWhiteSpace)) {
return false;
}
}
checker = checker.getPrevSibling();
}
}
return false;
}
public PyExpression getTarget() {
ASTNode n = getNode().findChildByType(PyElementTypes.EXPRESSIONS);
if (followsNodeOfType(n, PyTokenTypes.FOR_KEYWORD)) {
return (PyExpression)n.getPsi(); // can't be null, 'if' would fail
}
else return null;
}
public PyExpression getSource() {
List<PsiElement> exprs = findChildrenByType(PyElementTypes.EXPRESSIONS);
// normally there are 2 exprs, the second is the source.
if (exprs.size() != 2) return null; // could be a parsing error
PyExpression ret = (PyExpression)exprs.get(1);
if (followsNodeOfType(ret.getNode(), PyTokenTypes.IN_KEYWORD)) return ret;
else return null;
}
}
@@ -21,20 +21,15 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.PyStatementList;
/**
* Created by IntelliJ IDEA.
* User: yole
* Date: 29.05.2005
* Time: 21:21:12
* To change this template use File | Settings | File Templates.
* Date: 29.05.2005 (initial)
*/
public class PyForStatementImpl extends PyElementImpl implements PyForStatement {
public class PyForStatementImpl extends PyPartitionedElementImpl implements PyForStatement {
public PyForStatementImpl(ASTNode astNode) {
super(astNode);
}
@@ -44,24 +39,13 @@ public class PyForStatementImpl extends PyElementImpl implements PyForStatement
pyVisitor.visitPyForStatement(this);
}
public PyElsePart getElsePart() {
return (PyElsePart)getPart(PyElementTypes.ELSE_PART);
}
@NotNull
public PyStatementList getStatementList() {
return childToPsiNotNull(PyElementTypes.STATEMENT_LISTS, 0);
}
@Nullable
public PyStatementList getElseStatementList() {
return childToPsi(PyElementTypes.STATEMENT_LISTS, 1);
}
@Nullable
public PyExpression getTargetExpression() {
return childToPsi(PyElementTypes.EXPRESSIONS, 0);
}
@Nullable
public PyExpression getLoopExpression() {
return childToPsi(PyElementTypes.EXPRESSIONS, 1);
public PyForPart getForPart() {
return (PyForPart)getPartNotNull(PyElementTypes.FOR_PART);
}
@Override
@@ -69,25 +53,29 @@ public class PyForStatementImpl extends PyElementImpl implements PyForStatement
@NotNull ResolveState substitutor,
PsiElement lastParent,
@NotNull PsiElement place) {
final PyExpression target = getTargetExpression();
PyForPart forPart = getForPart();
final PyExpression target = forPart.getTarget();
if (target != null && target != lastParent && !target.processDeclarations(processor, substitutor, null, place)) {
return false;
}
final PyStatementList statementList = getStatementList();
if (statementList != lastParent && !statementList.processDeclarations(processor, substitutor, null, place)) {
final PyStatementList statementList = forPart.getStatementList();
if (statementList != null && statementList != lastParent && !statementList.processDeclarations(processor, substitutor, null, place)) {
return false;
}
PyStatementList elseList = getElseStatementList();
if (elseList != null && elseList != lastParent) {
return elseList.processDeclarations(processor, substitutor, null, place);
PyElsePart elsePart = getElsePart();
if (elsePart != null) {
PyStatementList elseList = elsePart.getStatementList();
if (elseList != null && elseList != lastParent) {
return elseList.processDeclarations(processor, substitutor, null, place);
}
}
return true;
}
@NotNull
public Iterable<PyElement> iterateNames() {
PyExpression tgt = getTargetExpression();
PyExpression tgt = getForPart().getTarget();
if (tgt instanceof PyReferenceExpression) return new SingleIterable<PyElement>(tgt);
else {
return PyUtil.flattenedParens(new PyElement[]{tgt});
@@ -0,0 +1,19 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.psi.PyIfPart;
/**
* PyIfPart that represents an 'elif' part.
* User: dcheryasov
* Date: Mar 12, 2009 5:21:11 PM
*/
public class PyIfPartElifImpl extends PyConditionalStatementPartImpl implements PyIfPart {
public PyIfPartElifImpl(ASTNode astNode) {
super(astNode);
}
public boolean isElif() {
return true;
}
}
@@ -0,0 +1,21 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.psi.PyIfPart;
/**
* PyIfPart that represents an 'if' part.
* User: dcheryasov
* Date: Mar 12, 2009 2:33:39 AM
*/
public class PyIfPartIfImpl extends PyConditionalStatementPartImpl implements PyIfPart {
public PyIfPartIfImpl(ASTNode astNode) {
super(astNode);
}
public boolean isElif() {
return false;
}
}
@@ -22,12 +22,8 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyIfStatement;
import com.jetbrains.python.psi.PyStatementList;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
@@ -36,65 +32,61 @@ import org.jetbrains.annotations.Nullable;
* Time: 21:08:06
* To change this template use File | Settings | File Templates.
*/
public class PyIfStatementImpl extends PyElementImpl implements PyIfStatement {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.psi.impl.PyIfStatementImpl");
public class PyIfStatementImpl extends PyPartitionedElementImpl implements PyIfStatement {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.psi.impl.PyIfStatementImpl");
public PyIfStatementImpl(ASTNode astNode) {
super(astNode);
}
public PyIfStatementImpl(ASTNode astNode) {
super(astNode);
}
@Override protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyIfStatement(this);
}
@Override protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyIfStatement(this);
}
@NotNull
public PyIfPart getIfPart() {
return (PyIfPart)getPartNotNull(PyElementTypes.IF_PART_IF);
}
@NotNull public PyExpression[] getConditions() {
return childrenToPsi(PyElementTypes.EXPRESSIONS, PyExpression.EMPTY_ARRAY);
}
@NotNull
public PyIfPart[] getElifParts() {
return childrenToPsi(PyElementTypes.ELIFS, PyIfPart.EMPTY_ARRAY);
}
@NotNull public PyStatementList[] getStatementLists() {
final ASTNode[] conditions = getNode().getChildren(PyElementTypes.EXPRESSIONS);
final PyStatementList[] statementLists = childrenToPsi(PyElementTypes.STATEMENT_LISTS, PyStatementList.EMPTY_ARRAY);
LOG.assertTrue(statementLists.length == conditions.length || statementLists.length == conditions.length+1);
if (statementLists.length > conditions.length) {
final PyStatementList[] result = new PyStatementList[conditions.length];
System.arraycopy(statementLists, 0, result, 0, conditions.length);
return result;
}
return statementLists;
}
public PyElsePart getElsePart() {
return (PyElsePart)getPart(PyElementTypes.ELSE_PART);
}
public @Nullable PyStatementList getElseStatementList() {
final ASTNode[] conditions = getNode().getChildren(PyElementTypes.EXPRESSIONS);
final PyStatementList[] statementLists = childrenToPsi(PyElementTypes.STATEMENT_LISTS, PyStatementList.EMPTY_ARRAY);
LOG.assertTrue(statementLists.length == conditions.length || statementLists.length == conditions.length+1);
if (statementLists.length > conditions.length) {
return statementLists [statementLists.length-1];
}
return null;
}
@Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState substitutor,
PsiElement lastParent,
@NotNull PsiElement place)
{
if (lastParent != null) {
return true;
}
PyStatementList[] statementLists = getStatementLists();
for (PyStatementList statementList: statementLists) {
if (!statementList.processDeclarations(processor, substitutor, lastParent, place)) {
return false;
}
}
PyStatementList elseList = getElseStatementList();
//noinspection RedundantIfStatement
if (elseList != null && !elseList.processDeclarations(processor, substitutor, lastParent, place)) {
if (lastParent != null) {
return true;
}
for (PyStatementPart part : getParts()) {
PyStatementList stmtList = part.getStatementList();
if (stmtList != null && !stmtList.processDeclarations(processor, substitutor, lastParent, place)) {
return false;
}
return true;
}
/*
PyStatementList[] statementLists = getStatementLists();
for (PyStatementList statementList: statementLists) {
if (!statementList.processDeclarations(processor, substitutor, lastParent, place)) {
return false;
}
}
PyStatementList elseList = getElseStatementList();
//noinspection RedundantIfStatement
if (elseList != null && !elseList.processDeclarations(processor, substitutor, lastParent, place)) {
return false;
}
*/
return true;
}
}
@@ -0,0 +1,39 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyElementType;
import com.jetbrains.python.psi.PyStatementPart;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Common parts functionality.
* User: dcheryasov
* Date: Mar 19, 2009 2:51:15 AM
*/
public class PyPartitionedElementImpl extends PyElementImpl {
public PyPartitionedElementImpl(ASTNode astNode) {
super(astNode);
}
@NotNull
PyStatementPart[] getParts() {
return childrenToPsi(PyElementTypes.PARTS, PyStatementPart.EMPTY_ARRAY);
}
@Nullable
protected PyStatementPart getPart(PyElementType which) {
ASTNode n = getNode().findChildByType(which);
if (n == null) return null;
return (PyStatementPart)n.getPsi();
}
@NotNull
protected PyStatementPart getPartNotNull(PyElementType which) {
ASTNode n = getNode().findChildByType(which);
assert n != null;
return (PyStatementPart)n.getPsi();
}
}
@@ -0,0 +1,25 @@
package com.jetbrains.python.psi.impl;
import com.jetbrains.python.psi.PyStatementPart;
import com.jetbrains.python.psi.PyStatementList;
import com.jetbrains.python.PyElementTypes;
import com.intellij.lang.ASTNode;
/**
* Abstract statement part implementation; extracts the statements list.
* User: dcheryasov
* Date: Mar 16, 2009 4:36:50 AM
*/
public abstract class PyStatementPartImpl extends PyElementImpl implements PyStatementPart {
protected PyStatementPartImpl(ASTNode astNode) {
super(astNode);
}
public PyStatementList getStatementList() {
ASTNode n = getNode().findChildByType(PyElementTypes.STATEMENT_LISTS);
if (n != null) {
return (PyStatementList)n.getPsi();
}
return null;
}
}
@@ -22,13 +22,8 @@ import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.tree.TokenSet;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyExceptBlock;
import com.jetbrains.python.psi.PyStatementList;
import com.jetbrains.python.psi.PyTryExceptStatement;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
@@ -37,8 +32,8 @@ import org.jetbrains.annotations.Nullable;
* Time: 23:14:57
* To change this template use File | Settings | File Templates.
*/
public class PyTryExceptStatementImpl extends PyElementImpl implements PyTryExceptStatement {
private static final TokenSet EXCEPT_BLOCKS = TokenSet.create(PyElementTypes.EXCEPT_BLOCK);
public class PyTryExceptStatementImpl extends PyPartitionedElementImpl implements PyTryExceptStatement {
private static final TokenSet EXCEPT_BLOCKS = TokenSet.create(PyElementTypes.EXCEPT_PART);
public PyTryExceptStatementImpl(ASTNode astNode) {
super(astNode);
@@ -50,45 +45,22 @@ public class PyTryExceptStatementImpl extends PyElementImpl implements PyTryExce
}
@NotNull
public PyStatementList getTryStatementList() {
return childToPsiNotNull(PyElementTypes.STATEMENT_LISTS, 0);
public PyExceptPart[] getExceptParts() {
return childrenToPsi(EXCEPT_BLOCKS, PyExceptPart.EMPTY_ARRAY);
}
public PyElsePart getElsePart() {
return (PyElsePart)getPart(PyElementTypes.ELSE_PART);
}
@NotNull
public PyExceptBlock[] getExceptBlocks() {
return childrenToPsi(EXCEPT_BLOCKS, PyExceptBlock.EMPTY_ARRAY);
public PyTryPart getTryPart() {
return (PyTryPart)getPartNotNull(PyElementTypes.TRY_PART);
}
@Nullable
public PyStatementList getElseStatementList() {
final ASTNode node = getNode().findChildByType(PyTokenTypes.ELSE_KEYWORD);
if (node != null) {
return (PyStatementList)findNextChildOfType(node, PyElementTypes.STATEMENT_LISTS);
}
return null;
}
@Nullable
public PyStatementList getFinallyStatementList() {
final ASTNode node = getNode().findChildByType(PyTokenTypes.FINALLY_KEYWORD);
if (node != null) {
return (PyStatementList)findNextChildOfType(node, PyElementTypes.STATEMENT_LISTS);
}
return null;
}
@Nullable
private static PsiElement findNextChildOfType(ASTNode node, final TokenSet matchTokens) {
ASTNode sibling = node.getTreeNext();
while (sibling != null) {
if (matchTokens.contains(sibling.getElementType())) {
return sibling.getPsi();
}
sibling = sibling.getTreeNext();
}
return null;
public PyFinallyPart getFinallyPart() {
return (PyFinallyPart)getPart(PyElementTypes.FINALLY_PART);
}
@Override
@@ -96,21 +68,28 @@ public class PyTryExceptStatementImpl extends PyElementImpl implements PyTryExce
@NotNull ResolveState substitutor,
PsiElement lastParent,
@NotNull PsiElement place) {
final PyStatementList tryStatementList = getTryStatementList();
if (tryStatementList != lastParent && !tryStatementList.processDeclarations(processor, substitutor, null, place)) {
/*
final PyStatementList tryStatementList = getTryPart().getStatementList();
if (tryStatementList != null && tryStatementList != lastParent && !tryStatementList.processDeclarations(processor, substitutor, null, place)) {
return false;
}
*/
for (PyExceptBlock block : getExceptBlocks()) {
if (block != lastParent && !block.processDeclarations(processor, substitutor, null, place)) {
for (PyStatementPart part : /*getExceptParts()*/ getParts()) {
if (part != lastParent && !part.processDeclarations(processor, substitutor, null, place)) {
return false;
}
}
PyStatementList elseStatementList = getElseStatementList();
if (elseStatementList != null && elseStatementList != lastParent) {
return elseStatementList.processDeclarations(processor, substitutor, null, place);
/*
final PyElsePart elsePart = getElsePart();
if (elsePart != null) {
PyStatementList elseStatementList = elsePart.getStatementList();
if (elseStatementList != null && elseStatementList != lastParent) {
return elseStatementList.processDeclarations(processor, substitutor, null, place);
}
}
*/
return true;
}
}
@@ -0,0 +1,14 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.psi.PyTryPart;
/**
* User: dcheryasov
* Date: Mar 16, 2009 6:56:55 AM
*/
public class PyTryPartImpl extends PyStatementPartImpl implements PyTryPart {
public PyTryPartImpl(ASTNode astNode) {
super(astNode);
}
}
@@ -0,0 +1,14 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.psi.PyWhilePart;
/**
* User: dcheryasov
* Date: Mar 16, 2009 4:33:32 AM
*/
public class PyWhilePartImpl extends PyConditionalStatementPartImpl implements PyWhilePart {
public PyWhilePartImpl(ASTNode astNode) {
super(astNode);
}
}
@@ -20,12 +20,9 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyStatementList;
import com.jetbrains.python.psi.PyWhileStatement;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -34,39 +31,51 @@ import com.jetbrains.python.psi.PyWhileStatement;
* Time: 15:47:44
* To change this template use File | Settings | File Templates.
*/
public class PyWhileStatementImpl extends PyElementImpl implements PyWhileStatement {
public PyWhileStatementImpl(ASTNode astNode) {
super(astNode);
}
public class PyWhileStatementImpl extends PyPartitionedElementImpl implements PyWhileStatement {
public PyWhileStatementImpl(ASTNode astNode) {
super(astNode);
}
@Override protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyWhileStatement(this);
}
@Override protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyWhileStatement(this);
}
public @NotNull PyStatementList getStatementList() {
return childToPsiNotNull(PyElementTypes.STATEMENT_LISTS, 0);
}
@NotNull
public PyWhilePart getWhilePart() {
return (PyWhilePart)getPartNotNull(PyElementTypes.WHILE_PART);
}
public @Nullable PyStatementList getElseStatementList() {
return childToPsi(PyElementTypes.STATEMENT_LISTS, 1);
}
public PyElsePart getElsePart() {
return (PyElsePart)getPart(PyElementTypes.ELSE_PART);
}
@Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState substitutor,
PsiElement lastParent,
@NotNull PsiElement place)
{
if (lastParent != null) {
return true;
}
if (lastParent != null) return true;
if (!getStatementList().processDeclarations(processor, substitutor, null, place)) {
return false;
for (PyStatementPart part : getParts()) {
PyStatementList stmtList = part.getStatementList();
if (stmtList != null) {
return stmtList.processDeclarations(processor, substitutor, null, place);
}
PyStatementList elseList = getElseStatementList();
}
/*
final PyStatementList whileStmts = getWhilePart().getStatementList();
if (whileStmts != null && !whileStmts.processDeclarations(processor, substitutor, null, place)) {
return false;
}
final PyElsePart elsePart = getElsePart();
if (elsePart != null) {
PyStatementList elseList = elsePart.getStatementList();
if (elseList != null) {
return elseList.processDeclarations(processor, substitutor, null, place);
}
return true;
}
*/
return true;
}
}
@@ -48,7 +48,7 @@ public class AssignTargetAnnotator extends PyAnnotator {
}
}
@Override public void visitPyExceptBlock(final PyExceptBlock node) {
@Override public void visitPyExceptBlock(final PyExceptPart node) {
PyExpression target = node.getTarget();
if (target != null) {
target.accept(new ExprVisitor(Operation.Except));
@@ -56,7 +56,7 @@ public class AssignTargetAnnotator extends PyAnnotator {
}
@Override public void visitPyForStatement(final PyForStatement node) {
PyExpression target = node.getTargetExpression();
PyExpression target = node.getForPart().getTarget();
if (target != null) {
target.accept(new ExprVisitor(Operation.For));
}
@@ -16,42 +16,36 @@
package com.jetbrains.python.validation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.python.PyBundle.message;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyBreakStatement;
import com.jetbrains.python.psi.PyContinueStatement;
import com.jetbrains.python.psi.PyTryExceptStatement;
import com.jetbrains.python.psi.*;
/**
* Created by IntelliJ IDEA.
* User: yole
* Date: 13.06.2005
* Time: 15:01:05
* To change this template use File | Settings | File Templates.
*/
public class BreakContinueAnnotator extends PyAnnotator {
@Override public void visitPyBreakStatement(final PyBreakStatement node) {
if (node.getContainingElement(PyElementTypes.LOOPS) == null) {
getHolder().createErrorAnnotation(node, message("ANN.break.outside.loop"));
}
@Override public void visitPyBreakStatement(final PyBreakStatement node) {
if (node.getContainingElement(PyElementTypes.LOOPS) == null) {
getHolder().createErrorAnnotation(node, message("ANN.break.outside.loop"));
}
}
@Override public void visitPyContinueStatement(final PyContinueStatement node) {
if (node.getContainingElement(PyElementTypes.LOOPS) == null) {
getHolder().createErrorAnnotation(node, message("ANN.continue.outside.loop"));
return;
}
PyTryExceptStatement tryStatement = node.getContainingElement(PyTryExceptStatement.class);
if (tryStatement != null) {
PsiElement parent = node.getParent();
while (parent != null) {
if (parent == tryStatement.getFinallyStatementList()) {
getHolder().createErrorAnnotation(node, message("ANN.cant.continue.in.finally"));
break;
}
parent = parent.getParent();
}
}
@Override public void visitPyContinueStatement(final PyContinueStatement node) {
final PyElement loopStmt = node.getContainingElement(PyElementTypes.LOOPS); // closest loop to contain the 'continue'
if (loopStmt == null) {
getHolder().createErrorAnnotation(node, message("ANN.continue.outside.loop"));
return;
}
PyTryExceptStatement tryStmt = node.getContainingElement(PyTryExceptStatement.class);
if (tryStmt != null) {
final PyFinallyPart finallyPart = tryStmt.getFinallyPart();
if (finallyPart != null && PsiTreeUtil.isAncestor(loopStmt, finallyPart, true)) {
getHolder().createErrorAnnotation(node, message("ANN.cant.continue.in.finally"));
}
}
}
}
@@ -17,7 +17,7 @@
package com.jetbrains.python.validation;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.PyExceptBlock;
import com.jetbrains.python.psi.PyExceptPart;
import com.jetbrains.python.psi.PyTryExceptStatement;
/**
@@ -27,13 +27,13 @@ import com.jetbrains.python.psi.PyTryExceptStatement;
*/
public class TryExceptAnnotator extends PyAnnotator {
@Override public void visitPyTryExceptStatement(final PyTryExceptStatement node) {
PyExceptBlock[] exceptBlocks = node.getExceptBlocks();
PyExceptPart[] exceptParts = node.getExceptParts();
boolean haveDefaultExcept = false;
for(PyExceptBlock block: exceptBlocks) {
for(PyExceptPart part : exceptParts) {
if (haveDefaultExcept) {
getHolder().createErrorAnnotation(block, PyBundle.message("ANN.default.except.must.be.last"));
getHolder().createErrorAnnotation(part, PyBundle.message("ANN.default.except.must.be.last"));
}
if (block.getExceptClass() == null) {
if (part.getExceptClass() == null) {
haveDefaultExcept = true;
}
}
+20
View File
@@ -0,0 +1,20 @@
if 0:
pass
if 1:
pass
else:
pass
if 2:
pass
elif 3:
pass
if 4:
pass
elif 5:
pass
else:
1
+88
View File
@@ -0,0 +1,88 @@
PyFile:IfStatement.py
PyIfStatement
PyIfPartIf
PsiElement(Py:IF_KEYWORD)('if')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('0')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyIfStatement
PyIfPartIf
PsiElement(Py:IF_KEYWORD)('if')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n')
PyElsePart
PsiElement(Py:ELSE_KEYWORD)('else')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyIfStatement
PyIfPartIf
PsiElement(Py:IF_KEYWORD)('if')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n')
PyIfPartElif
PsiElement(Py:ELIF_KEYWORD)('elif')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('3')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyIfStatement
PyIfPartIf
PsiElement(Py:IF_KEYWORD)('if')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('4')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace(' \n')
PyIfPartElif
PsiElement(Py:ELIF_KEYWORD)('elif')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('5')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n')
PyElsePart
PsiElement(Py:ELSE_KEYWORD)('else')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
+17 -15
View File
@@ -1,14 +1,15 @@
PyFile:TryExceptFinally.py
PyTryExceptStatement
PsiElement(Py:TRY_KEYWORD)('try')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PyTryPart
PsiElement(Py:TRY_KEYWORD)('try')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace('\n')
PyExceptBlock
PyExceptPart
PsiElement(Py:EXCEPT_KEYWORD)('except')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
@@ -17,10 +18,11 @@ PyFile:TryExceptFinally.py
PyReferenceExpression: y
PsiElement(Py:IDENTIFIER)('y')
PsiWhiteSpace('\n')
PsiElement(Py:FINALLY_KEYWORD)('finally')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: z
PsiElement(Py:IDENTIFIER)('z')
PyFinallyPart
PsiElement(Py:FINALLY_KEYWORD)('finally')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: z
PsiElement(Py:IDENTIFIER)('z')
+16 -14
View File
@@ -1,17 +1,19 @@
PyFile:TryFinally.py
PyTryExceptStatement
PsiElement(Py:TRY_KEYWORD)('try')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PyTryPart
PsiElement(Py:TRY_KEYWORD)('try')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace('\n')
PsiElement(Py:FINALLY_KEYWORD)('finally')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: z
PsiElement(Py:IDENTIFIER)('z')
PyFinallyPart
PsiElement(Py:FINALLY_KEYWORD)('finally')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyReferenceExpression: z
PsiElement(Py:IDENTIFIER)('z')
+5
View File
@@ -0,0 +1,5 @@
# for stmt
<stmt>for <target>x in <source>list:
pass
e<else>lse:
pass
+3
View File
@@ -0,0 +1,3 @@
# only if
<the_if>if <the_cond>True:
pass
+7
View File
@@ -0,0 +1,7 @@
# if, elif, and else
<the_if>if <the_cond>True:
<then_stmt>pass
elif <elif_cond>0:
<elif_stmt>pass
else:
<else_stmt>pass
+5
View File
@@ -0,0 +1,5 @@
# if and else
<the_if>if <the_cond>True:
<then_stmt>pass
else:
<else_stmt>pass
+12
View File
@@ -0,0 +1,12 @@
# try and friends
<stmt>try:
p<body>ass
e<ex1>xcept ArithmeticError, e:
pass
e<ex2>xcept:
pass
e<else>lse:
pass
f<finally>inally:
pass
+5
View File
@@ -0,0 +1,5 @@
# while stmt
<stmt>while <cond>True:
pass
e<else>lse:
pass
@@ -0,0 +1,103 @@
package com.jetbrains.python;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.PsiTestCase;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import java.io.File;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Base for cases that need marked PSI elements.
* User: dcheryasov
* Date: Mar 14, 2009 11:57:52 PM
*/
public abstract class MarkedTestCase extends PsiTestCase {
/**
* Marker "as expected", any alphanumeric sting in angle brackets.
*/
public final @NonNls String MARKER = "<[a-zA-Z0-9_]+>";
/**
* Uses MARKER as regexp.
* @see #configureByFileText(String, String, String)
* @param filePath file to load and parse
* @return a mapping of markers to PSI elements
* @throws Exception
*/
protected Map<String, PsiElement> configureByFile(@NonNls String filePath) throws Exception {
return configureByFile(filePath, MARKER);
}
/**
* Like <a href="#configureByFileText(String, String, String)">configureByFileText</a>, but with a file to be read.
* @param filePath file to read and parse
* @param markerRegexp regexp for markers
* @return a mapping of markers to PSI elements
* @throws Exception
*/
protected Map<String, PsiElement> configureByFile(@NonNls String filePath, @NonNls String markerRegexp)
throws Exception
{
final String fullPath = getTestDataPath() + filePath;
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fullPath.replace(File.separatorChar, '/'));
assertNotNull("file " + filePath + " not found", vFile);
String fileText = StringUtil.convertLineSeparators(VfsUtil.loadText(vFile), "\n");
final String fileName = vFile.getName();
return configureByFileText(fileText, fileName, markerRegexp);
}
/**
* Typically a text is marked with patterns: "foo &lt;ref1>bar() + &lt;ref2>baz", etc, and the result is
* a map where strings "&lt;ref1>" and "&lt;ref2>" are mapped to PSI elements for "bar" and "baz".
* @param fileText text to parse
* @param fileName name to give to the PSI file
* @param markerRegexp regexp to detect markers in the text
* @return mapping of markers to the PSI elements
* @throws Exception
*/
protected Map<String, PsiElement> configureByFileText(String fileText, String fileName, @NonNls String markerRegexp)
throws Exception
{
// build a map of marks to positions, and the text with marks stripped
Pattern pat = Pattern.compile(markerRegexp);
Matcher mat = pat.matcher(fileText);
int rest_index = 0; // from here on fileText is not yet looked at
Map<String, Integer> offsets = new HashMap<String, Integer>();
StringBuffer text = new StringBuffer();
while (mat.find(rest_index)) {
String mark = mat.group();
CharSequence prev_part = fileText.subSequence(rest_index, mat.start());
text.append(prev_part);
offsets.put(mark, text.length());
rest_index = mat.end();
}
if (rest_index < fileText.length()) text.append(fileText.substring(rest_index));
// create a file and map marks to PSI elements
Map<String, PsiElement> result = new HashMap<String, PsiElement>();
myFile = createFile(myModule, fileName, text.toString());
for (Map.Entry<String, Integer> entry : offsets.entrySet()) {
result.put(entry.getKey(), myFile.findElementAt(entry.getValue()));
}
return result;
}
protected Map<String, PsiElement> loadTest() throws Exception {
String fname = getTestName(false) + ".py";
return configureByFile(fname);
}
protected abstract String getTestDataPath();
}
@@ -19,6 +19,10 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(true);
}
public void testIfStatement() throws Exception {
doTest(true);
}
public void testConditionalExpression() throws Exception {
doTest(true);
}
@@ -0,0 +1,159 @@
package com.jetbrains.python;
import com.intellij.openapi.application.PathManager;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.*;
import java.util.Map;
/**
* Tests statement parts.
* User: dcheryasov
* Date: Mar 15, 2009 3:11:01 AM
*/
public class StatementPartsTest extends MarkedTestCase {
protected String getTestDataPath() {
return PathManager.getHomePath() + "/plugins/python/testData/psi/parts/";
}
public void testIf() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals(2, marks.size());
PsiElement elt = marks.get("<the_if>").getParent().getParent(); // if_keyword -> if_part -> if_stmt
assertTrue(elt instanceof PyIfStatement);
PyIfStatement if_stmt = (PyIfStatement)elt;
PyIfPart if_part = if_stmt.getIfPart();
assertFalse(if_part.isElif());
PyExpression if_cond = if_part.getCondition();
assertEquals(marks.get("<the_cond>").getParent(), if_cond);
}
public void testIfElse() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals(4, marks.size());
PsiElement elt = marks.get("<the_if>").getParent().getParent(); // if_keyword -> if_part -> if_stmt
assertTrue(elt instanceof PyIfStatement);
PyIfStatement if_stmt = (PyIfStatement)elt;
PyIfPart if_part = if_stmt.getIfPart();
assertFalse(if_part.isElif());
PyExpression if_cond = if_part.getCondition();
assertEquals(marks.get("<the_cond>").getParent(), if_cond);
PyStatementList stmt_list = if_part.getStatementList();
assertNotNull(stmt_list);
assertEquals(marks.get("<then_stmt>").getParent().getParent(), stmt_list); // keyword -> stmt -> stmt_list
PyElsePart else_part = if_stmt.getElsePart();
assertNotNull(else_part);
stmt_list = else_part.getStatementList();
assertNotNull(stmt_list);
assertEquals(marks.get("<else_stmt>").getParent().getParent(), stmt_list); // keyword -> stmt -> stmt_list
}
public void testIfElifElse() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals(6, marks.size());
PsiElement elt = marks.get("<the_if>").getParent().getParent(); // if_keyword -> if_part -> if_stmt
assertTrue(elt instanceof PyIfStatement);
PyIfStatement if_stmt = (PyIfStatement)elt;
PyIfPart if_part = if_stmt.getIfPart();
assertFalse(if_part.isElif());
PyExpression if_cond = if_part.getCondition();
assertEquals(marks.get("<the_cond>").getParent(), if_cond);
PyStatementList stmt_list = if_part.getStatementList();
assertNotNull(stmt_list);
assertEquals(marks.get("<then_stmt>").getParent().getParent(), stmt_list); // keyword -> stmt -> stmt_list
PyElsePart else_part = if_stmt.getElsePart();
assertNotNull(else_part);
stmt_list = else_part.getStatementList();
assertNotNull(stmt_list);
assertEquals(marks.get("<else_stmt>").getParent().getParent(), stmt_list); // keyword -> stmt -> stmt_list
PyIfPart elif_part = if_stmt.getElifParts()[0];
assertTrue(elif_part.isElif());
if_cond = elif_part.getCondition();
assertEquals(marks.get("<elif_cond>").getParent(), if_cond);
stmt_list = elif_part.getStatementList();
assertNotNull(stmt_list);
assertEquals(marks.get("<elif_stmt>").getParent().getParent(), stmt_list); // keyword -> stmt -> stmt_list
}
public void testWhile() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals(3, marks.size());
PsiElement elt = marks.get("<stmt>").getParent().getParent(); // keyword -> part -> stmt
assertTrue(elt instanceof PyWhileStatement);
PyWhileStatement while_stmt = (PyWhileStatement)elt;
PyWhilePart while_part = while_stmt.getWhilePart();
PyExpression cond = while_part.getCondition();
assertEquals(marks.get("<cond>").getParent(), cond);
elt = marks.get("<else>").getParent(); // keyword -> part
assertTrue(elt instanceof PyElsePart);
assertEquals(while_stmt.getElsePart(), elt);
}
public void testFor() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals(4, marks.size());
PsiElement elt = marks.get("<stmt>").getParent().getParent(); // keyword -> part -> stmt
assertTrue(elt instanceof PyForStatement);
PyForStatement stmt = (PyForStatement)elt;
PyForPart for_part = stmt.getForPart();
PyExpression target = for_part.getTarget();
assertEquals(marks.get("<target>").getParent(), target); // ident -> expr
PyExpression source = for_part.getSource();
assertEquals(marks.get("<source>").getParent(), source); // ident -> expr
elt = marks.get("<else>").getParent(); // keyword -> part
assertTrue(elt instanceof PyElsePart);
assertEquals(stmt.getElsePart(), elt);
}
public void testTry() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals(6, marks.size());
PsiElement elt = marks.get("<stmt>").getParent().getParent(); // keyword -> part -> stmt
assertTrue(elt instanceof PyTryExceptStatement);
PyTryExceptStatement stmt = (PyTryExceptStatement)elt;
PyTryPart try_part = stmt.getTryPart();
PyStatementList stmt_list = try_part.getStatementList();
assertNotNull(stmt_list);
assertEquals(marks.get("<body>").getParent().getParent(), stmt_list); // keyword -> stmt -> stmt_list
PyExceptPart exc_part = stmt.getExceptParts()[0];
assertEquals("ArithmeticError", exc_part.getExceptClass().getText());
assertEquals(marks.get("<ex1>").getParent(), exc_part);
exc_part = (PyExceptPart)marks.get("<ex2>").getParent(); // keyword -> part
assertEquals(stmt.getExceptParts()[1], exc_part);
assertNull(exc_part.getExceptClass());
elt = marks.get("<else>").getParent(); // keyword -> part
assertTrue(elt instanceof PyElsePart);
assertEquals(stmt.getElsePart(), elt);
elt = marks.get("<finally>").getParent(); // keyword -> part
assertTrue(elt instanceof PyFinallyPart);
assertEquals(stmt.getFinallyPart(), elt);
}
}