mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
keyword argument completion refactored a bit
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.jetbrains.python.psi.PyCallExpression;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
|
||||
import java.util.List;
|
||||
@@ -13,5 +14,5 @@ import java.util.List;
|
||||
public interface PyKeywordArgumentProvider {
|
||||
ExtensionPointName<PyKeywordArgumentProvider> EP_NAME = ExtensionPointName.create("Pythonid.keywordArgumentProvider");
|
||||
|
||||
List<String> getKeywordArguments(PyFunction function);
|
||||
List<String> getKeywordArguments(PyFunction function, PyCallExpression callExpr);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ package com.jetbrains.python.packaging.setupPy;
|
||||
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyKeywordArgumentProvider;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -17,7 +14,7 @@ import java.util.List;
|
||||
*/
|
||||
public class SetupKeywordArgumentProvider implements PyKeywordArgumentProvider {
|
||||
@Override
|
||||
public List<String> getKeywordArguments(PyFunction function) {
|
||||
public List<String> getKeywordArguments(PyFunction function, PyCallExpression callExpr) {
|
||||
if ("setup".equals(function.getName())) {
|
||||
final ScopeOwner scopeOwner = PsiTreeUtil.getParentOfType(function, ScopeOwner.class, true);
|
||||
if (scopeOwner instanceof PyFile) {
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package com.jetbrains.python.psi.impl.references;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyKeywordArgumentProvider;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
|
||||
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class KeywordArgumentCompletionUtil {
|
||||
public static void collectFunctionArgNames(PyElement element, List<LookupElement> ret) {
|
||||
PyCallExpression callExpr = PsiTreeUtil.getParentOfType(element, PyCallExpression.class);
|
||||
if (callExpr != null) {
|
||||
PyExpression callee = callExpr.getCallee();
|
||||
if (callee instanceof PyReferenceExpression && element.getParent() == callExpr.getArgumentList()) {
|
||||
final QualifiedResolveResult result = ((PyReferenceExpression)callee).followAssignmentsChain(PyResolveContext.defaultContext());
|
||||
PsiElement def = result.getElement();
|
||||
if (def instanceof PyFunction) {
|
||||
addKeywordArgumentVariants((PyFunction)def, callExpr, ret);
|
||||
}
|
||||
else if (def instanceof PyClass) {
|
||||
PyFunction init = ((PyClass)def).findMethodByName(PyNames.INIT, true); // search in superclasses
|
||||
if (init != null) {
|
||||
addKeywordArgumentVariants(init, callExpr, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void addKeywordArgumentVariants(PyFunction def, PyCallExpression callExpr, final List<LookupElement> ret) {
|
||||
addKeywordArgumentVariants(def, callExpr, ret, new HashSet<PyFunction>());
|
||||
}
|
||||
|
||||
public static void addKeywordArgumentVariants(PyFunction def, PyCallExpression callExpr, List<LookupElement> ret,
|
||||
Collection<PyFunction> visited) {
|
||||
if (visited.contains(def)) {
|
||||
return;
|
||||
}
|
||||
visited.add(def);
|
||||
boolean needSelf = def.getContainingClass() != null && def.getModifier() != PyFunction.Modifier.STATICMETHOD;
|
||||
final KwArgParameterCollector collector = new KwArgParameterCollector(needSelf, ret);
|
||||
def.getParameterList().acceptChildren(collector);
|
||||
if (collector.hasKwArgs()) {
|
||||
for (PyKeywordArgumentProvider provider : Extensions.getExtensions(PyKeywordArgumentProvider.EP_NAME)) {
|
||||
final List<String> arguments = provider.getKeywordArguments(def, callExpr);
|
||||
for (String argument : arguments) {
|
||||
ret.add(PyUtil.createNamedParameterLookup(argument));
|
||||
}
|
||||
}
|
||||
KwArgFromStatementCallCollector fromStatementCallCollector = new KwArgFromStatementCallCollector(ret, collector.getKwArgs());
|
||||
final PyStatementList statementList = def.getStatementList();
|
||||
if (statementList != null)
|
||||
statementList.acceptChildren(fromStatementCallCollector);
|
||||
|
||||
//if (collector.hasOnlySelfAndKwArgs()) {
|
||||
// nothing interesting besides self and **kwargs, let's look at superclass (PY-778)
|
||||
if (fromStatementCallCollector.isKwArgsTransit()) {
|
||||
|
||||
final PsiElement superMethod = PySuperMethodsSearch.search(def).findFirst();
|
||||
if (superMethod instanceof PyFunction) {
|
||||
addKeywordArgumentVariants((PyFunction)superMethod, callExpr, ret, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
public static class KwArgParameterCollector extends PyElementVisitor {
|
||||
private int myCount;
|
||||
private final boolean myNeedSelf;
|
||||
private final List<LookupElement> myRet;
|
||||
private boolean myHasSelf = false;
|
||||
private boolean myHasKwArgs = false;
|
||||
private PyParameter kwArgsParam = null;
|
||||
|
||||
public KwArgParameterCollector(boolean needSelf, List<LookupElement> ret) {
|
||||
myNeedSelf = needSelf;
|
||||
myRet = ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyParameter(PyParameter par) {
|
||||
myCount++;
|
||||
if (myCount == 1 && myNeedSelf) {
|
||||
myHasSelf = true;
|
||||
return;
|
||||
}
|
||||
PyNamedParameter namedParam = par.getAsNamed();
|
||||
if (namedParam != null) {
|
||||
if (!namedParam.isKeywordContainer() && !namedParam.isPositionalContainer()) {
|
||||
final LookupElement item = PyUtil.createNamedParameterLookup(namedParam.getName());
|
||||
myRet.add(item);
|
||||
}
|
||||
else if (namedParam.isKeywordContainer()) {
|
||||
myHasKwArgs = true;
|
||||
kwArgsParam = namedParam;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyTupleParameter nestedTParam = par.getAsTuple();
|
||||
if (nestedTParam != null) {
|
||||
for (PyParameter inner_par : nestedTParam.getContents()) inner_par.accept(this);
|
||||
}
|
||||
// else it's a lone star that can't contribute
|
||||
}
|
||||
}
|
||||
|
||||
public PyParameter getKwArgs() {
|
||||
return kwArgsParam;
|
||||
}
|
||||
|
||||
public boolean hasKwArgs() {
|
||||
return myHasKwArgs;
|
||||
}
|
||||
|
||||
public boolean hasOnlySelfAndKwArgs() {
|
||||
return myCount == 2 && myHasSelf && myHasKwArgs;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KwArgFromStatementCallCollector extends PyElementVisitor {
|
||||
private final List<LookupElement> myRet;
|
||||
private final PyParameter myKwArgs;
|
||||
private boolean kwArgsTransit = true;
|
||||
|
||||
public KwArgFromStatementCallCollector(List<LookupElement> ret, @NotNull PyParameter kwArgs) {
|
||||
myRet = ret;
|
||||
this.myKwArgs = kwArgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyElement(PyElement node) {
|
||||
node.acceptChildren(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPySubscriptionExpression(PySubscriptionExpression node) {
|
||||
String operandName = node.getOperand().getName();
|
||||
processGet(operandName, node.getIndexExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyCallExpression(PyCallExpression node) {
|
||||
if (node.isCalleeText("pop", "get", "getattr")) {
|
||||
PyReferenceExpression child = PsiTreeUtil.getChildOfType(node.getCallee(), PyReferenceExpression.class);
|
||||
if (child != null) {
|
||||
String operandName = child.getName();
|
||||
if (node.getArguments().length > 0) {
|
||||
PyExpression argument = node.getArguments()[0];
|
||||
processGet(operandName, argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.isCalleeText("__init__")) {
|
||||
kwArgsTransit = false;
|
||||
for (PyExpression e : node.getArguments()) {
|
||||
if (e instanceof PyStarArgument) {
|
||||
PyStarArgument kw = (PyStarArgument)e;
|
||||
if (Comparing.equal(myKwArgs.getName(), kw.getFirstChild().getNextSibling().getText())) {
|
||||
kwArgsTransit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitPyCallExpression(node);
|
||||
}
|
||||
|
||||
private void processGet(String operandName, PyExpression argument) {
|
||||
if (Comparing.equal(myKwArgs.getName(), operandName) &&
|
||||
argument instanceof PyStringLiteralExpression) {
|
||||
String name = ((PyStringLiteralExpression)argument).getStringValue();
|
||||
if (PyUtil.isPythonIdentifier(name)) {
|
||||
myRet.add(PyUtil.createNamedParameterLookup(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* is name of kwargs parameter the same as transmitted to __init__ call
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isKwArgsTransit() {
|
||||
return kwArgsTransit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.cython.CythonLanguageDialect;
|
||||
import com.jetbrains.cython.CythonResolveUtil;
|
||||
import com.jetbrains.cython.psi.*;
|
||||
import com.jetbrains.django.util.PythonDataflowUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
|
||||
@@ -533,7 +532,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
|
||||
}
|
||||
|
||||
// in a call, include function's arg names
|
||||
PythonDataflowUtil.collectFunctionArgNames(element, ret);
|
||||
KeywordArgumentCompletionUtil.collectFunctionArgNames(element, ret);
|
||||
|
||||
// include builtin names
|
||||
final PyFile builtinsFile = PyBuiltinCache.getInstance(element).getBuiltinsFile();
|
||||
|
||||
Reference in New Issue
Block a user