mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Parse and highlight the 'await' keyword (PY-16094)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<info descr="null">async</info> def <info descr="null">foo</info>():
|
||||
<info descr="null">await</info> x
|
||||
|
||||
|
||||
await = 0
|
||||
@@ -0,0 +1,5 @@
|
||||
async def f(x):
|
||||
await x
|
||||
|
||||
|
||||
await = 1
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user