diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt index 9ae023523b8a..8b48f640fe35 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt @@ -82,9 +82,9 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() val psiElement = item.psiElement if (psiElement is PsiNamedElement && psiElement.containingFile != null) { val name = QualifiedName.fromDottedString(item.lookupString).removeLastComponent().toString() - val commonAlias = PyPackageAliasesProvider.commonImportAliases[name] - val nameToImport = commonAlias ?: name - AddImportHelper.addImportStatement(context.file, nameToImport, if (commonAlias != null) name else null, + val packageNameForAlias = PyPackageAliasesProvider.commonImportAliases[name] + val nameToImport = packageNameForAlias ?: name + AddImportHelper.addImportStatement(context.file, nameToImport, if (packageNameForAlias != null) name else null, AddImportHelper.getImportPriority(context.file, psiElement.containingFile), ref?.element as? PyElement) } @@ -115,9 +115,9 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() val packageNameForAlias = PyPackageAliasesProvider.commonImportAliases[qualifier.toString()] val packageName = if (packageNameForAlias != null) QualifiedName.fromDottedString(packageNameForAlias) else qualifier val resultMatchingCompleteReference = result.withPrefixMatcher(QualifiedNameMatcher(qualifiedName)) + val scope = PySearchUtilBase.defaultSuggestionScope(parameters.originalFile) - val availableModules = PyModuleNameIndex.findByQualifiedName(packageName, project, - PySearchUtilBase.defaultSuggestionScope(parameters.originalFile)) + val availableModules = PyModuleNameIndex.findByQualifiedName(packageName, project, scope) .asSequence() if (packageNameForAlias == null) { @@ -141,11 +141,10 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() .filter { it.containingFile != null } .filterNot { it is PsiFileSystemItem } .filterNot { it.name == null || it.name!!.startsWith('_') } - .filter { attribute.isEmpty() || resultMatchingCompleteReference.prefixMatcher.prefixMatches("$packageName.${it.name}") } + .filter { attribute.isEmpty() || resultMatchingCompleteReference.prefixMatcher.prefixMatches("$qualifier.${it.name}") } .mapNotNull { - val qualifiedNameToSuggest = "$qualifier.${it.name}" - if (suggestedQualifiedNames.add(qualifiedNameToSuggest)) { - LookupElementBuilder.create(it, qualifiedNameToSuggest) + if (suggestedQualifiedNames.add("$packageName.${it.name}")) { + LookupElementBuilder.create(it, "$qualifier.${it.name}") .withIcon(it.getIcon(0)) .withInsertHandler(getInsertHandler(it, parameters.position)) .withTypeText(packageNameForAlias) @@ -158,14 +157,13 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() result.restartCompletionOnAnyPrefixChange() return } - val scope = PySearchUtilBase.defaultSuggestionScope(parameters.originalFile) PyQualifiedNameCompletionMatcher.processMatchingExportedNames( - packageName.append(attribute), if (packageNameForAlias != null) qualifiedName else null, parameters.originalFile, scope, + qualifiedName, parameters.originalFile, scope, Processor { ProgressManager.checkCanceled() if (suggestedQualifiedNames.add(it.qualifiedName.toString())) { resultMatchingCompleteReference.addElement(LookupElementBuilder - .createWithSmartPointer(it.qualifiedNameWithUserTypedAlias.toString(), + .createWithSmartPointer(it.qualifiedName.toString(), it.element) .withIcon(it.element.getIcon(0)) .withInsertHandler(getInsertHandler(it.element, parameters.position))) diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java b/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java index 5ef8a3c5c9fe..47fd5e0f222a 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java @@ -32,7 +32,6 @@ public class PyQualifiedNameCompletionMatcher { } public static void processMatchingExportedNames(@NotNull QualifiedName qualifiedNamePattern, - @Nullable QualifiedName originallyTypedAlias, @NotNull PsiFile currentFile, @NotNull GlobalSearchScope scope, @NotNull Processor processor) { @@ -78,7 +77,7 @@ public class PyQualifiedNameCompletionMatcher { } QualifiedName attributeQualifiedName = importPath.append(attributeName); if (alreadySuggestedAttributes.add(attributeQualifiedName)) { - if (!processor.process(new ExportedName(attributeQualifiedName, originallyTypedAlias, element))) { + if (!processor.process(new ExportedName(attributeQualifiedName, element))) { return false; } } @@ -127,12 +126,10 @@ public class PyQualifiedNameCompletionMatcher { public static final class ExportedName { private final QualifiedName myQualifiedName; - private final QualifiedName myOriginallyTypedQName; private final PyElement myElement; - private ExportedName(@NotNull QualifiedName qualifiedName, @Nullable QualifiedName originallyTypedQName, @NotNull PyElement element) { + private ExportedName(@NotNull QualifiedName qualifiedName, @NotNull PyElement element) { myQualifiedName = qualifiedName; - myOriginallyTypedQName = originallyTypedQName; myElement = element; } @@ -141,22 +138,10 @@ public class PyQualifiedNameCompletionMatcher { return myQualifiedName; } - @Nullable - public QualifiedName getOriginallyTypedQName() { - return myOriginallyTypedQName; - } - @NotNull public PyElement getElement() { return myElement; } - - @NotNull - public QualifiedName getQualifiedNameWithUserTypedAlias() { - return myOriginallyTypedQName != null - ? myOriginallyTypedQName.removeLastComponent().append(myQualifiedName.getLastComponent()) - : myQualifiedName; - } } public static final class QualifiedNameMatcher extends PrefixMatcher { diff --git a/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/main.after.py b/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/main.after.py new file mode 100644 index 000000000000..73817b18a95e --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/main.after.py @@ -0,0 +1,3 @@ +import pylab as p + +p.argmax() \ No newline at end of file diff --git a/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/main.py b/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/main.py new file mode 100644 index 000000000000..78171c7f3ee6 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/main.py @@ -0,0 +1 @@ +p.argma \ No newline at end of file diff --git a/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/pylab.py b/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/pylab.py new file mode 100644 index 000000000000..d66aab884ada --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AliasAttributesShouldNotBeDuplicated/pylab.py @@ -0,0 +1,2 @@ +def argmax(): + pass diff --git a/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/fileinput/__init__.py b/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/fileinput/__init__.py new file mode 100644 index 000000000000..9238ebaebbe7 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/fileinput/__init__.py @@ -0,0 +1,2 @@ +def input(): + pass diff --git a/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/main.py b/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/main.py new file mode 100644 index 000000000000..971726cfb5b0 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/main.py @@ -0,0 +1 @@ +np.in \ No newline at end of file diff --git a/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/numpy/__init__.py b/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/numpy/__init__.py new file mode 100644 index 000000000000..4afd05293151 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/FuzzyResultsShouldBeAddedToAliasAttributesCompletion/numpy/__init__.py @@ -0,0 +1,2 @@ +def invert(): + pass diff --git a/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/main.after.py b/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/main.after.py new file mode 100644 index 000000000000..46dd5b732baf --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/main.after.py @@ -0,0 +1 @@ +np.r \ No newline at end of file diff --git a/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/main.py b/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/main.py new file mode 100644 index 000000000000..15f0768dfba4 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/main.py @@ -0,0 +1 @@ +np.r \ No newline at end of file diff --git a/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/numpy/__init__.py b/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/numpy/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/numpy/random/__init__.py b/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/numpy/random/__init__.py new file mode 100644 index 000000000000..8337e550b885 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/ShowOnlyImmediateAttributesForAliases/numpy/random/__init__.py @@ -0,0 +1,2 @@ +def rand(): + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java b/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java index b5496a75ea5c..e6a43bd476e2 100644 --- a/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java @@ -114,6 +114,27 @@ public class PyNotImportedQualifiedNameCompletionTest extends PyTestCase { doTestBasicCompletion(); } + //PY-47253 + public void testShowOnlyImmediateAttributesForAliases() { + doTestBasicCompletion(); + } + + //PY-47253 + public void testFuzzyResultsShouldBeAddedToAliasAttributesCompletion() { + final String testName = getTestName(false); + myFixture.copyDirectoryToProject(testName, ""); + myFixture.configureByFile("main.py"); + myFixture.completeBasic(); + List variants = myFixture.getLookupElementStrings(); + assertEquals(2, variants.size()); + assertContainsElements(variants, "np.invert", "fileinput.input"); + } + + //PY-47253 + public void testAliasAttributesShouldNotBeDuplicated() { + doTestBasicCompletion(); + } + private void doTestBasicCompletion() { final String testName = getTestName(false); myFixture.copyDirectoryToProject(testName, "");