Code completion for 'async' and 'await' (PY-16094)

This commit is contained in:
Andrey Vlasovskikh
2015-09-08 14:22:17 +03:00
parent c855959143
commit c8a8585c36
13 changed files with 105 additions and 9 deletions
@@ -450,6 +450,8 @@ public class PyNames {
public static final String IN = "in";
public static final String NOT = "not";
public static final String LAMBDA = "lambda";
public static final String ASYNC = "async";
public static final String AWAIT = "await";
/**
* Contains keywords as of CPython 2.5.
@@ -79,6 +79,8 @@ extends
@Nullable
Modifier getModifier();
boolean isAsync();
/**
* Flags that mark common alterations of a function: decoration by and wrapping in classmethod() and staticmethod().
*/
@@ -21,4 +21,5 @@ import com.jetbrains.python.psi.PyFunction;
public interface PyFunctionStub extends NamedStub<PyFunction> {
String getDocString();
String getDeprecationMessage();
boolean isAsync();
}
@@ -34,6 +34,8 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.codeInsight.PyUnindentingInsertHandler;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.documentation.doctest.PyDocstringFile;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NonNls;
@@ -182,13 +184,19 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
}
}
private static class Py3kFilter implements ElementFilter {
private static class LanguageLevelAtLeastFilter implements ElementFilter {
@NotNull private final LanguageLevel myLevel;
public LanguageLevelAtLeastFilter(@NotNull LanguageLevel level) {
myLevel = level;
}
public boolean isAcceptable(Object element, PsiElement context) {
if (!(element instanceof PsiElement)) {
return false;
}
final PsiFile containingFile = ((PsiElement)element).getContainingFile();
return containingFile instanceof PyFile && ((PyFile)containingFile).getLanguageLevel().isPy3K();
return containingFile instanceof PyFile && ((PyFile)containingFile).getLanguageLevel().isAtLeast(myLevel);
}
public boolean isClassAcceptable(Class hintClass) {
@@ -196,6 +204,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
}
}
private static class NotParameterOrDefaultValue implements ElementFilter {
@Override
@@ -359,7 +368,8 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
));
*/
private static final FilterPattern PY3K = new FilterPattern(new Py3kFilter());
private static final FilterPattern PY3K = new FilterPattern(new PyKeywordCompletionContributor.LanguageLevelAtLeastFilter(LanguageLevel.PYTHON30));
private static final FilterPattern PY35 = new FilterPattern(new LanguageLevelAtLeastFilter(LanguageLevel.PYTHON35));
// ======
@@ -477,6 +487,28 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
,
new PyKeywordCompletionProvider(PyNames.NONLOCAL)
);
extend(CompletionType.BASIC,
psiElement()
.withLanguage(PythonLanguage.getInstance())
.and(PY35)
.andNot(AFTER_QUALIFIER)
.with(new PatternCondition<PsiElement>("insideAsyncDef") {
@Override
public boolean accepts(@NotNull PsiElement element, ProcessingContext context) {
final ScopeOwner owner = ScopeUtil.getScopeOwner(element);
return owner instanceof PyFunction && ((PyFunction)owner).isAsync();
}
})
.andOr(IN_BEGIN_STMT,
psiElement()
.inside(false, psiElement(PyAssignmentStatement.class), psiElement(PyTargetExpression.class))
.afterLeaf(psiElement().withElementType(PyTokenTypes.EQ)),
psiElement()
.inside(false, psiElement(PyAugAssignmentStatement.class), psiElement(PyTargetExpression.class))
.afterLeaf(psiElement().withElementType(PyTokenTypes.AUG_ASSIGN_OPERATIONS)),
psiElement().inside(true, psiElement(PyParenthesizedExpression.class))),
new PyKeywordCompletionProvider(PyNames.AWAIT));
}
private void addWithinIf() {
@@ -578,8 +610,23 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
.andNot(AFTER_QUALIFIER)
.andNot(IN_FUNCTION_HEADER)
,
new PyKeywordCompletionProvider(TailType.NONE, PyNames.TRUE, PyNames.FALSE, PyNames.NONE)
);
new PyKeywordCompletionProvider(TailType.NONE, PyNames.TRUE, PyNames.FALSE, PyNames.NONE));
extend(CompletionType.BASIC,
psiElement()
.withLanguage(PythonLanguage.getInstance())
.and(PY35)
.andNot(IN_COMMENT)
.andNot(IN_IMPORT_STMT)
.andNot(IN_PARAM_LIST)
.andNot(AFTER_QUALIFIER)
.andNot(IN_STRING_LITERAL),
new PyKeywordCompletionProvider(PyNames.ASYNC));
extend(CompletionType.BASIC,
psiElement()
.withLanguage(PythonLanguage.getInstance())
.and(PY35)
.afterLeaf(psiElement().withElementType(PyTokenTypes.IDENTIFIER).withText(PyNames.ASYNC)),
new PyKeywordCompletionProvider(PyNames.DEF, PyNames.WITH, PyNames.FOR));
}
private void addAs() {
@@ -63,7 +63,7 @@ public class PyFileElementType extends IStubFileElementType<PyFileStub> {
@Override
public int getStubVersion() {
// Don't forget to update versions of indexes that use the updated stub-based elements
return 50;
return 51;
}
@Nullable
@@ -638,6 +638,15 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
return null;
}
@Override
public boolean isAsync() {
final PyFunctionStub stub = getStub();
if (stub != null) {
return stub.isAsync();
}
return getNode().findChildByType(PyTokenTypes.ASYNC_KEYWORD) != null;
}
@Nullable
private Modifier getWrappersFromStub() {
final StubElement parentStub = getStub().getParentStub();
@@ -56,7 +56,8 @@ public class PyFunctionElementType extends PyStubElementType<PyFunctionStub, PyF
String message = function.extractDeprecationMessage();
final PyStringLiteralExpression docStringExpression = function.getDocStringExpression();
return new PyFunctionStubImpl(psi.getName(), PyPsiUtils.strValue(docStringExpression),
message == null ? null : StringRef.fromString(message), parentStub, getStubElementType());
message == null ? null : StringRef.fromString(message), function.isAsync(), parentStub,
getStubElementType());
}
public void serialize(@NotNull final PyFunctionStub stub, @NotNull final StubOutputStream dataStream)
@@ -64,6 +65,7 @@ public class PyFunctionElementType extends PyStubElementType<PyFunctionStub, PyF
dataStream.writeName(stub.getName());
dataStream.writeUTFFast(stub.getDocString() != null ? stub.getDocString() : "");
dataStream.writeName(stub.getDeprecationMessage());
dataStream.writeBoolean(stub.isAsync());
}
@NotNull
@@ -71,7 +73,9 @@ public class PyFunctionElementType extends PyStubElementType<PyFunctionStub, PyF
String name = StringRef.toString(dataStream.readName());
String docString = dataStream.readUTFFast();
StringRef deprecationMessage = dataStream.readName();
return new PyFunctionStubImpl(name, docString.length() > 0 ? docString : null, deprecationMessage, parentStub, getStubElementType());
final boolean isAsync = dataStream.readBoolean();
return new PyFunctionStubImpl(name, docString.length() > 0 ? docString : null, deprecationMessage, isAsync, parentStub,
getStubElementType());
}
public void indexStub(@NotNull final PyFunctionStub stub, @NotNull final IndexSink sink) {
@@ -27,13 +27,15 @@ public class PyFunctionStubImpl extends StubBase<PyFunction> implements PyFuncti
private final String myName;
private final String myDocString;
private final StringRef myDeprecationMessage;
private final boolean myAsync;
public PyFunctionStubImpl(final String name, final String docString, @Nullable final StringRef deprecationMessage,
public PyFunctionStubImpl(final String name, final String docString, @Nullable final StringRef deprecationMessage, boolean isAsync,
final StubElement parent, IStubElementType stubElementType) {
super(parent, stubElementType);
myName = name;
myDocString = docString;
myDeprecationMessage = deprecationMessage;
myAsync = isAsync;
}
public String getName() {
@@ -49,6 +51,11 @@ public class PyFunctionStubImpl extends StubBase<PyFunction> implements PyFuncti
return myDeprecationMessage == null ? null : myDeprecationMessage.getString();
}
@Override
public boolean isAsync() {
return myAsync;
}
@Override
public String toString() {
return "PyFunctionStub(" + myName + ")";
@@ -0,0 +1 @@
async # Comment
+1
View File
@@ -0,0 +1 @@
asy<caret> # Comment
@@ -0,0 +1,2 @@
async def foo():
await # comment
+2
View File
@@ -0,0 +1,2 @@
async def foo():
awa<caret> # comment
@@ -157,6 +157,24 @@ public class Py3CompletionTest extends PyTestCase {
doTest();
}
public void testAsync() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest();
}
});
}
public void testAwait() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest();
}
});
}
@Override
protected String getTestDataPath() {
return super.getTestDataPath() + "/completion";