Parse and highlight the 'await' keyword (PY-16094)

This commit is contained in:
Andrey Vlasovskikh
2015-09-08 14:21:52 +03:00
parent 4439b41170
commit c855959143
11 changed files with 90 additions and 15 deletions
@@ -74,6 +74,7 @@ public class PyTokenTypes {
public static final PyElementType NONLOCAL_KEYWORD = new PyElementType("NONLOCAL_KEYWORD");
public static final PyElementType DEBUG_KEYWORD = new PyElementType("DEBUG_KEYWORD");
public static final PyElementType ASYNC_KEYWORD = new PyElementType("ASYNC_KEYWORD");
public static final PyElementType AWAIT_KEYWORD = new PyElementType("AWAIT_KEYWORD", "__await__");
public static final PyElementType INTEGER_LITERAL = new PyElementType("INTEGER_LITERAL");
public static final PyElementType FLOAT_LITERAL = new PyElementType("FLOAT_LITERAL");
@@ -130,7 +130,8 @@ public interface PyElementTypes {
PyTokenTypes.MINUS, PyTokenTypes.MULT, PyTokenTypes.AT, PyTokenTypes.FLOORDIV, PyTokenTypes.DIV,
PyTokenTypes.PERC, PyTokenTypes.EXP);
TokenSet UNARY_OPS = TokenSet.create(PyTokenTypes.NOT_KEYWORD, PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.TILDE);
TokenSet UNARY_OPS = TokenSet.create(PyTokenTypes.NOT_KEYWORD, PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.TILDE,
PyTokenTypes.AWAIT_KEYWORD);
// Parts
PyElementType IF_PART_IF = new PyElementType("IF_IF", PyIfPartIfImpl.class);
@@ -73,7 +73,7 @@ public class PythonTokenSetContributor extends PythonDialectsTokenSetContributor
LAMBDA_KEYWORD, NOT_KEYWORD, OR_KEYWORD, PASS_KEYWORD, PRINT_KEYWORD,
RAISE_KEYWORD, RETURN_KEYWORD, TRY_KEYWORD, WITH_KEYWORD, WHILE_KEYWORD,
YIELD_KEYWORD,
NONE_KEYWORD, TRUE_KEYWORD, FALSE_KEYWORD, NONLOCAL_KEYWORD, DEBUG_KEYWORD, ASYNC_KEYWORD);
NONE_KEYWORD, TRUE_KEYWORD, FALSE_KEYWORD, NONLOCAL_KEYWORD, DEBUG_KEYWORD, ASYNC_KEYWORD, AWAIT_KEYWORD);
}
@NotNull
@@ -915,7 +915,7 @@ public class ExpressionParsing extends Parsing {
private boolean parsePowerExpression(boolean isTargetExpression) {
PsiBuilder.Marker expr = myBuilder.mark();
if (!parseMemberExpression(isTargetExpression)) {
if (!parseAwaitExpression(isTargetExpression)) {
expr.drop();
return false;
}
@@ -933,4 +933,19 @@ public class ExpressionParsing extends Parsing {
return true;
}
private boolean parseAwaitExpression(boolean isTargetExpression) {
if (atToken(PyTokenTypes.AWAIT_KEYWORD)) {
PsiBuilder.Marker expr = myBuilder.mark();
myBuilder.advanceLexer();
if (!parseMemberExpression(isTargetExpression)) {
myBuilder.error(message("PARSE.expected.expression"));
}
expr.done(PyElementTypes.PREFIX_EXPRESSION);
return true;
}
else {
return parseMemberExpression(isTargetExpression);
}
}
}
@@ -51,6 +51,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
@NonNls protected static final String TOK_NONLOCAL = "nonlocal";
@NonNls protected static final String TOK_EXEC = "exec";
@NonNls protected static final String TOK_ASYNC = "async";
@NonNls protected static final String TOK_AWAIT = "await";
private static final String EXPRESSION_EXPECTED = "Expression expected";
public static final String IDENTIFIER_EXPECTED = "Identifier expected";
@@ -937,9 +938,16 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (isWordAtPosition(text, start, end, TOK_NONLOCAL)) {
return PyTokenTypes.NONLOCAL_KEYWORD;
}
if (myContext.getLanguageLevel().isAtLeast(LanguageLevel.PYTHON35) && isWordAtPosition(text, start, end, TOK_ASYNC)) {
if (myContext.getScope().isAsync() || myBuilder.lookAhead(1) == PyTokenTypes.DEF_KEYWORD) {
return PyTokenTypes.ASYNC_KEYWORD;
if (myContext.getLanguageLevel().isAtLeast(LanguageLevel.PYTHON35)) {
if (isWordAtPosition(text, start, end, TOK_ASYNC)) {
if (myContext.getScope().isAsync() || myBuilder.lookAhead(1) == PyTokenTypes.DEF_KEYWORD) {
return PyTokenTypes.ASYNC_KEYWORD;
}
}
if (isWordAtPosition(text, start, end, TOK_AWAIT)) {
if (myContext.getScope().isAsync()) {
return PyTokenTypes.AWAIT_KEYWORD;
}
}
}
}
@@ -22,10 +22,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.highlighting.PyHighlighter;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyForStatement;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyWithStatement;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
/**
@@ -34,17 +31,22 @@ import org.jetbrains.annotations.NotNull;
public class DumbAwareHighlightingAnnotator extends PyAnnotator implements HighlightRangeExtension {
@Override
public void visitPyFunction(PyFunction node) {
highlightAsyncKeyword(node);
highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD);
}
@Override
public void visitPyForStatement(PyForStatement node) {
highlightAsyncKeyword(node);
highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD);
}
@Override
public void visitPyWithStatement(PyWithStatement node) {
highlightAsyncKeyword(node);
highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD);
}
@Override
public void visitPyPrefixExpression(PyPrefixExpression node) {
highlightKeyword(node, PyTokenTypes.AWAIT_KEYWORD);
}
@Override
@@ -52,8 +54,8 @@ public class DumbAwareHighlightingAnnotator extends PyAnnotator implements Highl
return file instanceof PyFile;
}
private void highlightAsyncKeyword(@NotNull PsiElement node) {
final ASTNode asyncNode = node.getNode().findChildByType(PyTokenTypes.ASYNC_KEYWORD);
private void highlightKeyword(@NotNull PsiElement node, @NotNull PyElementType elementType) {
final ASTNode asyncNode = node.getNode().findChildByType(elementType);
if (asyncNode != null) {
final Annotation annotation = getHolder().createInfoAnnotation(asyncNode, null);
annotation.setTextAttributes(PyHighlighter.PY_KEYWORD);
+5
View File
@@ -0,0 +1,5 @@
<info descr="null">async</info> def <info descr="null">foo</info>():
<info descr="null">await</info> x
await = 0
+5
View File
@@ -0,0 +1,5 @@
async def f(x):
await x
await = 1
+30
View File
@@ -0,0 +1,30 @@
PyFile:Await.py
PyFunction('f')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('f')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('x')
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyPrefixExpression
PsiElement(Py:AWAIT_KEYWORD)('await')
PsiWhiteSpace(' ')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace('\n\n\n')
PyAssignmentStatement
PyTargetExpression: await
PsiElement(Py:IDENTIFIER)('await')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
@@ -233,6 +233,10 @@ public class PythonHighlightingTest extends PyTestCase {
doTest(LanguageLevel.PYTHON35, true, true);
}
public void testAwait() {
doTest(LanguageLevel.PYTHON35, true, true);
}
// ---
private void doTest(final LanguageLevel languageLevel, final boolean checkWarnings, final boolean checkInfos) {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), languageLevel);
@@ -498,6 +498,10 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.PYTHON35);
}
public void testAwait() {
doTest(LanguageLevel.PYTHON35);
}
public void doTest(LanguageLevel languageLevel) {
LanguageLevel prev = myLanguageLevel;
myLanguageLevel = languageLevel;