mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user