Complete class-private names only in right contexts (PY-570).

Only complete underscore-starting names if explicitly asked to (PY-568).
This commit is contained in:
Dmitry Cheryasov
2010-04-08 20:47:34 +03:00
parent 7d19bef359
commit cd224f55a1
19 changed files with 126 additions and 26 deletions
@@ -7,6 +7,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.*;
@@ -704,4 +705,21 @@ public class PyUtil {
else if (name.startsWith("_")) underscores = 1;
return underscores;
}
public static class UnderscoreFilter implements Condition<String> {
private int myAllowed; // how many starting underscores is allowed: 0 is none, 1 is only one, 2 is two and more.
public UnderscoreFilter(int allowed) {
myAllowed = allowed;
}
public boolean value(String name) {
if (name == null) return false;
if (name.length() < 1) return false; // empty strings make no sense
int have_underscores = 0;
if (name.charAt(0) == '_') have_underscores = 1;
if (have_underscores != 0 && name.length() > 1 && name.charAt(1) == '_') have_underscores = 2;
return myAllowed >= have_underscores;
}
}
}
@@ -70,7 +70,7 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
final Set<String> names_already = new HashSet<String>(); // don't propose already imported names
String ref_name = myElement.getName();
Condition<PsiElement> node_filter = new PyResolveUtil.FilterNameNotIn(names_already);
Condition<String> underscore_filter = new UnderscoreFilter(PyUtil.getInitialUnderscores(ref_name));
Condition<String> underscore_filter = new PyUtil.UnderscoreFilter(PyUtil.getInitialUnderscores(ref_name));
// are we in "import _" or "from foo import _"?
PyFromImportStatement from_import = PsiTreeUtil.getParentOfType(myElement, PyFromImportStatement.class);
if (from_import != null && myElement.getParent() != from_import) { // in "from foo import _"
@@ -285,7 +285,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
// include our own names
final int underscores = PyUtil.getInitialUnderscores(myElement.getName());
final UnderscoreFilter filter = new UnderscoreFilter(underscores);
final PyUtil.UnderscoreFilter filter = new PyUtil.UnderscoreFilter(underscores);
final VariantsProcessor processor = new VariantsProcessor(myElement, null, filter);
PyResolveUtil.treeCrawlUp(processor, realContext); // names from here
PyResolveUtil.scanOuterContext(processor, realContext); // possible names from around us at call time
@@ -425,23 +425,4 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
return myElement.hashCode();
}
/**
* Logical conjunction.
*/
protected static class UnderscoreFilter implements Condition<String> {
private int myAllowed; // how many starting underscores is allowed: 0 is none, 1 is only one, 2 is two and more.
public UnderscoreFilter(int allowed) {
myAllowed = allowed;
}
public boolean value(String name) {
if (name == null) return false;
if (name.length() < 1) return false; // empty strings make no sense
int have_underscores = 0;
if (name.charAt(0) == '_') have_underscores = 1;
if (have_underscores != 0 && name.length() > 1 && name.charAt(1) == '_') have_underscores = 2;
return myAllowed >= have_underscores;
}
}
}
@@ -3,13 +3,16 @@ package com.jetbrains.python.psi.types;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.codeInsight.PyDynamicMember;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.PyUtil;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.patterns.ParentMatcher;
import com.jetbrains.python.psi.resolve.PyResolveUtil;
import com.jetbrains.python.psi.resolve.ResolveProcessor;
import com.jetbrains.python.psi.resolve.VariantsProcessor;
@@ -115,19 +118,30 @@ public class PyClassType implements PyType {
}
public Object[] getCompletionVariants(final PyReferenceExpression referenceExpression, ProcessingContext context) {
List<? extends PsiElement> class_list = new ParentMatcher(PyClass.class).search(referenceExpression);
boolean within_our_class = class_list != null && class_list.get(0) == this;
Set<String> names_already = context.get(PyType.CTX_NAMES);
final VariantsProcessor processor = new VariantsProcessor(referenceExpression, new PyResolveUtil.FilterNotInstance(myClass));
myClass.processDeclarations(processor, ResolveState.initial(), null, referenceExpression);
List<Object> ret = new ArrayList<Object>();
Condition<String> underscore_filter = new PyUtil.UnderscoreFilter(PyUtil.getInitialUnderscores(referenceExpression.getName()));
// from providers
for(PyClassMembersProvider provider: Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
for (PyDynamicMember member : provider.getMembers(myClass)) {
ret.add(LookupElementBuilder.create(member.getName()).setIcon(member.getIcon()).setTypeText(member.getShortType()));
final String name = member.getName();
if (underscore_filter.value(name)) {
ret.add(LookupElementBuilder.create(name).setIcon(member.getIcon()).setTypeText(member.getShortType()));
}
}
}
// from our own class
final VariantsProcessor processor = new VariantsProcessor(
referenceExpression, new PyResolveUtil.FilterNotInstance(myClass), underscore_filter
);
myClass.processDeclarations(processor, ResolveState.initial(), null, referenceExpression);
if (names_already != null) {
for (LookupElement le : processor.getResultList()) {
String name = le.getLookupString();
if (names_already.contains(name)) continue;
if (! within_our_class && isClassPrivate(name)) continue;
names_already.add(name);
ret.add(le);
}
@@ -137,9 +151,10 @@ public class PyClassType implements PyType {
Object[] ancestry = (new PyClassType(ancestor, true)).getCompletionVariants(referenceExpression, context);
for (Object ob : ancestry) {
if (ob instanceof LookupElementBuilder) {
ret.add(((LookupElementBuilder)ob).setTypeText(ancestor.getName()));
final LookupElementBuilder lookup_elt = (LookupElementBuilder)ob;
if (! isClassPrivate(lookup_elt.getLookupString())) ret.add(lookup_elt.setTypeText(ancestor.getName()));
} else {
ret.add(ob);
if (! isClassPrivate(ob.toString())) ret.add(ob);
}
}
ret.addAll(Arrays.asList(ancestry));
@@ -147,6 +162,10 @@ public class PyClassType implements PyType {
return ret.toArray();
}
private static boolean isClassPrivate(String lookup_string) {
return lookup_string.startsWith("__") && ! lookup_string.endsWith("__");
}
public String getName() {
PyClass cls = getPyClass();
if (cls != null)
@@ -0,0 +1,3 @@
class Foo:
__BOO = 1
z = __BOO + 1
@@ -0,0 +1,3 @@
class Foo:
__BOO = 1
z = __B<caret> + 1
@@ -0,0 +1,4 @@
class Foo:
__BOO = 1
def foo(self):
z = self.__B
@@ -0,0 +1,4 @@
class Foo:
__BOO = 1
def foo(self):
z = self.__B<caret>
@@ -0,0 +1,6 @@
class Foo:
__BOO = 1
class Boo(Foo):
z = __B
@@ -0,0 +1,6 @@
class Foo:
__BOO = 1
class Boo(Foo):
z = __B<caret>
@@ -0,0 +1,4 @@
class Foo:
__BOO = 1
z = Foo.__B
@@ -0,0 +1,4 @@
class Foo:
__BOO = 1
z = Foo.__B<caret>
@@ -0,0 +1,4 @@
_zoo = 1
__zar = 2
_zoo
@@ -0,0 +1,4 @@
_zoo = 1
__zar = 2
_z<caret>
@@ -0,0 +1,4 @@
_zoo = 1
__zar = 2
__zar
@@ -0,0 +1,4 @@
_zoo = 1
__zar = 2
__z<caret>
@@ -0,0 +1,4 @@
_zoo = 1
__zar = 2
_zoo
@@ -0,0 +1,4 @@
_zoo = 1
__zar = 2
_<caret>
@@ -55,6 +55,30 @@ public class PythonCompletionTest extends PyLightFixtureTestCase {
doTest();
}
public void testClassPrivate() throws Exception {
doTest();
}
public void testClassPrivateNotInherited() throws Exception {
doTest();
}
public void testClassPrivateNotPublic() throws Exception {
doTest();
}
public void testTwoUnderscores() throws Exception {
doTest();
}
public void testOneUnderscore() throws Exception {
doTest();
}
public void testTwoUnderscoresNotOne() throws Exception {
doTest();
}
public void testPy255() throws Exception {
final String dirname = "completion/";
final String testName = dirname + "moduleClass";