From 591ccf31268547ecb4d2a40fb645d344628901a6 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 23 Nov 2020 13:11:51 +0300 Subject: [PATCH] PY-45541 Display symbol's canonical import path as tail text in the lookup Previously, we used the corresponding element's presentation for this purpose, but it showed the shortest qualified name of its module, not necessarily the "canonical" name that was actually used for importing. For instance, we could have suggested `ndarray` from `numpy.core._multiarray_umath` but imported it as `from numpy import ndarray`. It was both misleading and could have led to duplicate entries for the same symbol among suggestions. GitOrigin-RevId: abb350feeac9fd212d47dc0943442e0b16b21c9a --- .../PyClassNameCompletionContributor.java | 16 +++++++--------- .../canonicalImportPathUsedAsLookupTailText.py | 1 + .../pkg/__init__.py | 1 + .../pkg/mod.py | 5 +++++ .../python/PyClassNameCompletionTest.java | 16 ++++++++++++++++ 5 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/canonicalImportPathUsedAsLookupTailText.py create mode 100644 python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/__init__.py create mode 100644 python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/mod.py diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java index bfc58bdd547d..63ee2f36d55c 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java @@ -7,8 +7,6 @@ import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.PrioritizedLookupElement; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.navigation.ItemPresentation; -import com.intellij.navigation.NavigationItem; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; @@ -24,6 +22,7 @@ import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.psi.search.PySearchUtilBase; import com.jetbrains.python.psi.stubs.PyClassNameIndex; import com.jetbrains.python.psi.stubs.PyFunctionNameIndex; @@ -125,16 +124,15 @@ public class PyClassNameCompletionContributor extends PyExtendedCompletionContri ProgressManager.checkCanceled(); if (!condition.value(element)) return true; String name = element.getName(); - ItemPresentation itemPresentation = ((NavigationItem)element).getPresentation(); - if (name == null || itemPresentation == null) return true; - String locationString = itemPresentation.getLocationString(); - if (locationString == null) return true; - String uniquenessKey = name + locationString; - if (alreadySuggested.add(uniquenessKey)) { + if (name == null) return true; + QualifiedName importPath = QualifiedNameFinder.findCanonicalImportPath(element, targetFile); + if (importPath == null) return true; + String qualifiedName = importPath + "." + name; + if (alreadySuggested.add(qualifiedName)) { LookupElementBuilder lookupElement = LookupElementBuilder .createWithSmartPointer(name, element) .withIcon(element.getIcon(0)) - .withTailText(" " + locationString, true) + .withTailText(" (" + importPath + ")", true) .withInsertHandler(insertHandler); resultSet.addElement(elementHandler.apply(lookupElement)); } diff --git a/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/canonicalImportPathUsedAsLookupTailText.py b/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/canonicalImportPathUsedAsLookupTailText.py new file mode 100644 index 000000000000..727760c19d9d --- /dev/null +++ b/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/canonicalImportPathUsedAsLookupTailText.py @@ -0,0 +1 @@ +my_ diff --git a/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/__init__.py b/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/__init__.py new file mode 100644 index 000000000000..c0e2262456a3 --- /dev/null +++ b/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/__init__.py @@ -0,0 +1 @@ +from .mod import my_func diff --git a/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/mod.py b/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/mod.py new file mode 100644 index 000000000000..85f09741347e --- /dev/null +++ b/python/testData/completion/className/canonicalImportPathUsedAsLookupTailText/pkg/mod.py @@ -0,0 +1,5 @@ +def my_func(): + pass + + +my_var = 42 diff --git a/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java b/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java index ba66b2d85cbc..2ee54383f31e 100644 --- a/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java @@ -8,6 +8,8 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.testFramework.fixtures.TestLookupElementPresentation; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.formatter.PyCodeStyleSettings; @@ -164,6 +166,20 @@ public class PyClassNameCompletionTest extends PyTestCase { assertEquals(1, Collections.frequency(allVariants, "my_func")); } + // PY-45541 + public void testCanonicalImportPathUsedAsLookupTailText() { + LookupElement[] lookupElements = doExtendedCompletion(); + LookupElement reexportedFunc = ContainerUtil.find(lookupElements, variant -> variant.getLookupString().equals("my_func")); + assertNotNull(reexportedFunc); + TestLookupElementPresentation funcPresentation = TestLookupElementPresentation.renderReal(reexportedFunc); + assertEquals(" (pkg)", funcPresentation.getTailText()); + + LookupElement notExportedVar = ContainerUtil.find(lookupElements, variant -> variant.getLookupString().equals("my_var")); + assertNotNull(notExportedVar); + TestLookupElementPresentation varPresentation = TestLookupElementPresentation.renderReal(notExportedVar); + assertEquals(" (pkg.mod)", varPresentation.getTailText()); + } + private void doTest() { LookupElement[] lookupElements = doExtendedCompletion(); if (lookupElements != null) {