diff --git a/python/src/com/jetbrains/python/PyElementTypes.java b/python/src/com/jetbrains/python/PyElementTypes.java index 86040c3fa558..863a817c96d2 100644 --- a/python/src/com/jetbrains/python/PyElementTypes.java +++ b/python/src/com/jetbrains/python/PyElementTypes.java @@ -5,6 +5,7 @@ import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.*; import com.jetbrains.python.psi.impl.stubs.*; import com.jetbrains.python.psi.stubs.*; +import com.jetbrains.python.psi.PyDecorator; public interface PyElementTypes { @@ -12,18 +13,23 @@ public interface PyElementTypes { PyStubElementType CLASS_DECLARATION = new PyClassElementType(); PyStubElementType PARAMETER_LIST = new PyParameterListElementType(); + //PyElementType DECORATOR_LIST = new PyElementType("DECORATOR", PyDecoratorListImpl.class); + PyStubElementType DECORATOR_LIST = new PyDecoratorListElementType(); + TokenSet PARAMETER_LIST_SET = TokenSet.create(PARAMETER_LIST); PyStubElementType FORMAL_PARAMETER = new PyFormalParameterElementType(); + PyStubElementType DECORATOR_CALL = new PyDecoratorCallElementType(); TokenSet FORMAL_PARAMETER_SET = TokenSet.create(FORMAL_PARAMETER); - PyElementType DECORATED_FUNCTION_DECLARATION = new PyElementType("DECORATED_FUNCTION_DECLARATION", PyDecoratedFunctionImpl.class); + // Deprecated PyElementType DECORATED_FUNCTION_DECLARATION = new PyElementType("DECORATED_FUNCTION_DECLARATION", PyDecoratedFunctionImpl.class); 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 PRINT_TARGET = new PyElementType("PRINT_TARGET", PyPrintTargetImpl.class); + PyElementType DECORATOR = new PyElementType("DECORATOR", PyDecoratorImpl.class); // Statements PyElementType EXPRESSION_STATEMENT = new PyElementType("EXPRESSION_STATEMENT", PyExpressionStatementImpl.class); diff --git a/python/src/com/jetbrains/python/parsing/FunctionParsing.java b/python/src/com/jetbrains/python/parsing/FunctionParsing.java index 836b2f535301..c68fea1a87bb 100644 --- a/python/src/com/jetbrains/python/parsing/FunctionParsing.java +++ b/python/src/com/jetbrains/python/parsing/FunctionParsing.java @@ -19,6 +19,7 @@ package com.jetbrains.python.parsing; import com.intellij.lang.PsiBuilder; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.tree.IElementType; +import static com.jetbrains.python.PyBundle.message; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyTokenTypes; @@ -35,44 +36,56 @@ public class FunctionParsing extends Parsing { public void parseFunctionDeclaration() { assertCurrentToken(PyTokenTypes.DEF_KEYWORD); final PsiBuilder.Marker functionMarker = myBuilder.mark(); + parseFunctionInnards(functionMarker); + } + + protected void parseFunctionInnards(PsiBuilder.Marker functionMarker) { myBuilder.advanceLexer(); if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) { myBuilder.advanceLexer(); } else { - myBuilder.error("function name expected"); + myBuilder.error(message("PARSE.expected.func.name")); } parseParameterList(); - checkMatches(PyTokenTypes.COLON, "colon expected"); + checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon")); getStatementParser().parseSuite(functionMarker, PyElementTypes.FUNCTION_DECLARATION); } public void parseDecoratedFunctionDeclaration() { - assertCurrentToken(PyTokenTypes.AT); + assertCurrentToken(PyTokenTypes.AT); // ??? need this? final PsiBuilder.Marker functionMarker = myBuilder.mark(); - myBuilder.advanceLexer(); - getStatementParser().parseDottedName(); - if (myBuilder.getTokenType() == PyTokenTypes.LPAR) { - getExpressionParser().parseArgumentList(myBuilder); + final PsiBuilder.Marker decoListMarker = myBuilder.mark(); + boolean decorated = false; + while (myBuilder.getTokenType() == PyTokenTypes.AT) { + PsiBuilder.Marker decoratorMarker = myBuilder.mark(); + myBuilder.advanceLexer(); + getStatementParser().parseDottedName(); + if (myBuilder.getTokenType() == PyTokenTypes.LPAR) { + getExpressionParser().parseArgumentList(myBuilder); + } + checkMatches(PyTokenTypes.STATEMENT_BREAK, message("PARSE.expected.statement.break")); + decoratorMarker.done(PyElementTypes.DECORATOR_CALL); + decorated = true; } - checkMatches(PyTokenTypes.STATEMENT_BREAK, "statement break expected"); - if (myBuilder.getTokenType() == PyTokenTypes.AT) { - parseDecoratedFunctionDeclaration(); - } - else if (myBuilder.getTokenType() == PyTokenTypes.DEF_KEYWORD) { - parseFunctionDeclaration(); + if (decorated) decoListMarker.done(PyElementTypes.DECORATOR_LIST); + //else decoListMarker.rollbackTo(); + if (myBuilder.getTokenType() == PyTokenTypes.DEF_KEYWORD) { + parseFunctionInnards(functionMarker); // it calls functionMarker.done() } else { - myBuilder.error("'def' or '@' expected"); + myBuilder.error(message("PARSE.expected.@.or.def")); + PsiBuilder.Marker parameterList = myBuilder.mark(); // To have non-empty parameters list at all the time. + parameterList.done(PyElementTypes.PARAMETER_LIST); + functionMarker.done(PyElementTypes.FUNCTION_DECLARATION); } - functionMarker.done(PyElementTypes.DECORATED_FUNCTION_DECLARATION); } private void parseParameterList() { final PsiBuilder.Marker parameterList; if (myBuilder.getTokenType() != PyTokenTypes.LPAR) { - myBuilder.error("( expected"); + myBuilder.error(message("PARSE.expected.lpar")); parameterList = myBuilder.mark(); // To have non-empty parameters list at all the time. parameterList.done(PyElementTypes.PARAMETER_LIST); return; @@ -100,7 +113,7 @@ public class FunctionParsing extends Parsing { parseParameterSubList(); } else { - myBuilder.error(", or ( or ) expected"); + myBuilder.error(message("PARSE.expected.comma.lpar.rpar")); break; } } @@ -120,7 +133,7 @@ public class FunctionParsing extends Parsing { parameter.done(PyElementTypes.FORMAL_PARAMETER); } else { - myBuilder.error("formal parameter name expected"); + myBuilder.error(message("PARSE.expected.formal.param.name")); parameter.rollbackTo(); } } @@ -149,7 +162,7 @@ public class FunctionParsing extends Parsing { break; } if (myBuilder.getTokenType() != PyTokenTypes.COMMA) { - myBuilder.error(", or ( or ) expected"); + myBuilder.error(message("PARSE.expected.comma.lpar.rpar")); break; } myBuilder.advanceLexer(); diff --git a/python/src/com/jetbrains/python/psi/PyFunction.java b/python/src/com/jetbrains/python/psi/PyFunction.java index dcff13c65cb9..5dbcd1caddc5 100644 --- a/python/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/src/com/jetbrains/python/psi/PyFunction.java @@ -50,4 +50,7 @@ public interface PyFunction extends PsiNamedElement, PyElement, NameDefiner, PyD @Nullable PyClass getContainingClass(); + + @Nullable + PyDecoratorList getDecoratorList(); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index ae5f740c00a8..68ddbe304f38 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -80,7 +80,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp @NotNull public PyParameterList getParameterList() { - return (PyParameterList) getRequiredStubOrPsiChild(PyElementTypes.PARAMETER_LIST); + return getRequiredStubOrPsiChild(PyElementTypes.PARAMETER_LIST); } @NotNull @@ -109,6 +109,11 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp return null; } + @Nullable + public PyDecoratorList getDecoratorList() { + return getStubOrPsiChild(PyElementTypes.DECORATOR_LIST); // PsiTreeUtil.getChildOfType(this, PyDecoratorList.class); + } + @Override protected void acceptPyVisitor(PyElementVisitor pyVisitor) { pyVisitor.visitPyFunction(this);