diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 904c36f72c2e..5397623c773e 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -42,6 +42,7 @@ + diff --git a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java index d2b005619951..198c761ea77b 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java @@ -8,9 +8,9 @@ import com.intellij.util.ProcessingContext; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.patterns.SyntaxMatchers; import com.jetbrains.python.psi.resolve.*; +import com.jetbrains.python.psi.stubs.PyClassNameIndexInsensitive; import com.jetbrains.python.psi.stubs.PyFunctionNameIndex; import com.jetbrains.python.psi.types.*; -import com.jetbrains.python.toolbox.Maybe; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -156,6 +156,21 @@ public class PyQualifiedReferenceImpl extends PyReferenceImpl { return qualifierType.getCompletionVariants(myElement, ctx); } } + return getUntypedVariants(); + } + + private Object[] getUntypedVariants() { + final PyExpression qualifierElement = myElement.getQualifier(); + if (qualifierElement instanceof PyReferenceExpression) { + PyReferenceExpression qualifier = (PyReferenceExpression)qualifierElement; + if (qualifier.getQualifier() == null) { + final String className = qualifier.getText(); + final Collection classes = PyClassNameIndexInsensitive.find(className, getElement().getProject()); + if (classes.size() == 1) { + return new PyClassType(classes.iterator().next(), false).getCompletionVariants(myElement, new ProcessingContext()); + } + } + } return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java index e7fb6c76d1f1..d13613bd7104 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java @@ -14,6 +14,7 @@ import com.jetbrains.python.psi.PyStubElementType; import com.jetbrains.python.psi.impl.PyClassImpl; import com.jetbrains.python.psi.impl.PyQualifiedName; import com.jetbrains.python.psi.stubs.PyClassNameIndex; +import com.jetbrains.python.psi.stubs.PyClassNameIndexInsensitive; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PySuperClassIndex; @@ -74,6 +75,7 @@ public class PyClassElementType extends PyStubElementType final String name = stub.getName(); if (name != null) { sink.occurrence(PyClassNameIndex.KEY, name); + sink.occurrence(PyClassNameIndexInsensitive.KEY, name.toLowerCase()); } for(PyQualifiedName s: stub.getSuperClasses()) { if (s != null) { diff --git a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndexInsensitive.java b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndexInsensitive.java new file mode 100644 index 000000000000..d5972dea8cac --- /dev/null +++ b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndexInsensitive.java @@ -0,0 +1,27 @@ +package com.jetbrains.python.psi.stubs; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.stubs.StringStubIndexExtension; +import com.intellij.psi.stubs.StubIndex; +import com.intellij.psi.stubs.StubIndexKey; +import com.intellij.util.io.KeyDescriptor; +import com.jetbrains.python.psi.PyClass; + +import java.util.Collection; + +/** + * @author yole + */ +public class PyClassNameIndexInsensitive extends StringStubIndexExtension { + public static final StubIndexKey KEY = StubIndexKey.createIndexKey("Py.class.shortNameInsensitive"); + + @Override + public StubIndexKey getKey() { + return KEY; + } + + public static Collection find(String name, Project project) { + return StubIndex.getInstance().get(KEY, name.toLowerCase(), project, ProjectScope.getProjectScope(project)); + } +} diff --git a/python/testData/completion/classNameFromVarName.after.py b/python/testData/completion/classNameFromVarName.after.py new file mode 100644 index 000000000000..750ff360bf11 --- /dev/null +++ b/python/testData/completion/classNameFromVarName.after.py @@ -0,0 +1,5 @@ +class Product: + def doStuff(self): pass + +def foo(product): + product.doStuff() \ No newline at end of file diff --git a/python/testData/completion/classNameFromVarName.py b/python/testData/completion/classNameFromVarName.py new file mode 100644 index 000000000000..366c9a90df3a --- /dev/null +++ b/python/testData/completion/classNameFromVarName.py @@ -0,0 +1,5 @@ +class Product: + def doStuff(self): pass + +def foo(product): + product.doS \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index b95bc16fa227..c4dbc7c3419d 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -111,6 +111,10 @@ public class PythonCompletionTest extends PyLightFixtureTestCase { doTest(); } + public void testClassNameFromVarName() throws Exception { + doTest(); + } + public void testImportModule() throws Exception { final String testName = "completion/" + getTestName(true); myFixture.configureByFiles(testName + ".py", "completion/someModule.py");