mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
when completing method calls, auto-insert parentheses and show parameter info (PY-437)
This commit is contained in:
@@ -27,7 +27,7 @@ public class PyJavaClassType implements PyType {
|
||||
}
|
||||
|
||||
public Object[] getCompletionVariants(final PyReferenceExpression referenceExpression, ProcessingContext context) {
|
||||
final VariantsProcessor processor = new VariantsProcessor();
|
||||
final VariantsProcessor processor = new VariantsProcessor(referenceExpression);
|
||||
myClass.processDeclarations(processor, ResolveState.initial(), null, referenceExpression);
|
||||
return processor.getResult();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.jetbrains.python.codeInsight;
|
||||
|
||||
import com.intellij.codeInsight.AutoPopupController;
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyReferenceExpression;
|
||||
import com.jetbrains.python.psi.impl.PyCallExpressionHelper;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyFunctionInsertHandler extends ParenthesesInsertHandler<LookupElement> {
|
||||
public static PyFunctionInsertHandler INSTANCE = new PyFunctionInsertHandler();
|
||||
|
||||
@Override
|
||||
public void handleInsert(InsertionContext context, LookupElement item) {
|
||||
super.handleInsert(context, item);
|
||||
if (hasParams(context, item)) {
|
||||
AutoPopupController.getInstance(context.getProject()).autoPopupParameterInfo(context.getEditor(), (PyFunction) item.getObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) {
|
||||
return hasParams(context, item);
|
||||
}
|
||||
|
||||
private static boolean hasParams(InsertionContext context, LookupElement item) {
|
||||
PyFunction function = (PyFunction) item.getObject();
|
||||
final PsiElement element = context.getFile().findElementAt(context.getStartOffset());
|
||||
PyReferenceExpression refExpr = PsiTreeUtil.getParentOfType(element, PyReferenceExpression.class);
|
||||
int implicitArgsCount = refExpr != null
|
||||
? PyCallExpressionHelper.getImplicitArgumentCount(refExpr, function)
|
||||
: 0;
|
||||
return function.getParameterList().getParameters().length > implicitArgsCount;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,11 @@ package com.jetbrains.python.psi.impl;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.PyDecorator;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -97,55 +95,71 @@ public class PyCallExpressionHelper {
|
||||
}
|
||||
if (resolved instanceof PyFunction) {
|
||||
EnumSet<PyFunction.Flag> flags = EnumSet.noneOf(PyFunction.Flag.class);
|
||||
int implicit_offset = 0;
|
||||
boolean is_by_instance = isByInstance(us);
|
||||
if (is_by_instance) implicit_offset += 1;
|
||||
// wrapped flags?
|
||||
if (wrapped_flag != null) {
|
||||
flags.add(wrapped_flag);
|
||||
flags.add(PyFunction.Flag.WRAPPED);
|
||||
if (wrapped_flag == PyFunction.Flag.STATICMETHOD && implicit_offset > 0) implicit_offset -= 1; // might have marked it as implicit 'self'
|
||||
if (wrapped_flag == PyFunction.Flag.CLASSMETHOD && ! is_by_instance) implicit_offset += 1; // Both Foo.method() and foo.method() have implicit the first arg
|
||||
}
|
||||
// decorators?
|
||||
PyFunction method = (PyFunction)resolved; // constructor call?
|
||||
if (PyNames.INIT.equals(method.getName())) {
|
||||
String refName = us.getCallee() instanceof PyReferenceExpression
|
||||
? ((PyReferenceExpression) us.getCallee()).getReferencedName()
|
||||
: null;
|
||||
if (!PyNames.INIT.equals(refName)) { // PY-312
|
||||
implicit_offset += 1;
|
||||
}
|
||||
}
|
||||
// look for closest decorator
|
||||
PyDecoratorList decolist = method.getDecoratorList();
|
||||
if (decolist != null) {
|
||||
PyDecorator[] decos = decolist.getDecorators();
|
||||
// TODO: look for all decorators
|
||||
if (decos.length == 1) {
|
||||
PyDecorator deco = decos[0];
|
||||
String deconame = deco.getName();
|
||||
if (deco.isBuiltin()) {
|
||||
if (PyNames.STATICMETHOD.equals(deconame)) {
|
||||
flags.add(PyFunction.Flag.STATICMETHOD);
|
||||
if (implicit_offset > 0) implicit_offset -= 1; // might have marked it as implicit 'self'
|
||||
}
|
||||
else if (PyNames.CLASSMETHOD.equals(deconame)) {
|
||||
flags.add(PyFunction.Flag.CLASSMETHOD);
|
||||
if (! is_by_instance) implicit_offset += 1; // Both Foo.method() and foo.method() have implicit the first arg
|
||||
}
|
||||
// else could be custom decorator processing
|
||||
}
|
||||
}
|
||||
}
|
||||
int implicit_offset = getImplicitArgumentCount(us.getCallee(), (PyFunction) resolved, wrapped_flag, flags);
|
||||
return new PyCallExpression.PyMarkedFunction((PyFunction)resolved, flags, implicit_offset);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static boolean isByInstance(PyCallExpression us) {
|
||||
PyExpression callee = us.getCallee();
|
||||
public static int getImplicitArgumentCount(final PyExpression callReference, PyFunction functionBeingCalled) {
|
||||
return getImplicitArgumentCount(callReference, functionBeingCalled, null, null);
|
||||
}
|
||||
|
||||
private static int getImplicitArgumentCount(final PyExpression callReference,
|
||||
PyFunction method,
|
||||
@Nullable PyFunction.Flag wrapped_flag,
|
||||
@Nullable EnumSet<PyFunction.Flag> flags) {
|
||||
int implicit_offset = 0;
|
||||
boolean is_by_instance = isByInstance(callReference);
|
||||
if (is_by_instance) implicit_offset += 1;
|
||||
// wrapped flags?
|
||||
if (wrapped_flag != null) {
|
||||
if (flags != null) {
|
||||
flags.add(wrapped_flag);
|
||||
flags.add(PyFunction.Flag.WRAPPED);
|
||||
}
|
||||
if (wrapped_flag == PyFunction.Flag.STATICMETHOD && implicit_offset > 0) implicit_offset -= 1; // might have marked it as implicit 'self'
|
||||
if (wrapped_flag == PyFunction.Flag.CLASSMETHOD && ! is_by_instance) implicit_offset += 1; // Both Foo.method() and foo.method() have implicit the first arg
|
||||
}
|
||||
// decorators?
|
||||
if (PyNames.INIT.equals(method.getName())) {
|
||||
String refName = callReference instanceof PyReferenceExpression
|
||||
? ((PyReferenceExpression)callReference).getReferencedName()
|
||||
: null;
|
||||
if (!PyNames.INIT.equals(refName)) { // PY-312
|
||||
implicit_offset += 1;
|
||||
}
|
||||
}
|
||||
// look for closest decorator
|
||||
PyDecoratorList decolist = method.getDecoratorList();
|
||||
if (decolist != null) {
|
||||
PyDecorator[] decos = decolist.getDecorators();
|
||||
// TODO: look for all decorators
|
||||
if (decos.length == 1) {
|
||||
PyDecorator deco = decos[0];
|
||||
String deconame = deco.getName();
|
||||
if (deco.isBuiltin()) {
|
||||
if (PyNames.STATICMETHOD.equals(deconame)) {
|
||||
if (flags != null) {
|
||||
flags.add(PyFunction.Flag.STATICMETHOD);
|
||||
}
|
||||
if (implicit_offset > 0) implicit_offset -= 1; // might have marked it as implicit 'self'
|
||||
}
|
||||
else if (PyNames.CLASSMETHOD.equals(deconame)) {
|
||||
if (flags != null) {
|
||||
flags.add(PyFunction.Flag.CLASSMETHOD);
|
||||
}
|
||||
if (! is_by_instance) implicit_offset += 1; // Both Foo.method() and foo.method() have implicit the first arg
|
||||
}
|
||||
// else could be custom decorator processing
|
||||
}
|
||||
}
|
||||
}
|
||||
return implicit_offset;
|
||||
}
|
||||
|
||||
protected static boolean isByInstance(final PyExpression callee) {
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
PyExpression qualifier = ((PyReferenceExpression)callee).getQualifier();
|
||||
if (qualifier != null) {
|
||||
|
||||
@@ -276,7 +276,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
|
||||
final PsiElement realContext = PyPsiUtils.getRealContext(myElement);
|
||||
|
||||
// include our own names
|
||||
final VariantsProcessor processor = new VariantsProcessor();
|
||||
final VariantsProcessor processor = new VariantsProcessor(myElement);
|
||||
PyResolveUtil.treeCrawlUp(processor, realContext); // names from here
|
||||
PyResolveUtil.scanOuterContext(processor, realContext); // possible names from around us at call time
|
||||
|
||||
|
||||
@@ -685,7 +685,7 @@ public class ResolveImportUtil {
|
||||
if (mod_candidate instanceof PyExpression) {
|
||||
addImportedNames(from_import.getImportElements(), names_already); // don't propose already imported items
|
||||
// collect what's within module file
|
||||
final VariantsProcessor processor = new VariantsProcessor(new PyResolveUtil.FilterNameNotIn(names_already));
|
||||
final VariantsProcessor processor = new VariantsProcessor(partial_ref, new PyResolveUtil.FilterNameNotIn(names_already));
|
||||
PyResolveUtil.treeCrawlUp(processor, true, mod_candidate);
|
||||
variants.addAll(processor.getResultList());
|
||||
// try to collect submodules
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.ResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.util.Icons;
|
||||
import com.jetbrains.python.codeInsight.PyFunctionInsertHandler;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -17,14 +18,17 @@ import java.util.*;
|
||||
public class VariantsProcessor implements PsiScopeProcessor {
|
||||
private final Map<String, LookupElement> myVariants = new HashMap<String, LookupElement>();
|
||||
|
||||
protected final PsiElement myContext;
|
||||
protected String myNotice;
|
||||
protected PyResolveUtil.Filter myFilter;
|
||||
|
||||
public VariantsProcessor() {
|
||||
public VariantsProcessor(PsiElement context) {
|
||||
// empty
|
||||
myContext = context;
|
||||
}
|
||||
|
||||
public VariantsProcessor(final PyResolveUtil.Filter filter) {
|
||||
public VariantsProcessor(PsiElement context, final PyResolveUtil.Filter filter) {
|
||||
myContext = context;
|
||||
myFilter = filter;
|
||||
}
|
||||
|
||||
@@ -33,6 +37,9 @@ public class VariantsProcessor implements PsiScopeProcessor {
|
||||
}
|
||||
|
||||
protected LookupElementBuilder setupItem(LookupElementBuilder item) {
|
||||
if (item.getObject() instanceof PyFunction) {
|
||||
item = item.setInsertHandler(PyFunctionInsertHandler.INSTANCE);
|
||||
}
|
||||
if (myNotice != null) {
|
||||
return setItemNotice(item, myNotice);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class PyClassType implements PyType {
|
||||
|
||||
public Object[] getCompletionVariants(final PyReferenceExpression referenceExpression, ProcessingContext context) {
|
||||
Set<String> names_already = context.get(PyType.CTX_NAMES);
|
||||
final VariantsProcessor processor = new VariantsProcessor(new PyResolveUtil.FilterNotInstance(myClass));
|
||||
final VariantsProcessor processor = new VariantsProcessor(referenceExpression, new PyResolveUtil.FilterNotInstance(myClass));
|
||||
myClass.processDeclarations(processor, ResolveState.initial(), null, referenceExpression);
|
||||
List<Object> ret = new ArrayList<Object>();
|
||||
for(PyClassMembersProvider provider: Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
|
||||
|
||||
@@ -3,19 +3,17 @@ package com.jetbrains.python.psi.types;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFromImportStatement;
|
||||
import com.jetbrains.python.psi.PyImportElement;
|
||||
import com.jetbrains.python.psi.PyReferenceExpression;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
import static com.jetbrains.python.psi.resolve.ResolveImportUtil.ROLE_IN_IMPORT.*;
|
||||
import com.jetbrains.python.psi.resolve.VariantsProcessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.python.psi.resolve.ResolveImportUtil.ROLE_IN_IMPORT.NONE;
|
||||
// .impl looks impure
|
||||
|
||||
/**
|
||||
@@ -79,7 +77,7 @@ public class PyModuleType implements PyType { // Maybe make it a PyClassType ref
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
ResolveImportUtil.ROLE_IN_IMPORT role = ResolveImportUtil.getRoleInImport(referenceExpression.getReference());
|
||||
if (role == NONE) { // when not inside import, add regular attributes
|
||||
final VariantsProcessor processor = new VariantsProcessor();
|
||||
final VariantsProcessor processor = new VariantsProcessor(referenceExpression);
|
||||
myModule.processDeclarations(processor, ResolveState.initial(), null, referenceExpression);
|
||||
if (names_already != null) {
|
||||
for (LookupElement le : processor.getResultList()) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
class A:
|
||||
def method(self): pass
|
||||
def test(self): self.method
|
||||
def test(self): self.method()
|
||||
Reference in New Issue
Block a user