mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Parse and highlight the 'async' keyword (PY-16094)
We had to switch to stack-based parsing contexts in order to be able to use context-sensitive token filtering for 'async'. Also this commit fixes the highlighting annotator issue with blinking names of the functions and blinking 'async'.
This commit is contained in:
@@ -32,7 +32,7 @@ public class IpnbPyParser extends PyParser {
|
||||
builder.setTokenTypeRemapper(statementParser);
|
||||
|
||||
while (!builder.eof()) {
|
||||
statementParser.parseStatement(context.emptyParsingScope());
|
||||
statementParser.parseStatement();
|
||||
}
|
||||
rootMarker.done(root);
|
||||
return builder.getTreeBuilt();
|
||||
|
||||
@@ -73,6 +73,7 @@ public class PyTokenTypes {
|
||||
public static final PyElementType FALSE_KEYWORD = new PyElementType("FALSE_KEYWORD");
|
||||
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 INTEGER_LITERAL = new PyElementType("INTEGER_LITERAL");
|
||||
public static final PyElementType FLOAT_LITERAL = new PyElementType("FLOAT_LITERAL");
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<lang.documentationProvider language="Python" implementationClass="com.jetbrains.python.documentation.PythonDocumentationProvider"/>
|
||||
<lang.documentationProvider language="Python" implementationClass="com.jetbrains.python.console.PydevDocumentationProvider"/>
|
||||
<lang.emacs language="Python" implementationClass="com.jetbrains.python.editor.PyEmacsHandler"/>
|
||||
<highlightRangeExtension implementation="com.jetbrains.python.validation.DumbAwareHighlightingAnnotator"/>
|
||||
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyAnnotatingVisitor"/>
|
||||
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyDumbAwareAnnotator"/>
|
||||
<quoteHandler fileType="Python" className="com.jetbrains.python.editor.PythonQuoteHandler"/>
|
||||
@@ -614,6 +615,7 @@
|
||||
<dumbAnnotator implementation="com.jetbrains.python.validation.GeneratorInArgumentListAnnotator"/>
|
||||
<dumbAnnotator implementation="com.jetbrains.python.validation.StarAnnotator"/>
|
||||
<dumbAnnotator implementation="com.jetbrains.python.validation.StringLiteralQuotesAnnotator"/>
|
||||
<dumbAnnotator implementation="com.jetbrains.python.validation.DumbAwareHighlightingAnnotator"/>
|
||||
|
||||
<customTargetExpressionStubType implementation="com.jetbrains.python.psi.impl.stubs.PropertyStubType"/>
|
||||
<dialectsTokenSetContributor implementation="com.jetbrains.python.PythonTokenSetContributor"/>
|
||||
|
||||
@@ -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);
|
||||
NONE_KEYWORD, TRUE_KEYWORD, FALSE_KEYWORD, NONLOCAL_KEYWORD, DEBUG_KEYWORD, ASYNC_KEYWORD);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -56,7 +56,7 @@ public class PyConsoleParser extends PyParser{
|
||||
builder.setTokenTypeRemapper(stmt_parser); // must be done before touching the caching lexer with eof() call.
|
||||
|
||||
while (!builder.eof()) {
|
||||
stmt_parser.parseStatement(context.emptyParsingScope());
|
||||
stmt_parser.parseStatement();
|
||||
}
|
||||
rootMarker.done(root);
|
||||
return builder.getTreeBuilt();
|
||||
|
||||
@@ -72,7 +72,7 @@ public class PyConsoleParsingContext extends ParsingContext {
|
||||
|
||||
|
||||
@Override
|
||||
public void parseStatement(ParsingScope scope) {
|
||||
public void parseStatement() {
|
||||
if (myStartsWithIPythonSymbol) {
|
||||
parseIPythonCommand();
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class PyConsoleParsingContext extends ParsingContext {
|
||||
myBuilder.advanceLexer();
|
||||
}
|
||||
}
|
||||
super.parseStatement(scope);
|
||||
super.parseStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,13 +101,14 @@ public class PyConsoleParsingContext extends ParsingContext {
|
||||
ipythonCommand.done(PyElementTypes.EMPTY_EXPRESSION);
|
||||
}
|
||||
|
||||
protected void checkEndOfStatement(ParsingScope scope) {
|
||||
protected void checkEndOfStatement() {
|
||||
if (myPythonConsoleData.isIPythonEnabled()) {
|
||||
PsiBuilder builder = myContext.getBuilder();
|
||||
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
|
||||
builder.advanceLexer();
|
||||
}
|
||||
else if (builder.getTokenType() == PyTokenTypes.SEMICOLON) {
|
||||
final ParsingScope scope = getParsingContext().getScope();
|
||||
if (!scope.isSuite()) {
|
||||
builder.advanceLexer();
|
||||
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
|
||||
@@ -131,7 +132,7 @@ public class PyConsoleParsingContext extends ParsingContext {
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.checkEndOfStatement(scope);
|
||||
super.checkEndOfStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.lang.WhitespacesBinders;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.jetbrains.python.PyBundle.message;
|
||||
|
||||
@@ -33,23 +34,25 @@ public class FunctionParsing extends Parsing {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public void parseFunctionDeclaration() {
|
||||
public void parseFunctionDeclaration(@NotNull PsiBuilder.Marker endMarker) {
|
||||
assertCurrentToken(PyTokenTypes.DEF_KEYWORD);
|
||||
final PsiBuilder.Marker functionMarker = myBuilder.mark();
|
||||
parseFunctionInnards(functionMarker);
|
||||
parseFunctionInnards(endMarker);
|
||||
}
|
||||
|
||||
protected IElementType getFunctionType() {
|
||||
return FUNCTION_TYPE;
|
||||
}
|
||||
|
||||
protected void parseFunctionInnards(PsiBuilder.Marker functionMarker) {
|
||||
protected void parseFunctionInnards(@NotNull PsiBuilder.Marker functionMarker) {
|
||||
myBuilder.advanceLexer();
|
||||
parseIdentifierOrSkip(PyTokenTypes.LPAR);
|
||||
parseParameterList();
|
||||
parseReturnTypeAnnotation();
|
||||
checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));
|
||||
getStatementParser().parseSuite(functionMarker, getFunctionType(), myContext.emptyParsingScope().withFunction(true));
|
||||
final ParsingContext context = getParsingContext();
|
||||
context.pushScope(context.getScope().withFunction(true));
|
||||
getStatementParser().parseSuite(functionMarker, getFunctionType());
|
||||
context.popScope();
|
||||
}
|
||||
|
||||
public void parseReturnTypeAnnotation() {
|
||||
@@ -63,7 +66,7 @@ public class FunctionParsing extends Parsing {
|
||||
}
|
||||
}
|
||||
|
||||
public void parseDecoratedDeclaration(ParsingScope scope) {
|
||||
public void parseDecoratedDeclaration() {
|
||||
assertCurrentToken(PyTokenTypes.AT); // ??? need this?
|
||||
final PsiBuilder.Marker decoratorStartMarker = myBuilder.mark();
|
||||
final PsiBuilder.Marker decoListMarker = myBuilder.mark();
|
||||
@@ -92,15 +95,15 @@ public class FunctionParsing extends Parsing {
|
||||
}
|
||||
if (decorated) decoListMarker.done(PyElementTypes.DECORATOR_LIST);
|
||||
//else decoListMarker.rollbackTo();
|
||||
parseDeclarationAfterDecorator(decoratorStartMarker, scope);
|
||||
parseDeclarationAfterDecorator(decoratorStartMarker);
|
||||
}
|
||||
|
||||
protected void parseDeclarationAfterDecorator(PsiBuilder.Marker endMarker, ParsingScope scope) {
|
||||
protected void parseDeclarationAfterDecorator(PsiBuilder.Marker endMarker) {
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.DEF_KEYWORD) {
|
||||
parseFunctionInnards(endMarker); // it calls endMarker.done()
|
||||
parseFunctionInnards(endMarker);
|
||||
}
|
||||
else if (myBuilder.getTokenType() == PyTokenTypes.CLASS_KEYWORD) {
|
||||
getStatementParser().parseClassDeclaration(endMarker, scope);
|
||||
getStatementParser().parseClassDeclaration(endMarker);
|
||||
}
|
||||
else {
|
||||
myBuilder.error(message("PARSE.expected.@.or.def"));
|
||||
|
||||
@@ -17,6 +17,10 @@ package com.jetbrains.python.parsing;
|
||||
|
||||
import com.intellij.lang.PsiBuilder;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class ParsingContext {
|
||||
private final StatementParsing stmtParser;
|
||||
@@ -24,6 +28,7 @@ public class ParsingContext {
|
||||
private final FunctionParsing functionParser;
|
||||
private final PsiBuilder myBuilder;
|
||||
private final LanguageLevel myLanguageLevel;
|
||||
private final Deque<ParsingScope> myScopes;
|
||||
|
||||
public ParsingContext(final PsiBuilder builder, LanguageLevel languageLevel, StatementParsing.FUTURE futureFlag) {
|
||||
myBuilder = builder;
|
||||
@@ -31,6 +36,22 @@ public class ParsingContext {
|
||||
stmtParser = new StatementParsing(this, futureFlag);
|
||||
expressionParser = new ExpressionParsing(this);
|
||||
functionParser = new FunctionParsing(this);
|
||||
myScopes = new ArrayDeque<ParsingScope>();
|
||||
myScopes.push(emptyParsingScope());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ParsingScope popScope() {
|
||||
return myScopes.pop();
|
||||
}
|
||||
|
||||
public void pushScope(@NotNull ParsingScope scope) {
|
||||
myScopes.push(scope);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ParsingScope getScope() {
|
||||
return myScopes.peek();
|
||||
}
|
||||
|
||||
public StatementParsing getStatementParser() {
|
||||
|
||||
@@ -23,6 +23,9 @@ public class ParsingScope {
|
||||
private boolean myClass = false;
|
||||
private boolean mySuite = false;
|
||||
private boolean myAfterSemicolon = false;
|
||||
private boolean myAsync = false;
|
||||
|
||||
protected ParsingScope() {}
|
||||
|
||||
public ParsingScope withFunction(boolean flag) {
|
||||
final ParsingScope result = copy();
|
||||
@@ -42,6 +45,12 @@ public class ParsingScope {
|
||||
return result;
|
||||
}
|
||||
|
||||
public ParsingScope withAsync() {
|
||||
final ParsingScope result = copy();
|
||||
result.myAsync = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isFunction() {
|
||||
return myFunction;
|
||||
}
|
||||
@@ -54,6 +63,10 @@ public class ParsingScope {
|
||||
return mySuite;
|
||||
}
|
||||
|
||||
public boolean isAsync() {
|
||||
return myAsync;
|
||||
}
|
||||
|
||||
public boolean isAfterSemicolon() {
|
||||
return myAfterSemicolon;
|
||||
}
|
||||
@@ -71,6 +84,7 @@ public class ParsingScope {
|
||||
result.myFunction = myFunction;
|
||||
result.myClass = myClass;
|
||||
result.mySuite = mySuite;
|
||||
result.myAsync = myAsync;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,15 @@ public class PyParser implements PsiParser {
|
||||
builder.setTokenTypeRemapper(statementParser); // must be done before touching the caching lexer with eof() call.
|
||||
boolean lastAfterSemicolon = false;
|
||||
while (!builder.eof()) {
|
||||
ParsingScope scope = context.emptyParsingScope();
|
||||
context.pushScope(context.emptyParsingScope());
|
||||
if (lastAfterSemicolon) {
|
||||
statementParser.parseSimpleStatement(scope);
|
||||
statementParser.parseSimpleStatement();
|
||||
}
|
||||
else {
|
||||
statementParser.parseStatement(scope);
|
||||
statementParser.parseStatement();
|
||||
}
|
||||
lastAfterSemicolon = scope.isAfterSemicolon();
|
||||
lastAfterSemicolon = context.getScope().isAfterSemicolon();
|
||||
context.popScope();
|
||||
}
|
||||
rootMarker.done(root);
|
||||
ASTNode ast = builder.getTreeBuilt();
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyElementType;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -49,6 +50,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
@NonNls protected static final String TOK_FALSE = "False";
|
||||
@NonNls protected static final String TOK_NONLOCAL = "nonlocal";
|
||||
@NonNls protected static final String TOK_EXEC = "exec";
|
||||
@NonNls protected static final String TOK_ASYNC = "async";
|
||||
|
||||
private static final String EXPRESSION_EXPECTED = "Expression expected";
|
||||
public static final String IDENTIFIER_EXPECTED = "Identifier expected";
|
||||
@@ -86,7 +88,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
myBuilder.setTokenTypeRemapper(this); // clear cached token type
|
||||
}
|
||||
|
||||
public void parseStatement(ParsingScope scope) {
|
||||
public void parseStatement() {
|
||||
|
||||
while (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
|
||||
myBuilder.advanceLexer();
|
||||
@@ -98,103 +100,107 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
if (firstToken == null) return;
|
||||
|
||||
if (firstToken == PyTokenTypes.WHILE_KEYWORD) {
|
||||
parseWhileStatement(scope);
|
||||
parseWhileStatement();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.IF_KEYWORD) {
|
||||
parseIfStatement(PyTokenTypes.IF_KEYWORD, PyTokenTypes.ELIF_KEYWORD, PyTokenTypes.ELSE_KEYWORD, PyElementTypes.IF_STATEMENT, scope);
|
||||
parseIfStatement(PyTokenTypes.IF_KEYWORD, PyTokenTypes.ELIF_KEYWORD, PyTokenTypes.ELSE_KEYWORD, PyElementTypes.IF_STATEMENT);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.FOR_KEYWORD) {
|
||||
parseForStatement(scope);
|
||||
parseForStatement(myBuilder.mark());
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.TRY_KEYWORD) {
|
||||
parseTryStatement(scope);
|
||||
parseTryStatement();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.DEF_KEYWORD) {
|
||||
getFunctionParser().parseFunctionDeclaration();
|
||||
getFunctionParser().parseFunctionDeclaration(myBuilder.mark());
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.AT) {
|
||||
getFunctionParser().parseDecoratedDeclaration(scope);
|
||||
getFunctionParser().parseDecoratedDeclaration();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.CLASS_KEYWORD) {
|
||||
parseClassDeclaration(scope);
|
||||
parseClassDeclaration();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.WITH_KEYWORD) {
|
||||
parseWithStatement(scope);
|
||||
parseWithStatement(myBuilder.mark());
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.ASYNC_KEYWORD) {
|
||||
parseAsyncStatement();
|
||||
return;
|
||||
}
|
||||
|
||||
parseSimpleStatement(scope);
|
||||
parseSimpleStatement();
|
||||
}
|
||||
|
||||
protected void parseSimpleStatement(ParsingScope scope) {
|
||||
protected void parseSimpleStatement() {
|
||||
PsiBuilder builder = myContext.getBuilder();
|
||||
final IElementType firstToken = builder.getTokenType();
|
||||
if (firstToken == null) {
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.PRINT_KEYWORD && hasPrintStatement()) {
|
||||
parsePrintStatement(builder, scope);
|
||||
parsePrintStatement(builder);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.ASSERT_KEYWORD) {
|
||||
parseAssertStatement(scope);
|
||||
parseAssertStatement();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.BREAK_KEYWORD) {
|
||||
parseKeywordStatement(builder, PyElementTypes.BREAK_STATEMENT, scope);
|
||||
parseKeywordStatement(builder, PyElementTypes.BREAK_STATEMENT);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.CONTINUE_KEYWORD) {
|
||||
parseKeywordStatement(builder, PyElementTypes.CONTINUE_STATEMENT, scope);
|
||||
parseKeywordStatement(builder, PyElementTypes.CONTINUE_STATEMENT);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.DEL_KEYWORD) {
|
||||
parseDelStatement(scope);
|
||||
parseDelStatement();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.EXEC_KEYWORD) {
|
||||
parseExecStatement(scope);
|
||||
parseExecStatement();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.GLOBAL_KEYWORD) {
|
||||
parseNameDefiningStatement(scope, PyElementTypes.GLOBAL_STATEMENT);
|
||||
parseNameDefiningStatement(PyElementTypes.GLOBAL_STATEMENT);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.NONLOCAL_KEYWORD) {
|
||||
parseNameDefiningStatement(scope, PyElementTypes.NONLOCAL_STATEMENT);
|
||||
parseNameDefiningStatement(PyElementTypes.NONLOCAL_STATEMENT);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.IMPORT_KEYWORD) {
|
||||
parseImportStatement(scope, PyElementTypes.IMPORT_STATEMENT, PyElementTypes.IMPORT_ELEMENT);
|
||||
parseImportStatement(PyElementTypes.IMPORT_STATEMENT, PyElementTypes.IMPORT_ELEMENT);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.FROM_KEYWORD) {
|
||||
parseFromImportStatement(scope);
|
||||
parseFromImportStatement();
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.PASS_KEYWORD) {
|
||||
parseKeywordStatement(builder, PyElementTypes.PASS_STATEMENT, scope);
|
||||
parseKeywordStatement(builder, PyElementTypes.PASS_STATEMENT);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.RETURN_KEYWORD) {
|
||||
parseReturnStatement(builder, scope);
|
||||
parseReturnStatement(builder);
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.RAISE_KEYWORD) {
|
||||
parseRaiseStatement(scope);
|
||||
parseRaiseStatement();
|
||||
return;
|
||||
}
|
||||
PsiBuilder.Marker exprStatement = builder.mark();
|
||||
if (builder.getTokenType() == PyTokenTypes.YIELD_KEYWORD) {
|
||||
getExpressionParser().parseYieldOrTupleExpression(false);
|
||||
checkEndOfStatement(scope);
|
||||
checkEndOfStatement();
|
||||
exprStatement.done(PyElementTypes.EXPRESSION_STATEMENT);
|
||||
return;
|
||||
}
|
||||
@@ -242,7 +248,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
checkEndOfStatement(scope);
|
||||
checkEndOfStatement();
|
||||
exprStatement.done(statementType);
|
||||
return;
|
||||
}
|
||||
@@ -270,8 +276,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
return myContext.getLanguageLevel().hasPrintStatement() && !myFutureFlags.contains(FUTURE.PRINT_FUNCTION);
|
||||
}
|
||||
|
||||
protected void checkEndOfStatement(ParsingScope scope) {
|
||||
protected void checkEndOfStatement() {
|
||||
PsiBuilder builder = myContext.getBuilder();
|
||||
final ParsingScope scope = getParsingContext().getScope();
|
||||
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
|
||||
builder.advanceLexer();
|
||||
scope.setAfterSemicolon(false);
|
||||
@@ -291,7 +298,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
private void parsePrintStatement(final PsiBuilder builder, ParsingScope scope) {
|
||||
private void parsePrintStatement(final PsiBuilder builder) {
|
||||
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.PRINT_KEYWORD);
|
||||
final PsiBuilder.Marker statement = builder.mark();
|
||||
builder.advanceLexer();
|
||||
@@ -311,29 +318,29 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
getExpressionParser().parseSingleExpression(false);
|
||||
}
|
||||
checkEndOfStatement(scope);
|
||||
checkEndOfStatement();
|
||||
statement.done(PyElementTypes.PRINT_STATEMENT);
|
||||
}
|
||||
|
||||
protected void parseKeywordStatement(PsiBuilder builder, IElementType statementType, ParsingScope scope) {
|
||||
protected void parseKeywordStatement(PsiBuilder builder, IElementType statementType) {
|
||||
final PsiBuilder.Marker statement = builder.mark();
|
||||
builder.advanceLexer();
|
||||
checkEndOfStatement(scope);
|
||||
checkEndOfStatement();
|
||||
statement.done(statementType);
|
||||
}
|
||||
|
||||
private void parseReturnStatement(PsiBuilder builder, ParsingScope inSuite) {
|
||||
private void parseReturnStatement(PsiBuilder builder) {
|
||||
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.RETURN_KEYWORD);
|
||||
final PsiBuilder.Marker returnStatement = builder.mark();
|
||||
builder.advanceLexer();
|
||||
if (builder.getTokenType() != null && !getEndOfStatementsTokens().contains(builder.getTokenType())) {
|
||||
getExpressionParser().parseExpression();
|
||||
}
|
||||
checkEndOfStatement(inSuite);
|
||||
checkEndOfStatement();
|
||||
returnStatement.done(PyElementTypes.RETURN_STATEMENT);
|
||||
}
|
||||
|
||||
private void parseDelStatement(ParsingScope inSuite) {
|
||||
private void parseDelStatement() {
|
||||
assertCurrentToken(PyTokenTypes.DEL_KEYWORD);
|
||||
final PsiBuilder.Marker delStatement = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
@@ -349,11 +356,11 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
checkEndOfStatement(inSuite);
|
||||
checkEndOfStatement();
|
||||
delStatement.done(PyElementTypes.DEL_STATEMENT);
|
||||
}
|
||||
|
||||
private void parseRaiseStatement(ParsingScope inSuite) {
|
||||
private void parseRaiseStatement() {
|
||||
assertCurrentToken(PyTokenTypes.RAISE_KEYWORD);
|
||||
final PsiBuilder.Marker raiseStatement = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
@@ -374,11 +381,11 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
checkEndOfStatement(inSuite);
|
||||
checkEndOfStatement();
|
||||
raiseStatement.done(PyElementTypes.RAISE_STATEMENT);
|
||||
}
|
||||
|
||||
private void parseAssertStatement(ParsingScope scope) {
|
||||
private void parseAssertStatement() {
|
||||
assertCurrentToken(PyTokenTypes.ASSERT_KEYWORD);
|
||||
final PsiBuilder.Marker assertStatement = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
@@ -389,7 +396,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
myContext.getBuilder().error(EXPRESSION_EXPECTED);
|
||||
}
|
||||
}
|
||||
checkEndOfStatement(scope);
|
||||
checkEndOfStatement();
|
||||
}
|
||||
else {
|
||||
myContext.getBuilder().error(EXPRESSION_EXPECTED);
|
||||
@@ -397,12 +404,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
assertStatement.done(PyElementTypes.ASSERT_STATEMENT);
|
||||
}
|
||||
|
||||
protected void parseImportStatement(ParsingScope scope, IElementType statementType, IElementType elementType) {
|
||||
protected void parseImportStatement(IElementType statementType, IElementType elementType) {
|
||||
final PsiBuilder builder = myContext.getBuilder();
|
||||
final PsiBuilder.Marker importStatement = builder.mark();
|
||||
builder.advanceLexer();
|
||||
parseImportElements(elementType, true, false, false);
|
||||
checkEndOfStatement(scope);
|
||||
checkEndOfStatement();
|
||||
importStatement.done(statementType);
|
||||
}
|
||||
|
||||
@@ -411,7 +418,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
from identifier import id, id... -- may be either relative or absolute
|
||||
from . import identifier -- only relative
|
||||
*/
|
||||
private void parseFromImportStatement(ParsingScope inSuite) {
|
||||
private void parseFromImportStatement() {
|
||||
PsiBuilder builder = myContext.getBuilder();
|
||||
assertCurrentToken(PyTokenTypes.FROM_KEYWORD);
|
||||
myFutureImportPhase = Phase.FROM;
|
||||
@@ -447,7 +454,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
statementType = types.statement;
|
||||
parseImportElements(types.element, false, false, from_future);
|
||||
}
|
||||
checkEndOfStatement(inSuite);
|
||||
checkEndOfStatement();
|
||||
fromImportStatement.done(statementType);
|
||||
myFutureImportPhase = Phase.NONE;
|
||||
}
|
||||
@@ -563,7 +570,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void parseNameDefiningStatement(ParsingScope scope, final PyElementType elementType) {
|
||||
private void parseNameDefiningStatement(final PyElementType elementType) {
|
||||
final PsiBuilder.Marker globalStatement = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
|
||||
@@ -571,11 +578,11 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
myBuilder.advanceLexer();
|
||||
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
|
||||
}
|
||||
checkEndOfStatement(scope);
|
||||
checkEndOfStatement();
|
||||
globalStatement.done(elementType);
|
||||
}
|
||||
|
||||
private void parseExecStatement(ParsingScope inSuite) {
|
||||
private void parseExecStatement() {
|
||||
assertCurrentToken(PyTokenTypes.EXEC_KEYWORD);
|
||||
final PsiBuilder.Marker execStatement = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
@@ -588,12 +595,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
getExpressionParser().parseSingleExpression(false);
|
||||
}
|
||||
}
|
||||
checkEndOfStatement(inSuite);
|
||||
checkEndOfStatement();
|
||||
execStatement.done(PyElementTypes.EXEC_STATEMENT);
|
||||
}
|
||||
|
||||
protected void parseIfStatement(PyElementType ifKeyword, PyElementType elifKeyword, PyElementType elseKeyword, PyElementType elementType,
|
||||
ParsingScope scope) {
|
||||
protected void parseIfStatement(PyElementType ifKeyword, PyElementType elifKeyword, PyElementType elseKeyword,
|
||||
PyElementType elementType) {
|
||||
assertCurrentToken(ifKeyword);
|
||||
final PsiBuilder.Marker ifStatement = myBuilder.mark();
|
||||
final PsiBuilder.Marker ifPart = myBuilder.mark();
|
||||
@@ -601,7 +608,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
if (!getExpressionParser().parseSingleExpression(false)) {
|
||||
myBuilder.error("expression expected");
|
||||
}
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
ifPart.done(PyElementTypes.IF_PART_IF);
|
||||
PsiBuilder.Marker elifPart = myBuilder.mark();
|
||||
while (myBuilder.getTokenType() == elifKeyword) {
|
||||
@@ -609,7 +616,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
if (!getExpressionParser().parseSingleExpression(false)) {
|
||||
myBuilder.error("expression expected");
|
||||
}
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
elifPart.done(PyElementTypes.IF_PART_ELIF);
|
||||
elifPart = myBuilder.mark();
|
||||
}
|
||||
@@ -617,7 +624,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == elseKeyword) {
|
||||
myBuilder.advanceLexer();
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else {
|
||||
@@ -647,33 +654,32 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void parseForStatement(ParsingScope scope) {
|
||||
private void parseForStatement(PsiBuilder.Marker endMarker) {
|
||||
assertCurrentToken(PyTokenTypes.FOR_KEYWORD);
|
||||
final PsiBuilder.Marker statement = myBuilder.mark();
|
||||
parseForPart(scope);
|
||||
parseForPart();
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else {
|
||||
elsePart.drop();
|
||||
}
|
||||
statement.done(PyElementTypes.FOR_STATEMENT);
|
||||
endMarker.done(PyElementTypes.FOR_STATEMENT);
|
||||
}
|
||||
|
||||
protected void parseForPart(ParsingScope scope) {
|
||||
protected void parseForPart() {
|
||||
final PsiBuilder.Marker forPart = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
getExpressionParser().parseExpression(true, true);
|
||||
checkMatches(PyTokenTypes.IN_KEYWORD, "'in' expected");
|
||||
getExpressionParser().parseExpression();
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
forPart.done(PyElementTypes.FOR_PART);
|
||||
}
|
||||
|
||||
private void parseWhileStatement(ParsingScope scope) {
|
||||
private void parseWhileStatement() {
|
||||
assertCurrentToken(PyTokenTypes.WHILE_KEYWORD);
|
||||
final PsiBuilder.Marker statement = myBuilder.mark();
|
||||
final PsiBuilder.Marker whilePart = myBuilder.mark();
|
||||
@@ -681,12 +687,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
if (!getExpressionParser().parseSingleExpression(false)) {
|
||||
myBuilder.error(EXPRESSION_EXPECTED);
|
||||
}
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
whilePart.done(PyElementTypes.WHILE_PART);
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else {
|
||||
@@ -695,12 +701,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
statement.done(PyElementTypes.WHILE_STATEMENT);
|
||||
}
|
||||
|
||||
private void parseTryStatement(ParsingScope scope) {
|
||||
private void parseTryStatement() {
|
||||
assertCurrentToken(PyTokenTypes.TRY_KEYWORD);
|
||||
final PsiBuilder.Marker statement = myBuilder.mark();
|
||||
final PsiBuilder.Marker tryPart = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
tryPart.done(PyElementTypes.TRY_PART);
|
||||
boolean haveExceptClause = false;
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.EXCEPT_KEYWORD) {
|
||||
@@ -720,13 +726,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
exceptBlock.done(PyElementTypes.EXCEPT_PART);
|
||||
}
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else {
|
||||
@@ -736,7 +742,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker finallyPart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.FINALLY_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
parseColonAndSuite(scope);
|
||||
parseColonAndSuite();
|
||||
finallyPart.done(PyElementTypes.FINALLY_PART);
|
||||
}
|
||||
else {
|
||||
@@ -750,9 +756,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
statement.done(PyElementTypes.TRY_EXCEPT_STATEMENT);
|
||||
}
|
||||
|
||||
private void parseColonAndSuite(ParsingScope scope) {
|
||||
private void parseColonAndSuite() {
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
parseSuite();
|
||||
}
|
||||
else {
|
||||
final PsiBuilder.Marker mark = myBuilder.mark();
|
||||
@@ -760,9 +766,8 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseWithStatement(ParsingScope scope) {
|
||||
private void parseWithStatement(PsiBuilder.Marker endMarker) {
|
||||
assertCurrentToken(PyTokenTypes.WITH_KEYWORD);
|
||||
final PsiBuilder.Marker statement = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
while (true) {
|
||||
PsiBuilder.Marker withItem = myBuilder.mark();
|
||||
@@ -780,16 +785,16 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
break;
|
||||
}
|
||||
}
|
||||
parseColonAndSuite(scope);
|
||||
statement.done(PyElementTypes.WITH_STATEMENT);
|
||||
parseColonAndSuite();
|
||||
endMarker.done(PyElementTypes.WITH_STATEMENT);
|
||||
}
|
||||
|
||||
private void parseClassDeclaration(ParsingScope scope) {
|
||||
private void parseClassDeclaration() {
|
||||
final PsiBuilder.Marker classMarker = myBuilder.mark();
|
||||
parseClassDeclaration(classMarker, scope);
|
||||
parseClassDeclaration(classMarker);
|
||||
}
|
||||
|
||||
public void parseClassDeclaration(PsiBuilder.Marker classMarker, ParsingScope scope) {
|
||||
public void parseClassDeclaration(PsiBuilder.Marker classMarker) {
|
||||
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
|
||||
myBuilder.advanceLexer();
|
||||
parseIdentifierOrSkip(PyTokenTypes.LPAR, PyTokenTypes.COLON);
|
||||
@@ -800,15 +805,41 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
|
||||
inheritMarker.done(PyElementTypes.ARGUMENT_LIST);
|
||||
}
|
||||
parseColonAndSuite(scope.withClass(true));
|
||||
final ParsingContext context = getParsingContext();
|
||||
context.pushScope(context.getScope().withClass(true));
|
||||
parseColonAndSuite();
|
||||
context.popScope();
|
||||
classMarker.done(PyElementTypes.CLASS_DECLARATION);
|
||||
}
|
||||
|
||||
public void parseSuite(ParsingScope scope) {
|
||||
parseSuite(null, null, scope);
|
||||
private void parseAsyncStatement() {
|
||||
assertCurrentToken(PyTokenTypes.ASYNC_KEYWORD);
|
||||
final PsiBuilder.Marker marker = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
final IElementType token = myBuilder.getTokenType();
|
||||
if (token == PyTokenTypes.DEF_KEYWORD) {
|
||||
final ParsingContext context = getParsingContext();
|
||||
context.pushScope(context.getScope().withAsync());
|
||||
getFunctionParser().parseFunctionDeclaration(marker);
|
||||
context.popScope();
|
||||
}
|
||||
else if (token == PyTokenTypes.WITH_KEYWORD) {
|
||||
parseWithStatement(marker);
|
||||
}
|
||||
else if (token == PyTokenTypes.FOR_KEYWORD) {
|
||||
parseForStatement(marker);
|
||||
}
|
||||
else {
|
||||
marker.drop();
|
||||
myBuilder.error("'def' or 'with' or 'for' expected");
|
||||
}
|
||||
}
|
||||
|
||||
public void parseSuite(@Nullable PsiBuilder.Marker endMarker, @Nullable IElementType elType, ParsingScope scope) {
|
||||
public void parseSuite() {
|
||||
parseSuite(null, null);
|
||||
}
|
||||
|
||||
public void parseSuite(@Nullable PsiBuilder.Marker endMarker, @Nullable IElementType elType) {
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
|
||||
myBuilder.advanceLexer();
|
||||
|
||||
@@ -821,7 +852,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
else {
|
||||
while (!myBuilder.eof() && myBuilder.getTokenType() != PyTokenTypes.DEDENT) {
|
||||
parseStatement(scope);
|
||||
parseStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -837,10 +868,6 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
if (indentFound && !myBuilder.eof()) {
|
||||
checkMatches(PyTokenTypes.DEDENT, "Dedent expected");
|
||||
}
|
||||
// NOTE: the following line advances the PsiBuilder lexer and thus
|
||||
// ensures that the whitespace following the statement list is included
|
||||
// in the block containing the statement list
|
||||
myBuilder.getTokenType();
|
||||
}
|
||||
else {
|
||||
final PsiBuilder.Marker marker = myBuilder.mark();
|
||||
@@ -848,12 +875,17 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
myBuilder.error("Statement expected");
|
||||
}
|
||||
else {
|
||||
parseSimpleStatement(scope.withSuite(true));
|
||||
final ParsingContext context = getParsingContext();
|
||||
context.pushScope(context.getScope().withSuite(true));
|
||||
parseSimpleStatement();
|
||||
context.popScope();
|
||||
while (matchToken(PyTokenTypes.SEMICOLON)) {
|
||||
if (matchToken(PyTokenTypes.STATEMENT_BREAK)) {
|
||||
break;
|
||||
}
|
||||
parseSimpleStatement(scope.withSuite(true));
|
||||
context.pushScope(context.getScope().withSuite(true));
|
||||
parseSimpleStatement();
|
||||
context.popScope();
|
||||
}
|
||||
}
|
||||
marker.done(PyElementTypes.STATEMENT_LIST);
|
||||
@@ -905,6 +937,11 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!myContext.getLanguageLevel().isPy3K() && source == PyTokenTypes.IDENTIFIER) {
|
||||
if (isWordAtPosition(text, start, end, TOK_EXEC)) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.validation;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightRangeExtension;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.annotation.Annotation;
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class DumbAwareHighlightingAnnotator extends PyAnnotator implements HighlightRangeExtension {
|
||||
@Override
|
||||
public void visitPyFunction(PyFunction node) {
|
||||
highlightAsyncKeyword(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyForStatement(PyForStatement node) {
|
||||
highlightAsyncKeyword(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyWithStatement(PyWithStatement node) {
|
||||
highlightAsyncKeyword(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForceHighlightParents(@NotNull PsiFile file) {
|
||||
return file instanceof PyFile;
|
||||
}
|
||||
|
||||
private void highlightAsyncKeyword(@NotNull PsiElement node) {
|
||||
final ASTNode asyncNode = node.getNode().findChildByType(PyTokenTypes.ASYNC_KEYWORD);
|
||||
if (asyncNode != null) {
|
||||
final Annotation annotation = getHolder().createInfoAnnotation(asyncNode, null);
|
||||
annotation.setTextAttributes(PyHighlighter.PY_KEYWORD);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<info descr="null">async</info> def <info descr="null">foo</info>():
|
||||
pass
|
||||
|
||||
async = 1
|
||||
|
||||
<info descr="null">async</info> def <info descr="null">bar</info>():
|
||||
pass
|
||||
|
||||
|
||||
<info descr="null">async</info> def<error descr="'(' expected"><error descr="Identifier expected"> </error></error> # Incomplete<EOLError descr="':' expected"></EOLError>
|
||||
|
||||
|
||||
<error descr="Indent expected">d</error>ef <info descr="null">regular</info>(<info descr="null">xs</info>):
|
||||
|
||||
<info descr="null">async</info> def <info descr="null">quux</info>():
|
||||
async for x in xs:
|
||||
pass
|
||||
|
||||
async with xs:
|
||||
pass
|
||||
|
||||
async for x in xs:
|
||||
pass
|
||||
|
||||
async with xs:
|
||||
pass
|
||||
|
||||
return async
|
||||
@@ -1,3 +1,3 @@
|
||||
def foo():
|
||||
class C:
|
||||
def <info descr="null">foo</info>():
|
||||
class <info descr="null">C</info>:
|
||||
<error descr="'return' outside of function">return 1</error>
|
||||
@@ -1,3 +1,3 @@
|
||||
def f():
|
||||
def <info descr="null">f</info>():
|
||||
yield 42
|
||||
<error descr="Python versions < 3.3 do not allow 'return' with argument inside generator.">return 28</error>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
async def foo(x, y):
|
||||
pass
|
||||
|
||||
async def foo_nested():
|
||||
pass
|
||||
|
||||
|
||||
async = 10
|
||||
|
||||
|
||||
def bar():
|
||||
print(async)
|
||||
|
||||
async def bar_nested():
|
||||
pass
|
||||
@@ -0,0 +1,80 @@
|
||||
PyFile:AsyncDef.py
|
||||
PyFunction('foo')
|
||||
PsiElement(Py:ASYNC_KEYWORD)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('foo')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyNamedParameter('x')
|
||||
PsiElement(Py:IDENTIFIER)('x')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNamedParameter('y')
|
||||
PsiElement(Py:IDENTIFIER)('y')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
PsiWhiteSpace('\n\n ')
|
||||
PyFunction('foo_nested')
|
||||
PsiElement(Py:ASYNC_KEYWORD)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('foo_nested')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
PsiWhiteSpace('\n\n\n')
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: async
|
||||
PsiElement(Py:IDENTIFIER)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('10')
|
||||
PsiWhiteSpace('\n\n\n')
|
||||
PyFunction('bar')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('bar')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyExpressionStatement
|
||||
PyCallExpression: print
|
||||
PyReferenceExpression: print
|
||||
PsiElement(Py:IDENTIFIER)('print')
|
||||
PyArgumentList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyReferenceExpression: async
|
||||
PsiElement(Py:IDENTIFIER)('async')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiWhiteSpace('\n\n ')
|
||||
PyFunction('bar_nested')
|
||||
PsiElement(Py:ASYNC_KEYWORD)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('bar_nested')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
@@ -0,0 +1,7 @@
|
||||
async def f():
|
||||
async for x in xs:
|
||||
pass
|
||||
|
||||
|
||||
async for y in ys:
|
||||
pass
|
||||
@@ -0,0 +1,54 @@
|
||||
PyFile:AsyncFor.py
|
||||
PyFunction('f')
|
||||
PsiElement(Py:ASYNC_KEYWORD)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('f')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyForStatement
|
||||
PsiElement(Py:ASYNC_KEYWORD)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PyForPart
|
||||
PsiElement(Py:FOR_KEYWORD)('for')
|
||||
PsiWhiteSpace(' ')
|
||||
PyTargetExpression: x
|
||||
PsiElement(Py:IDENTIFIER)('x')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IN_KEYWORD)('in')
|
||||
PsiWhiteSpace(' ')
|
||||
PyReferenceExpression: xs
|
||||
PsiElement(Py:IDENTIFIER)('xs')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
PsiWhiteSpace('\n\n\n')
|
||||
PyExpressionStatement
|
||||
PyReferenceExpression: async
|
||||
PsiElement(Py:IDENTIFIER)('async')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PyForStatement
|
||||
PyForPart
|
||||
PsiElement(Py:FOR_KEYWORD)('for')
|
||||
PsiWhiteSpace(' ')
|
||||
PyTargetExpression: y
|
||||
PsiElement(Py:IDENTIFIER)('y')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IN_KEYWORD)('in')
|
||||
PsiWhiteSpace(' ')
|
||||
PyReferenceExpression: ys
|
||||
PsiElement(Py:IDENTIFIER)('ys')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
@@ -0,0 +1,6 @@
|
||||
async def foo():
|
||||
async with x:
|
||||
pass
|
||||
|
||||
async with y:
|
||||
pass
|
||||
@@ -0,0 +1,44 @@
|
||||
PyFile:AsyncWith.py
|
||||
PyFunction('foo')
|
||||
PsiElement(Py:ASYNC_KEYWORD)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('foo')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyWithStatement
|
||||
PsiElement(Py:ASYNC_KEYWORD)('async')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:WITH_KEYWORD)('with')
|
||||
PsiWhiteSpace(' ')
|
||||
PyWithItem
|
||||
PyReferenceExpression: x
|
||||
PsiElement(Py:IDENTIFIER)('x')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
PsiWhiteSpace('\n\n')
|
||||
PyExpressionStatement
|
||||
PyReferenceExpression: async
|
||||
PsiElement(Py:IDENTIFIER)('async')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PyWithStatement
|
||||
PsiElement(Py:WITH_KEYWORD)('with')
|
||||
PsiWhiteSpace(' ')
|
||||
PyWithItem
|
||||
PyReferenceExpression: y
|
||||
PsiElement(Py:IDENTIFIER)('y')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
@@ -229,6 +229,10 @@ public class PythonHighlightingTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAsync() {
|
||||
doTest(LanguageLevel.PYTHON35, true, true);
|
||||
}
|
||||
|
||||
// ---
|
||||
private void doTest(final LanguageLevel languageLevel, final boolean checkWarnings, final boolean checkInfos) {
|
||||
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), languageLevel);
|
||||
|
||||
@@ -486,6 +486,18 @@ public class PythonParsingTest extends ParsingTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAsyncDef() {
|
||||
doTest(LanguageLevel.PYTHON35);
|
||||
}
|
||||
|
||||
public void testAsyncWith() {
|
||||
doTest(LanguageLevel.PYTHON35);
|
||||
}
|
||||
|
||||
public void testAsyncFor() {
|
||||
doTest(LanguageLevel.PYTHON35);
|
||||
}
|
||||
|
||||
public void doTest(LanguageLevel languageLevel) {
|
||||
LanguageLevel prev = myLanguageLevel;
|
||||
myLanguageLevel = languageLevel;
|
||||
|
||||
Reference in New Issue
Block a user