PY-14216 Code completion for Model.objects does not work with Django 1.7.x

PY-14714 Unresolved attribute reference 'objects' for class 'Foo'
This commit is contained in:
Ilya.Kazakevich
2014-12-30 21:00:29 +03:00
parent b2a4d34cd7
commit 3220e2bacd
12 changed files with 371 additions and 243 deletions
@@ -45,6 +45,11 @@ public class PyCustomMember {
boolean myFunction = false;
/**
* Force resolving to {@link MyInstanceElement} even if element is function
*/
private boolean myAlwaysResolveToCustomElement;
public PyCustomMember(@NotNull final String name, @Nullable final String type, final boolean resolveToInstance) {
myName = name;
myResolveToInstance = resolveToInstance;
@@ -82,6 +87,7 @@ public class PyCustomMember {
myTypeName = typeName;
myTypeCallback = null;
}
public PyCustomMember(@NotNull final String name, @Nullable final PsiElement target) {
this(name, target, null);
}
@@ -96,6 +102,15 @@ public class PyCustomMember {
return this;
}
/**
* Force resolving to {@link MyInstanceElement} even if element is function
*/
@NotNull
public final PyCustomMember alwaysResolveToCustomElement() {
myAlwaysResolveToCustomElement = true;
return this;
}
public PyCustomMember toClass(String name) {
myPsiPath = new PyPsiPath.ToClass(myPsiPath, name);
return this;
@@ -156,7 +171,7 @@ public class PyCustomMember {
PyClass targetClass =
myTypeName != null && myTypeName.indexOf('.') > 0 ? PyPsiFacade.getInstance(context.getProject()).findClass(myTypeName) : null;
final PsiElement resolveTarget = findResolveTarget(context);
if (resolveTarget instanceof PyFunction) {
if (resolveTarget instanceof PyFunction && !myAlwaysResolveToCustomElement) {
return resolveTarget;
}
if (resolveTarget != null || targetClass != null) {
@@ -211,4 +226,4 @@ public class PyCustomMember {
return null;
}
}
}
}
@@ -30,7 +30,7 @@ public interface PyClassMembersProvider {
ExtensionPointName<PyClassMembersProvider> EP_NAME = ExtensionPointName.create("Pythonid.pyClassMembersProvider");
@NotNull
Collection<PyCustomMember> getMembers(PyClassType clazz, @Nullable PsiElement location);
Collection<PyCustomMember> getMembers(final PyClassType clazz, PsiElement location);
@Nullable
PsiElement resolveMember(PyClassType clazz, String name, @Nullable PsiElement location);
@@ -102,6 +102,7 @@ public class PyTypeProviderBase implements PyTypeProvider {
return null;
}
@Nullable
@Override
public PyType getCallType(@NotNull PyFunction function, @Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
ReturnTypeDescriptor descriptor;
@@ -191,7 +191,7 @@ public class TypeEvalContext {
}
}
final PyType type = element.getType(this, Key.INSTANCE);
assertValid(type, element);
assertValid(type, element);
synchronized (myEvaluated) {
myEvaluated.put(element, type);
}
@@ -850,5 +850,4 @@ remote.interpreter.configure.path.label=Python interpreter path:
remote.interpreter.configure.temp.files.path.label=PyCharm helpers path:
# Message we display for inspection if user uses custom class type members that do not exist
custom.type.name=Dynamic class
custom.type.mimic.name=Dynamic class based on {0}
@@ -1,204 +0,0 @@
/*
* Copyright 2000-2014 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;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.codeInsight.PyCustomMember;
import com.jetbrains.python.codeInsight.PyCustomMemberUtils;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyCallSiteExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Custom (aka dynamic) type that has some members you pass to it. To be used for cases like "type()"
* This class can also mimic any other class (optionally). When mimics, it has all methods from this class and its own.
* @author Ilya.Kazakevich
*/
public class PyCustomMembersType implements PyClassLikeType {
@NotNull
private final Map<String, PyCustomMember> myMembers;
@Nullable
private final PyClassType myTypeToMimic;
/**
* @param typeToMimic this type may mimic some other class-based type. Pass it to have all members from this class + custom.
* Check class manual for more info.
* @param members custom members
*/
public PyCustomMembersType(@Nullable final PyClassType typeToMimic, @NotNull final PyCustomMember... members) {
myTypeToMimic = typeToMimic;
myMembers = new HashMap<String, PyCustomMember>(members.length);
for (final PyCustomMember member : members) {
myMembers.put(member.getName(), member);
}
}
/**
* @return class we mimic (if any). Check class manual for more info.
*/
@Nullable
public PyClassType getTypeToMimic() {
return myTypeToMimic;
}
@Override
public boolean isDefinition() {
return false;
}
@Override
public PyClassLikeType toInstance() {
return this;
}
@Nullable
@Override
public String getClassQName() {
return null;
}
@NotNull
@Override
public List<PyClassLikeType> getSuperClassTypes(@NotNull TypeEvalContext context) {
return Collections.emptyList();
}
@Nullable
@Override
public List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
@Nullable final PyExpression location,
@NotNull final AccessDirection direction,
@NotNull final PyResolveContext resolveContext,
final boolean inherited) {
if (myMembers.containsKey(name)) {
PsiElement context = null;
if (location != null) {
context = location;
}
if (context == null) {
context = resolveContext.getTypeEvalContext().getOrigin();
}
if (context != null) {
final PsiElement resolveResult = myMembers.get(name).resolve(context);
if (resolveResult != null) {
return Collections.singletonList(new RatedResolveResult(0, resolveResult));
}
}
}
if (myTypeToMimic != null) {
return myTypeToMimic.resolveMember(name, location, direction, resolveContext, inherited);
}
return null;
}
@Override
public boolean isValid() {
return true;
}
@Nullable
@Override
public PyClassLikeType getMetaClassType(@NotNull TypeEvalContext context, boolean inherited) {
return null;
}
@Override
public boolean isCallable() {
return true;
}
@Nullable
@Override
public PyType getReturnType(@NotNull TypeEvalContext context) {
return null;
}
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
return this;
}
@Nullable
@Override
public List<PyCallableParameter> getParameters(@NotNull TypeEvalContext context) {
return null;
}
@Nullable
@Override
public List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
@Nullable final PyExpression location,
@NotNull final AccessDirection direction,
@NotNull final PyResolveContext resolveContext) {
return resolveMember(name, location, direction, resolveContext, true);
}
@Override
public Object[] getCompletionVariants(final String completionPrefix, final PsiElement location, final ProcessingContext context) {
final Collection<LookupElement> lookupElements = new ArrayList<LookupElement>(myMembers.size());
for (final PyCustomMember member : myMembers.values()) {
lookupElements.add(PyCustomMemberUtils.toLookUpElement(member, member.getShortType()));
}
return ArrayUtil.mergeArrays(ArrayUtil.toObjectArray(lookupElements),
((myTypeToMimic != null)
? myTypeToMimic.getCompletionVariants(completionPrefix, location, context)
: PsiElement.EMPTY_ARRAY));
}
@Nullable
@Override
public String getName() {
String mimicName = null;
if (myTypeToMimic != null) {
mimicName = myTypeToMimic.getName();
}
if (mimicName != null) {
return PyBundle.message("custom.type.mimic.name", mimicName);
}
return PyBundle.message("custom.type.name");
}
@Override
public boolean isBuiltin() {
return false;
}
@Override
public void assertValid(final String message) {
}
/**
* @param name name to check
* @return True if this class (not the one it mimics!) has member with passed name
*/
public boolean hasMember(@NotNull final String name) {
return myMembers.containsKey(name);
}
}
@@ -0,0 +1,245 @@
/*
* Copyright 2000-2014 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;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Custom (aka dynamic) type that delegates calls to some classes you pass to it.
* We say this this class <strong>mimics</strong> such classes.
* To be used for cases like "type()".
* It optionally filters methods using {@link ParentsMemberFilter}
*
* @author Ilya.Kazakevich
*/
public class PyCustomType implements PyClassLikeType, Predicate<RatedResolveResult> {
@NotNull
private final List<PyClassLikeType> myTypesToMimic = new ArrayList<PyClassLikeType>();
@Nullable
private final ParentsMemberFilter myFilter;
private final boolean myInstanceType;
/**
* @param filter filter to filter methods from classes (may be null to do no filtering)
* @param instanceType if true, then this class implements instance (it reports it is not definition and returns "this
* for {@link #toInstance()} call).
* @param typesToMimic types to "mimic": delegate calls to (must be one at least!)
*/
public PyCustomType(@Nullable final ParentsMemberFilter filter,
final boolean instanceType,
@NotNull final PyClassLikeType... typesToMimic) {
Preconditions.checkArgument(typesToMimic.length > 0, "Provide at least one class");
myFilter = filter;
myTypesToMimic.addAll(Arrays.asList(typesToMimic));
myInstanceType = instanceType;
}
/**
* @return class we mimic (if any). Check class manual for more info.
*/
@NotNull
public final List<PyClassLikeType> getTypesToMimic() {
return Collections.unmodifiableList(myTypesToMimic);
}
@Override
public final boolean isDefinition() {
return !myInstanceType;
}
@Override
public final PyClassLikeType toInstance() {
return myInstanceType ? this : new PyCustomType(myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[myTypesToMimic.size()]));
}
@Nullable
@Override
public final String getClassQName() {
return null;
}
@NotNull
@Override
public final List<PyClassLikeType> getSuperClassTypes(@NotNull final TypeEvalContext context) {
return Collections.emptyList();
}
@Nullable
@Override
public final List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
@Nullable final PyExpression location,
@NotNull final AccessDirection direction,
@NotNull final PyResolveContext resolveContext,
final boolean inherited) {
final List<RatedResolveResult> globalResult = new ArrayList<RatedResolveResult>();
// Delegate calls to classes, we mimic but filter if filter is set.
for (final PyClassLikeType parentType : myTypesToMimic) {
final List<? extends RatedResolveResult> results = parentType.resolveMember(name, location, direction, resolveContext, inherited);
if (results != null) {
globalResult.addAll(Collections2.filter(results, this));
}
}
return globalResult;
}
@Override
public final boolean isValid() {
return true;
}
@Nullable
@Override
public final PyClassLikeType getMetaClassType(@NotNull final TypeEvalContext context, final boolean inherited) {
return null;
}
@Override
public final boolean isCallable() {
return true; // We do not know, actually
}
@Nullable
@Override
public final PyType getReturnType(@NotNull final TypeEvalContext context) {
return (myInstanceType ? null : toInstance());
}
@Nullable
@Override
public final PyType getCallType(@NotNull final TypeEvalContext context, @NotNull final PyCallSiteExpression callSite) {
return getReturnType(context);
}
@Nullable
@Override
public final List<PyCallableParameter> getParameters(@NotNull final TypeEvalContext context) {
return null;
}
@Nullable
@Override
public final List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
@Nullable final PyExpression location,
@NotNull final AccessDirection direction,
@NotNull final PyResolveContext resolveContext) {
return resolveMember(name, location, direction, resolveContext, true);
}
@Override
public final Object[] getCompletionVariants(final String completionPrefix, final PsiElement location, final ProcessingContext context) {
final Collection<Object> lookupElements = new ArrayList<Object>();
for (final PyClassLikeType parentType : myTypesToMimic) {
lookupElements.addAll(Collections2.filter(Arrays.asList(parentType.getCompletionVariants(completionPrefix, location, context)),
new CompletionFilter()));
}
return lookupElements.toArray(new Object[lookupElements.size()]);
}
@Nullable
@Override
public final String getName() {
final Collection<String> classNames = new ArrayList<String>(myTypesToMimic.size());
for (final PyClassLikeType type : myTypesToMimic) {
String name = type.getName();
if (name == null && (type instanceof PyClassType)) {
name = ((PyClassType)type).getPyClass().getName();
}
if (name != null) {
classNames.add(name);
}
}
return PyBundle.message("custom.type.mimic.name", StringUtil.join(classNames, ","));
}
@Override
public final boolean isBuiltin() {
return false;
}
@Override
public final void assertValid(final String message) {
for (final PyClassLikeType type : myTypesToMimic) {
type.assertValid(message);
}
}
@Override
public final boolean apply(@Nullable final RatedResolveResult input) {
if (input == null) {
return false;
}
if (myFilter == null) {
return true; // No need to check
}
final PyElement pyElement = PyUtil.as(input.getElement(), PyElement.class);
if (pyElement == null) {
return false;
}
return myFilter.acceptMember(pyElement);
}
public interface ParentsMemberFilter {
boolean acceptMember(@NotNull PyElement element);
}
/**
* Predicate that filters completion using {@link #myFilter}
*/
private class CompletionFilter implements Predicate<Object> {
@Override
public final boolean apply(@Nullable final Object input) {
if (input == null) {
return false;
}
if (myFilter == null) {
return true; // No need to check
}
if (!(input instanceof LookupElement)) {
return true; // Do not know how to check
}
final PyElement pyElement = PyUtil.as(((LookupElement)input).getPsiElement(), PyElement.class);
if (pyElement == null) {
return false;
}
return myFilter.acceptMember(pyElement);
}
}
}
@@ -38,7 +38,7 @@ import com.intellij.util.PlatformUtils;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyCustomMembersType;
import com.jetbrains.python.PyCustomType;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.codeInsight.PyCustomMember;
@@ -607,13 +607,15 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
/**
* Checks if type is custom-member based and has custom member with certain name
* Checks if type is custom type and has custom member with certain name
* @param refName name to check
* @param type type
* @return true if has one
*/
private static boolean isHasCustomMember(@NotNull final String refName, @NotNull final PyType type) {
return (type instanceof PyCustomMembersType) && ((PyCustomMembersType)type).hasMember(refName);
// TODO: check
return false;
/*return (type instanceof PyCustomType) && ((PyCustomType)type).hasMember(refName);*/
}
/**
@@ -713,11 +715,15 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
return true;
}
}
if (type instanceof PyCustomMembersType) {
if (type instanceof PyCustomType) {
// Skip custom member types that mimics another class with fuzzy parents
PyClassType mimic = ((PyCustomMembersType)type).getTypeToMimic();
if (mimic != null && PyUtil.hasUnresolvedAncestors(mimic.getPyClass(), myTypeEvalContext)) {
return true;
for (final PyClassLikeType mimic : ((PyCustomType)type).getTypesToMimic()) {
if (!(mimic instanceof PyClassType)) {
continue;
}
if (PyUtil.hasUnresolvedAncestors(((PyClassType)mimic).getPyClass(), myTypeEvalContext)) {
return true;
}
}
}
if (type instanceof PyClassTypeImpl) {
@@ -360,6 +360,9 @@ public class PyCallExpressionHelper {
}
}
}
else if (qtype instanceof PyClassLikeType) {
return ((PyClassLikeType)qtype).isDefinition(); //Any definition means callable is classmethod
}
return false;
}
@@ -522,6 +525,10 @@ public class PyCallExpressionHelper {
final Callable callable = (Callable)target;
return Ref.create(callable.getCallType(context, call));
}
/*PyCallExpression.PyMarkedCallee markedCallee = call.resolveCallee(PyResolveContext.defaultContext().withTypeEvalContext(context));
if (markedCallee != null) {
return Ref.create(markedCallee.getCallable().getCallType(context, call));
}*/
return null;
}
@@ -1219,6 +1219,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
}
else {
for (PyExpression expression : getSuperClassExpressions()) {
context.getType(expression);
expression = unfoldClass(expression);
if (expression instanceof PyKeywordArgument) {
continue;
@@ -420,7 +420,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
@Nullable PsiElement anchor) {
for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
final PyType result = provider.getReferenceType(target, context, anchor);
if (result != null) return result;
if (result != null) {
return result;
}
}
return null;
@@ -32,6 +32,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.HashMap;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.PyCustomMember;
import com.jetbrains.python.codeInsight.PyCustomMemberUtils;
@@ -138,7 +139,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
resolving.add(key);
try {
return doResolveMember(name, location, direction, resolveContext, inherited);
return doResolveMember(name, location, direction, resolveContext, inherited, null);
}
finally {
resolving.remove(key);
@@ -146,14 +147,16 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
@Nullable
private List<? extends RatedResolveResult> doResolveMember(@NotNull String name,
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext resolveContext,
boolean inherited) {
public List<? extends RatedResolveResult> doResolveMember(@NotNull String name,
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext resolveContext,
boolean inherited,
@Nullable PyClassMembersProvider providerToSkip) {
final TypeEvalContext context = resolveContext.getTypeEvalContext();
PsiElement classMember =
resolveByOverridingMembersProviders(this, name, location); //overriding members provers have priority to normal resolve
resolveByOverridingMembersProviders(this, name, location,
providerToSkip); //overriding members provers have priority to normal resolve
if (classMember != null) {
return ResolveResultList.to(classMember);
}
@@ -188,7 +191,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
return ResolveResultList.to(classMember);
}
classMember = resolveByOverridingAncestorsMembersProviders(this, name, location);
classMember = resolveByOverridingAncestorsMembersProviders(this, name, location, providerToSkip);
if (classMember != null) {
return ResolveResultList.to(classMember);
}
@@ -225,7 +228,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
if (inherited) {
classMember =
resolveByMembersProviders(this, name, location); //ask providers after real class introspection as providers have less priority
resolveByMembersProviders(this, name, location,
providerToSkip); //ask providers after real class introspection as providers have less priority
}
if (classMember != null) {
@@ -236,7 +240,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
for (PyClassLikeType type : myClass.getAncestorTypes(context)) {
if (type instanceof PyClassType) {
final PyClass pyClass = ((PyClassType)type).getPyClass();
PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location);
PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location, providerToSkip);
if (superMember != null) {
return ResolveResultList.to(superMember);
@@ -345,8 +349,13 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
@Nullable
private static PsiElement resolveByMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) {
private static PsiElement resolveByMembersProviders(PyClassType aClass, String name,
@Nullable PsiElement location,
@Nullable PyClassMembersProvider providerToSkip) {
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
if (provider == providerToSkip) {
continue;
}
final PsiElement resolveResult = provider.resolveMember(aClass, name, location);
if (resolveResult != null) return resolveResult;
}
@@ -355,9 +364,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
@Nullable
private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) {
private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location,
@Nullable PyClassMembersProvider providerToSkip) {
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
if (provider instanceof PyOverridingClassMembersProvider) {
if (provider instanceof PyOverridingClassMembersProvider && provider != providerToSkip) {
final PsiElement resolveResult = provider.resolveMember(aClass, name, location);
if (resolveResult != null) return resolveResult;
}
@@ -367,9 +377,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
@Nullable
private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location) {
private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location,
@Nullable PyClassMembersProvider providerToSkip) {
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
if (provider instanceof PyOverridingAncestorsClassMembersProvider) {
if (provider instanceof PyOverridingAncestorsClassMembersProvider && !(provider == providerToSkip)) {
final PsiElement resolveResult = provider.resolveMember(type, name, location);
if (resolveResult != null) return resolveResult;
}
@@ -409,28 +420,50 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
if (namesAlready == null) {
namesAlready = new HashSet<String>();
}
List<Object> ret = new ArrayList<Object>();
// TODO: Doc
final Map<String, Pair<ElementType, Object>> usedNames = new HashMap<String, Pair<ElementType, Object>>();
boolean suppressParentheses = context.get(CTX_SUPPRESS_PARENTHESES) != null;
addOwnClassMembers(location, namesAlready, suppressParentheses, ret);
addOwnClassMembers(location, namesAlready, suppressParentheses, usedNames);
namesAlready.addAll(usedNames.keySet());
PsiFile origin = (location != null) ?
CompletionUtil.getOriginalOrSelf(location)
.getContainingFile() :
null;
final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(myClass.getProject(), origin);
addInheritedMembers(prefix, location, namesAlready, context, ret, typeEvalContext);
final List<Object> inheritedMembers = addInheritedMembers(prefix, location, namesAlready, context, usedNames, typeEvalContext);
namesAlready.addAll(usedNames.keySet());
// Move to func
// TODO: Override only if Overriding
// from providers
for (final PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
for (final PyCustomMember member : provider.getMembers(this, location)) {
final String name = member.getName();
if (!namesAlready.contains(name)) {
ret.add(PyCustomMemberUtils.toLookUpElement(member, getName()));
final LookupElementBuilder element = PyCustomMemberUtils.toLookUpElement(member, getName());
final Pair<ElementType, Object> usedNameInfo = usedNames.get(name);
// TODO: Doc
if (usedNameInfo == null ||
(usedNameInfo.first == ElementType.INHERITED && provider instanceof PyOverridingAncestorsClassMembersProvider) ||
(usedNameInfo.first == ElementType.OWN && provider instanceof PyOverridingClassMembersProvider)) {
usedNames.put(name, Pair.<ElementType, Object>create(ElementType.BY_PROVIDER, element));
} else {
int i = 1;
}
}
}
final List<Object> ret = new ArrayList<Object>(getElementsFromUsedNames(usedNames));
namesAlready.addAll(usedNames.keySet());
ret.addAll(inheritedMembers);
if (!myClass.isNewStyleClass()) {
final PyBuiltinCache cache = PyBuiltinCache.getInstance(myClass);
final PyClassType classobjType = cache.getOldstyleClassobjType();
@@ -449,7 +482,11 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
return ret.toArray();
}
private void addOwnClassMembers(PsiElement expressionHook, Set<String> namesAlready, boolean suppressParentheses, List<Object> ret) {
private void addOwnClassMembers(PsiElement expressionHook,
Set<String> namesAlready,
boolean suppressParentheses,
@NotNull final Map<String, Pair<ElementType, Object>> usedNames) {
PyClass containingClass = PsiTreeUtil.getParentOfType(expressionHook, PyClass.class);
if (containingClass != null) {
containingClass = CompletionUtil.getOriginalElement(containingClass);
@@ -475,12 +512,12 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
if (namesAlready.contains(name)) continue;
if (!withinOurClass && isClassPrivate(name)) continue;
namesAlready.add(name);
ret.add(le);
usedNames.put(name, Pair.<ElementType, Object>create(ElementType.OWN, le));
}
if (slots != null) {
for (String name : slots) {
if (!namesAlready.contains(name)) {
ret.add(LookupElementBuilder.create(name));
usedNames.put(name, Pair.<ElementType, Object>create(ElementType.OWN, LookupElementBuilder.create(name)));
}
}
}
@@ -494,12 +531,14 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
return false;
}
private void addInheritedMembers(String name,
@NotNull
private List<Object> addInheritedMembers(String name,
PsiElement expressionHook,
Set<String> namesAlready,
ProcessingContext context,
List<Object> ret,
@NotNull final Map<String, Pair<ElementType, Object>> usedNames,
@NotNull TypeEvalContext typeEvalContext) {
final List<Object> ret = new ArrayList<Object>();
for (PyExpression expression : myClass.getSuperClassExpressions()) {
final PsiReference reference = expression.getReference();
PsiElement element = null;
@@ -521,13 +560,14 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
for (Object ob : ancestry) {
String inheritedName = ob.toString();
if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) {
ret.add(ob);
usedNames.put(inheritedName, Pair.create(ElementType.INHERITED, ob));
namesAlready.add(inheritedName);
}
}
ContainerUtil.addAll(ret, ancestry);
}
}
return ret;
}
private static boolean isClassPrivate(String lookup_string) {
@@ -607,4 +647,20 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
return new PyClassTypeImpl(pyClass, isDefinition);
}
// TOOD: Doc
private static List<Object> getElementsFromUsedNames(@NotNull final Map<String, Pair<ElementType, Object>> usedNames) {
final List<Object> ret = new ArrayList<Object>();
for (final Pair<ElementType, Object> objectPair : usedNames.values()) {
ret.add(objectPair.second);
}
return ret;
}
// TODO: Doc
private enum ElementType {
OWN,
INHERITED,
BY_PROVIDER
}
}