From bf58611946df07bd2e42ab24f4b72f2d705a93e6 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Jul 2010 16:22:07 +0400 Subject: [PATCH] completion of instance attributes shows variants from __slots__ (PY-1211) --- .../python/psi/impl/PyClassImpl.java | 21 ++++--- .../python/psi/types/PyClassType.java | 56 +++++++++++++------ python/testData/completion/slots.after.py | 5 ++ python/testData/completion/slots.py | 5 ++ .../python/PythonCompletionTest.java | 4 ++ 5 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 python/testData/completion/slots.after.py create mode 100644 python/testData/completion/slots.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index b3b4f3bde784..87a351faf9c0 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -721,13 +721,18 @@ public class PyClassImpl extends PyPresentableElementImpl implement } public void processDeclarations(@NotNull PsiScopeProcessor processor) { - // class level + if (!processClassLevelDeclarations(processor)) return; + if (!processInstanceLevelDeclarations(processor)) return; + processor.execute(this, ResolveState.initial()); + } + + public boolean processClassLevelDeclarations(PsiScopeProcessor processor) { final PyClassStub stub = getStub(); if (stub != null) { final List children = stub.getChildrenStubs(); for (StubElement child : children) { if (!processor.execute(child.getPsi(), ResolveState.initial())) { - return; + return false; } } } @@ -735,16 +740,14 @@ public class PyClassImpl extends PyPresentableElementImpl implement final PsiElement the_psi = getNode().getPsi(); PyResolveUtil.treeCrawlUp(processor, true, the_psi, the_psi); } + return true; + } - // instance level + public boolean processInstanceLevelDeclarations(PsiScopeProcessor processor) { for(PyTargetExpression expr: getInstanceAttributes()) { - if (!processor.execute(expr, ResolveState.initial())) return; + if (!processor.execute(expr, ResolveState.initial())) return false; } - // - if (processor instanceof VariantsProcessor) { - return; - } - processor.execute(this, ResolveState.initial()); + return true; } public int getTextOffset() { diff --git a/python/src/com/jetbrains/python/psi/types/PyClassType.java b/python/src/com/jetbrains/python/psi/types/PyClassType.java index 311e97cd216f..0decd76a51e9 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassType.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassType.java @@ -6,7 +6,6 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiElement; -import com.intellij.psi.ResolveState; import com.intellij.util.ProcessingContext; import com.intellij.util.SmartList; import com.jetbrains.python.codeInsight.PyDynamicMember; @@ -145,9 +144,10 @@ public class PyClassType implements PyType { } public Object[] getCompletionVariants(final PyQualifiedExpression referenceExpression, ProcessingContext context) { - List classList = new ParentMatcher(PyClass.class).search(referenceExpression); - boolean withinOurClass = classList != null && classList.get(0) == this; Set namesAlready = context.get(CTX_NAMES); + if (namesAlready == null) { + namesAlready = new HashSet(); + } List ret = new ArrayList(); Condition underscoreFilter = new PyUtil.UnderscoreFilter(PyUtil.getInitialUnderscores(referenceExpression.getName())); // from providers @@ -159,23 +159,48 @@ public class PyClassType implements PyType { } } } - // from our own class + + addOwnClassMembers(referenceExpression, namesAlready, ret, underscoreFilter); + + addInheritedMembers(referenceExpression, context, ret); + + return ret.toArray(); + } + + private void addOwnClassMembers(PyQualifiedExpression referenceExpression, + Set namesAlready, + List ret, Condition underscoreFilter) { + List classList = new ParentMatcher(PyClass.class).search(referenceExpression); + boolean withinOurClass = classList != null && classList.get(0) == this; + final VariantsProcessor processor = new VariantsProcessor( referenceExpression, new PyResolveUtil.FilterNotInstance(myClass), underscoreFilter ); - ((PyClassImpl) myClass).processDeclarations(processor); - if (namesAlready != null) { - for (LookupElement le : processor.getResultList()) { - String name = le.getLookupString(); - if (namesAlready.contains(name)) continue; - if (!withinOurClass && isClassPrivate(name)) continue; - namesAlready.add(name); - ret.add(le); + ((PyClassImpl) myClass).processClassLevelDeclarations(processor); + + List slots = myClass.isNewStyleClass() ? myClass.getSlots() : null; + if (slots != null) { + processor.setAllowedNames(slots); + } + ((PyClassImpl) myClass).processInstanceLevelDeclarations(processor); + + for (LookupElement le : processor.getResultList()) { + String name = le.getLookupString(); + if (namesAlready.contains(name)) continue; + if (!withinOurClass && isClassPrivate(name)) continue; + namesAlready.add(name); + ret.add(le); + } + if (slots != null) { + for (String name : slots) { + if (!namesAlready.contains(name)) { + ret.add(LookupElementBuilder.create(name)); + } } } - else { - ret.addAll(processor.getResultList()); - } + } + + private void addInheritedMembers(PyQualifiedExpression referenceExpression, ProcessingContext context, List ret) { for (PyClass ancestor : myClass.getSuperClasses()) { Object[] ancestry = (new PyClassType(ancestor, true)).getCompletionVariants(referenceExpression, context); for (Object ob : ancestry) { @@ -189,7 +214,6 @@ public class PyClassType implements PyType { } ret.addAll(Arrays.asList(ancestry)); } - return ret.toArray(); } private static boolean isClassPrivate(String lookup_string) { diff --git a/python/testData/completion/slots.after.py b/python/testData/completion/slots.after.py new file mode 100644 index 000000000000..a3346ace28ed --- /dev/null +++ b/python/testData/completion/slots.after.py @@ -0,0 +1,5 @@ +class A(object): + __slots__ = ['foo', 'bar'] + +a = A() +a.foo diff --git a/python/testData/completion/slots.py b/python/testData/completion/slots.py new file mode 100644 index 000000000000..8d53fa7cb44d --- /dev/null +++ b/python/testData/completion/slots.py @@ -0,0 +1,5 @@ +class A(object): + __slots__ = ['foo', 'bar'] + +a = A() +a.f diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 1f974541e631..92468255dba1 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -160,4 +160,8 @@ public class PythonCompletionTest extends PyLightFixtureTestCase { myFixture.completeBasic(); assertSameElements(myFixture.getLookupElementStrings(), Arrays.asList("my_foo", "my_bar")); } + + public void testSlots() throws Exception { // PY-1211 + doTest(); + } } \ No newline at end of file