diff --git a/python/pluginCore/resources/META-INF/plugin.xml b/python/pluginCore/resources/META-INF/plugin.xml index af50428af31f..75c6eab43072 100644 --- a/python/pluginCore/resources/META-INF/plugin.xml +++ b/python/pluginCore/resources/META-INF/plugin.xml @@ -361,6 +361,7 @@ The Python plug-in provides smart editing for Python scripts. The feature set of + { + myFixture.configureByFiles("starImport/starImport.py", "starImport/importSource.py"); + myFixture.completeBasic(); + assertSameElements(myFixture.getLookupElementStrings(), Arrays.asList("my_foo", "my_bar")); + }); } // PY-1211, PY-29232 @@ -1678,10 +1678,12 @@ public abstract class PythonCommonCompletionTest extends PythonCommonTestCase { // PY-8302 public void testBeforeImport() { - myFixture.configureByFiles("beforeImport/beforeImport.py", "beforeImport/source.py"); - myFixture.completeBasic(); - List suggested = myFixture.getLookupElementStrings(); - assertDoesntContain(suggested, "my_foo", "my_bar"); + runWithImportableNamesInBasicCompletionDisabled(() -> { + myFixture.configureByFiles("beforeImport/beforeImport.py", "beforeImport/source.py"); + myFixture.completeBasic(); + List suggested = myFixture.getLookupElementStrings(); + assertDoesntContain(suggested, "my_foo", "my_bar"); + }); } // PY-8302 @@ -1694,10 +1696,12 @@ public abstract class PythonCommonCompletionTest extends PythonCommonTestCase { // PY-8302 public void testBeforeStarImport() { - myFixture.configureByFiles("beforeImport/beforeStarImport.py", "beforeImport/source.py"); - myFixture.completeBasic(); - List suggested = myFixture.getLookupElementStrings(); - assertDoesntContain(suggested, "my_foo", "my_bar"); + runWithImportableNamesInBasicCompletionDisabled(() -> { + myFixture.configureByFiles("beforeImport/beforeStarImport.py", "beforeImport/source.py"); + myFixture.completeBasic(); + List suggested = myFixture.getLookupElementStrings(); + assertDoesntContain(suggested, "my_foo", "my_bar"); + }); } // PY-8302 @@ -2167,6 +2171,101 @@ public abstract class PythonCommonCompletionTest extends PythonCommonTestCase { }); } + // PY-62208 + public void testImportableNamesNotSuggestedImmediatelyInsideClassBody() { + doMultiFileTest(); + } + + // PY-62208 + public void testImportableNamesSuggestedInsideOtherStatementsInsideClassBody() { + doMultiFileTest(); + } + + // PY-62208 + public void testImportableNamesNotSuggestedImmediatelyInsideMatchStatement() { + runWithLanguageLevel(LanguageLevel.getLatest(), () -> { + doMultiFileTest(); + }); + } + + // PY-62208 + public void testImportableFunctionsAndVariablesNotSuggestedInsideTypeHints() { + runWithLanguageLevel(LanguageLevel.getLatest(), () -> { + doMultiFileTest(); + }); + } + + // PY-62208 + public void testImportableFunctionsFromTypingSuggestedInsideTypeHints() { + runWithLanguageLevel(LanguageLevel.getLatest(), () -> { + doMultiFileTest(); + }); + } + + // PY-62208 + public void testImportableVariablesFromTypingSuggestedInsideTypeHints() { + runWithLanguageLevel(LanguageLevel.getLatest(), () -> { + doMultiFileTest(); + }); + } + + // PY-62208 + public void testImportableFunctionsAndVariablesNotSuggestedInsidePatterns() { + runWithLanguageLevel(LanguageLevel.getLatest(), () -> { + myFixture.copyDirectoryToProject(getTestName(true), ""); + myFixture.configureByFile("a.py"); + myFixture.complete(CompletionType.BASIC, 1); + List variants = myFixture.getLookupElementStrings(); + // TODO Use regular doMultiFileTest once PY-73173 is fixed + assertDoesntContain(variants, "unique_var", "unique_func"); + assertContainsElements(variants, "unique_class"); + }); + } + + // PY-62208 + public void testNotReExportedNamesFromPrivateModulesNotSuggested() { + doMultiFileTest(); + } + + // PY-62208 + public void testReExportedNamesFromPrivateModulesAreSuggested() { + doMultiFileTest(); + } + + // PY-62208 + public void testAlreadyImportedNamesNotSuggestedTwice() { + doMultiFileTest(); + } + + // PY-62208 + public void testAlreadyImportedNamesNotSuggestedTwiceInsidePatterns() { + runWithLanguageLevel(LanguageLevel.getLatest(), () -> { + myFixture.copyDirectoryToProject(getTestName(true), ""); + myFixture.configureByFile("a.py"); + myFixture.complete(CompletionType.BASIC, 1); + List variants = myFixture.getLookupElementStrings(); + // TODO Use regular doMultiFileTest once PY-73173 is fixed + assertEquals(1, Collections.frequency(variants, "MyClass")); + }); + } + + // PY-62208 + public void testTooCommonImportableNamesNotSuggested() { + doMultiFileTest(); + } + + private static void runWithImportableNamesInBasicCompletionDisabled(@NotNull Runnable action) { + PyCodeInsightSettings settings = PyCodeInsightSettings.getInstance(); + boolean old = settings.INCLUDE_IMPORTABLE_NAMES_IN_BASIC_COMPLETION; + settings.INCLUDE_IMPORTABLE_NAMES_IN_BASIC_COMPLETION = false; + try { + action.run(); + } + finally { + settings.INCLUDE_IMPORTABLE_NAMES_IN_BASIC_COMPLETION = old; + } + } + @Override protected @NotNull String getTestDataPath() { return super.getTestDataPath() + "/completion"; 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 8aa014931fe9..b8c94f24c77f 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,135 +7,234 @@ 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.codeInsight.lookup.LookupElementPresentation; +import com.intellij.codeInsight.lookup.LookupElementRenderer; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Conditions; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiNamedElement; +import com.intellij.psi.PsiReference; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.stubs.StubIndex; -import com.intellij.psi.stubs.StubIndexKey; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; +import com.intellij.util.TimeoutUtil; import com.intellij.util.containers.ContainerUtil; -import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; 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; -import com.jetbrains.python.psi.stubs.PyVariableNameIndex; +import com.jetbrains.python.psi.stubs.PyExportedModuleAttributeIndex; +import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.pyi.PyiFileType; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; -import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Set; -import java.util.function.Function; + +import static com.jetbrains.python.psi.PyUtil.as; /** * Adds completion variants for Python classes, functions and variables. */ -public final class PyClassNameCompletionContributor extends PyExtendedCompletionContributor { +public final class PyClassNameCompletionContributor extends PyImportableNameCompletionContributor { + // See https://plugins.jetbrains.com/plugin/18465-sputnik + private static final boolean TRACING_WITH_SPUTNIK_ENABLED = false; + private static final Logger LOG = Logger.getInstance(PyClassNameCompletionContributor.class); + private static final Set TOO_COMMON_NAMES = Set.of("main", "test"); + + public PyClassNameCompletionContributor() { + if (TRACING_WITH_SPUTNIK_ENABLED) { + //noinspection UseOfSystemOutOrSystemErr + System.out.println("\01hr('Importable names completion')"); + } + } @Override protected void doFillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) { - final PsiFile originalFile = parameters.getOriginalFile(); - final PsiElement element = parameters.getPosition(); - final PsiElement parent = element.getParent(); - final ScopeOwner originalScope = ScopeUtil.getScopeOwner(parameters.getOriginalPosition()); - final Condition fromAnotherScope = e -> ScopeUtil.getScopeOwner(e) != originalScope; + if (!PyCodeInsightSettings.getInstance().INCLUDE_IMPORTABLE_NAMES_IN_BASIC_COMPLETION && !parameters.isExtendedCompletion()) { + return; + } + PsiFile originalFile = parameters.getOriginalFile(); + PsiElement position = parameters.getPosition(); + PyReferenceExpression refExpr = as(position.getParent(), PyReferenceExpression.class); + PyTargetExpression targetExpr = as(position.getParent(), PyTargetExpression.class); + boolean insideUnqualifiedReference = refExpr != null && !refExpr.isQualified(); + boolean insidePattern = targetExpr != null && position.getParent().getParent() instanceof PyCapturePattern; + boolean insideStringLiteralInExtendedCompletion = position instanceof PyStringElement && parameters.isExtendedCompletion(); + if (!(insideUnqualifiedReference || insidePattern || insideStringLiteralInExtendedCompletion)) { + return; + } - addVariantsFromIndex(result, - originalFile, - PyClassNameIndex.KEY, - parent instanceof PyStringLiteralExpression ? getStringLiteralInsertHandler() : getImportingInsertHandler(), - // TODO: implement autocompletion for inner classes - Conditions.and(fromAnotherScope, PyUtil::isTopLevel), - PyClass.class, - createClassElementHandler(originalFile)); + // Directly inside the class body scope, it's rarely needed to have expression statements + // TODO apply the same logic for completion of importable module and package names + if (refExpr != null && + (isDirectlyInsideClassBody(refExpr) || isInsideErrorElement(refExpr))) { + return; + } + // TODO Use another method to collect already visible names + // Candidates: PyExtractMethodValidator, IntroduceValidator.isDefinedInScope + PsiReference refUnderCaret = refExpr != null ? refExpr.getReference() : + targetExpr != null ? targetExpr.getReference() : + null; + Set namesInScope = refUnderCaret == null ? Collections.emptySet() : StreamEx.of(refUnderCaret.getVariants()) + .select(LookupElement.class) + .map(LookupElement::getLookupString) + .toSet(); + Project project = originalFile.getProject(); + TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(project, originalFile); + int maxVariants = Registry.intValue("ide.completion.variant.limit"); + Counters counters = new Counters(); + StubIndex stubIndex = StubIndex.getInstance(); + TimeoutUtil.run(() -> { + GlobalSearchScope scope = createScope(originalFile); + Set alreadySuggested = new HashSet<>(); + StubIndex.getInstance().processAllKeys(PyExportedModuleAttributeIndex.KEY, elementName -> { + ProgressManager.checkCanceled(); + counters.scannedNames++; + if (TOO_COMMON_NAMES.contains(elementName)) return true; + if (!result.getPrefixMatcher().isStartMatch(elementName)) return true; + return stubIndex.processElements(PyExportedModuleAttributeIndex.KEY, elementName, project, scope, PyElement.class, exported -> { + ProgressManager.checkCanceled(); + String name = exported.getName(); + if (name == null || namesInScope.contains(name)) return true; + QualifiedName fqn = getFullyQualifiedName(exported); + if (!isApplicableInInsertionContext(exported, fqn, position, typeEvalContext)) { + counters.notApplicableInContext++; + return true; + } + if (alreadySuggested.add(fqn)) { + if (isPrivateDefinition(fqn, exported, originalFile)) { + counters.privateNames++; + return true; + } + LookupElementBuilder lookupElement = LookupElementBuilder + .createWithSmartPointer(name, exported) + .withIcon(exported.getIcon(0)) + .withExpensiveRenderer(new LookupElementRenderer<>() { + @Override + public void renderElement(LookupElement element, LookupElementPresentation presentation) { + presentation.setItemText(element.getLookupString()); + presentation.setIcon(exported.getIcon(0)); + QualifiedName importPath = QualifiedNameFinder.findCanonicalImportPath(exported, originalFile); + if (importPath == null) return; + presentation.setTypeText(importPath.toString()); + } + }) + .withInsertHandler(getInsertHandler(exported, position)); + result.addElement(PrioritizedLookupElement.withPriority(lookupElement, PythonCompletionWeigher.NOT_IMPORTED_MODULE_WEIGHT)); + counters.totalVariants++; + if (counters.totalVariants >= maxVariants) return false; + } + return true; + }); + }, scope); + }, duration -> { + LOG.debug(counters + " computed in " + duration + " ms"); + if (TRACING_WITH_SPUTNIK_ENABLED) { + //noinspection UseOfSystemOutOrSystemErr + System.out.println("\1h('Importable names completion','%d')".formatted((duration / 10) * 10)); + } + }); + } - addVariantsFromIndex(result, - originalFile, - PyFunctionNameIndex.KEY, - getFunctionInsertHandler(parent), - Conditions.and(fromAnotherScope, PyUtil::isTopLevel), - PyFunction.class, - Function.identity()); + private static boolean isApplicableInInsertionContext(@NotNull PyElement definition, + @NotNull QualifiedName fqn, @NotNull PsiElement position, + @NotNull TypeEvalContext context) { + if (PyTypingTypeProvider.isInsideTypeHint(position, context)) { + // Not all names from typing.py are defined as classes + return definition instanceof PyClass || ArrayUtil.contains(fqn.getFirstComponent(), "typing", "typing_extensions"); + } + if (PsiTreeUtil.getParentOfType(position, PyPattern.class, false) != null) { + return definition instanceof PyClass; + } + return true; + } - addVariantsFromIndex(result, - originalFile, - PyVariableNameIndex.KEY, - parent instanceof PyStringLiteralExpression ? getStringLiteralInsertHandler() : getImportingInsertHandler(), - Conditions.and(fromAnotherScope, PyUtil::isTopLevel), - PyTargetExpression.class, - Function.identity()); + private static boolean isInsideErrorElement(@NotNull PyReferenceExpression referenceExpression) { + return PsiTreeUtil.getParentOfType(referenceExpression, PsiErrorElement.class) != null; + } + + private static boolean isDirectlyInsideClassBody(@NotNull PyReferenceExpression referenceExpression) { + return referenceExpression.getParent() instanceof PyExpressionStatement statement && + ScopeUtil.getScopeOwner(statement) instanceof PyClass; + } + + private static @NotNull QualifiedName getFullyQualifiedName(@NotNull PyElement exported) { + String shortName = StringUtil.notNullize(exported.getName()); + String qualifiedName = exported instanceof PyQualifiedNameOwner qNameOwner ? qNameOwner.getQualifiedName() : null; + return QualifiedName.fromDottedString(qualifiedName != null ? qualifiedName : shortName); + } + + private static boolean isPrivateDefinition(@NotNull QualifiedName fqn, @NotNull PyElement exported, PsiFile originalFile) { + if (containsPrivateComponents(fqn)) { + QualifiedName importPath = QualifiedNameFinder.findCanonicalImportPath(exported, originalFile); + return importPath != null && containsPrivateComponents(importPath); + } + return false; + } + + private static boolean containsPrivateComponents(@NotNull QualifiedName fqn) { + return ContainerUtil.exists(fqn.getComponents(), c -> c.startsWith("_")); } @NotNull - private static Function createClassElementHandler(@NotNull PsiFile file) { - final PyFile pyFile = PyUtil.as(file, PyFile.class); - if (pyFile == null) return Function.identity(); + private static GlobalSearchScope createScope(@NotNull PsiFile originalFile) { + class HavingLegalImportPathScope extends QualifiedNameFinder.QualifiedNameBasedScope { + private HavingLegalImportPathScope(@NotNull Project project) { + super(project); + } - final Set sourceQNames = - ContainerUtil.map2SetNotNull(pyFile.getFromImports(), PyFromImportStatement::getImportSourceQName); + @Override + protected boolean containsQualifiedNameInRoot(@NotNull VirtualFile root, @NotNull QualifiedName qName) { + return ContainerUtil.all(qName.getComponents(), PyNames::isIdentifier) && !qName.equals(QualifiedName.fromComponents("__future__")); + } + } - return le -> { - final PyClass cls = PyUtil.as(le.getPsiElement(), PyClass.class); - if (cls == null) return le; - - final String clsQName = cls.getQualifiedName(); - if (clsQName == null) return le; - - if (!sourceQNames.contains(QualifiedName.fromDottedString(clsQName).removeLastComponent())) return le; - - return PrioritizedLookupElement.withPriority(le, PythonCompletionWeigher.PRIORITY_WEIGHT); - }; + Project project = originalFile.getProject(); + var pyiStubsScope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.everythingScope(project), PyiFileType.INSTANCE); + return PySearchUtilBase.defaultSuggestionScope(originalFile) + .intersectWith(GlobalSearchScope.notScope(pyiStubsScope)) + .intersectWith(GlobalSearchScope.notScope(GlobalSearchScope.fileScope(originalFile))) + .intersectWith(new HavingLegalImportPathScope(project)); } - private InsertHandler getFunctionInsertHandler(PsiElement parent) { - if (parent instanceof PyStringLiteralExpression) { + private @NotNull InsertHandler getInsertHandler(@NotNull PyElement exported, + @NotNull PsiElement position) { + if (position.getParent() instanceof PyStringLiteralExpression) { return getStringLiteralInsertHandler(); } - if (parent.getParent() instanceof PyDecorator) { - return getImportingInsertHandler(); + else if (exported instanceof PyFunction && !(position.getParent().getParent() instanceof PyDecorator)) { + return getFunctionInsertHandler(); } - return getFunctionInsertHandler(); + return getImportingInsertHandler(); } - private static void addVariantsFromIndex(@NotNull CompletionResultSet resultSet, - @NotNull PsiFile targetFile, - @NotNull StubIndexKey indexKey, - @NotNull InsertHandler insertHandler, - @NotNull Condition condition, - @NotNull Class elementClass, - @NotNull Function elementHandler) { - final Project project = targetFile.getProject(); - final GlobalSearchScope scope = PySearchUtilBase.defaultSuggestionScope(targetFile); - final Set alreadySuggested = new HashSet<>(); + private static class Counters { + int scannedNames; + int privateNames; + int totalVariants; + int notApplicableInContext; - StubIndex stubIndex = StubIndex.getInstance(); - final Collection allKeys = stubIndex.getAllKeys(indexKey, project); - for (String elementName : resultSet.getPrefixMatcher().sortMatching(allKeys)) { - stubIndex.processElements(indexKey, elementName, project, scope, elementClass, (element) -> { - ProgressManager.checkCanceled(); - if (!condition.value(element)) return true; - String name = element.getName(); - 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(" (" + importPath + ")", true) - .withInsertHandler(insertHandler); - resultSet.addElement(elementHandler.apply(lookupElement)); - } - return true; - }); + @Override + public String toString() { + return "Counters{" + + "scannedNames=" + scannedNames + + ", privateNames=" + privateNames + + ", totalVariants=" + totalVariants + + ", notApplicableInContext=" + notApplicableInContext + + '}'; } } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyExtendedCompletionContributor.kt b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyImportableNameCompletionContributor.kt similarity index 90% rename from python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyExtendedCompletionContributor.kt rename to python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyImportableNameCompletionContributor.kt index 19e1f8178cd7..13bd9863154f 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyExtendedCompletionContributor.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyImportableNameCompletionContributor.kt @@ -14,13 +14,9 @@ import com.jetbrains.python.psi.* import com.jetbrains.python.psi.resolve.QualifiedNameFinder /** - * Provides basic functionality for extended completion. - * - * Extended code completion is actually a basic code completion that shows the names of classes, functions, modules and variables. - * - * To provide variants for extended completion override [doFillCompletionVariants] + * Provides basic functionality for providing completion variants that should add an import statement or be expanded into a qualified name. */ -abstract class PyExtendedCompletionContributor : CompletionContributor(), DumbAware { +abstract class PyImportableNameCompletionContributor : CompletionContributor(), DumbAware { protected val importingInsertHandler: InsertHandler = InsertHandler { context, item -> addImportForLookupElement(context, item, context.tailOffset - 1) @@ -70,10 +66,6 @@ abstract class PyExtendedCompletionContributor : CompletionContributor(), DumbAw protected abstract fun doFillCompletionVariants(parameters: CompletionParameters, result: CompletionResultSet) private fun shouldDoCompletion(parameters: CompletionParameters, result: CompletionResultSet): Boolean { - if (!parameters.isExtendedCompletion) { - return false - } - if (result.prefixMatcher.prefix.isEmpty()) { result.restartCompletionOnPrefixChange(StandardPatterns.string().longerThan(0)) return false diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyModulePackageCompletionContributor.kt b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyModulePackageCompletionContributor.kt index 997c589e8116..2e0648322f4d 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyModulePackageCompletionContributor.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyModulePackageCompletionContributor.kt @@ -3,6 +3,7 @@ package com.jetbrains.python.codeInsight.completion import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.codeInsight.completion.CompletionResultSet +import com.intellij.codeInsight.completion.PrioritizedLookupElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiFileSystemItem import com.jetbrains.python.psi.PyStringLiteralExpression @@ -20,9 +21,12 @@ import com.jetbrains.python.psi.stubs.PyModuleNameIndex * The completion contributor ensures that completion variants are resolvable with project source root configuration. * The list of completion variants does not include namespace packages (but includes their modules where appropriate). */ -class PyModulePackageCompletionContributor : PyExtendedCompletionContributor() { +class PyModulePackageCompletionContributor : PyImportableNameCompletionContributor() { override fun doFillCompletionVariants(parameters: CompletionParameters, result: CompletionResultSet) { + if (!parameters.isExtendedCompletion) { + return + } val targetFile = parameters.originalFile val inStringLiteral = parameters.position.parent is PyStringLiteralExpression @@ -34,15 +38,13 @@ class PyModulePackageCompletionContributor : PyExtendedCompletionContributor() { .toList() val resolveContext = fromFoothold(targetFile) - val builders = modulesFromIndex.asSequence() + modulesFromIndex.asSequence() .flatMap { resolve(it, resolveContext) } .filter { PyUtil.isImportable(targetFile, it) } .mapNotNull { createLookupElementBuilder(targetFile, it) } - .map { it.withInsertHandler( - if (inStringLiteral) stringLiteralInsertHandler else importingInsertHandler) - } - - builders.forEach { result.addElement(it) } + .map { it.withInsertHandler(if (inStringLiteral) stringLiteralInsertHandler else importingInsertHandler) } + .map { PrioritizedLookupElement.withPriority(it, PythonCompletionWeigher.NOT_IMPORTED_MODULE_WEIGHT.toDouble()) } + .forEach { result.addElement(it) } } private fun resolve(module: PsiFile, resolveContext: PyQualifiedNameResolveContext): Sequence { diff --git a/python/python-syntax-core/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java b/python/python-syntax-core/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java index ba14e8eee36f..241293caa7a5 100644 --- a/python/python-syntax-core/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java +++ b/python/python-syntax-core/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java @@ -35,6 +35,7 @@ public class PyCodeInsightSettings implements PersistentStateComponent \ No newline at end of file diff --git a/python/testData/completion/alreadyImportedNamesNotSuggestedTwice/mod.py b/python/testData/completion/alreadyImportedNamesNotSuggestedTwice/mod.py new file mode 100644 index 000000000000..1dea43b79188 --- /dev/null +++ b/python/testData/completion/alreadyImportedNamesNotSuggestedTwice/mod.py @@ -0,0 +1,2 @@ +class MyClass: + pass \ No newline at end of file diff --git a/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/a.after.py b/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/a.after.py new file mode 100644 index 000000000000..7b87bdaf4692 --- /dev/null +++ b/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/a.after.py @@ -0,0 +1,5 @@ +from mod import MyClass + +def f(p): + match p: + case MyClass \ No newline at end of file diff --git a/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/a.py b/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/a.py new file mode 100644 index 000000000000..94993b77df3b --- /dev/null +++ b/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/a.py @@ -0,0 +1,5 @@ +from mod import MyClass + +def f(p): + match p: + case MyCla \ No newline at end of file diff --git a/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/mod.py b/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/mod.py new file mode 100644 index 000000000000..1dea43b79188 --- /dev/null +++ b/python/testData/completion/alreadyImportedNamesNotSuggestedTwiceInsidePatterns/mod.py @@ -0,0 +1,2 @@ +class MyClass: + pass \ No newline at end of file diff --git a/python/testData/completion/className/combinedOrdering/combinedOrdering.py b/python/testData/completion/className/combinedOrdering/combinedOrdering.py index 70c0ce02fd1e..7cbae18c1e62 100644 --- a/python/testData/completion/className/combinedOrdering/combinedOrdering.py +++ b/python/testData/completion/className/combinedOrdering/combinedOrdering.py @@ -1,3 +1,3 @@ -path = "something" +path1 = "something" pat \ No newline at end of file diff --git a/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.py b/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.py index b10a5e482aa4..ae1216832797 100644 --- a/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.py +++ b/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.py @@ -1,2 +1,2 @@ -def my_func(*args, **kwargs): +def func(*args, **kwargs): pass diff --git a/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.pyi b/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.pyi index 97084e33b930..f2a3d9077985 100644 --- a/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.pyi +++ b/python/testData/completion/className/noDuplicatesForStubsAndOverloads/mod.pyi @@ -2,10 +2,10 @@ from typing import overload @overload -def my_func(p: int): +def func(p: int): pass @overload -def my_func(p1: str, p2: int): +def func(p1: str, p2: int): pass diff --git a/python/testData/completion/className/orderingUnderscoreInName/a/__init__.py b/python/testData/completion/className/orderingUnderscoreInName/a/__init__.py deleted file mode 100644 index 3c2a9c4d90a5..000000000000 --- a/python/testData/completion/className/orderingUnderscoreInName/a/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def __foo__(): - return "private" \ No newline at end of file diff --git a/python/testData/completion/className/orderingUnderscoreInName/b/__init__.py b/python/testData/completion/className/orderingUnderscoreInName/b/__init__.py deleted file mode 100644 index b3abe6b05fa2..000000000000 --- a/python/testData/completion/className/orderingUnderscoreInName/b/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def _foo(): - return "private" \ No newline at end of file diff --git a/python/testData/completion/className/orderingUnderscoreInName/c/__init__.py b/python/testData/completion/className/orderingUnderscoreInName/c/__init__.py deleted file mode 100644 index 67ad6b9eb1ed..000000000000 --- a/python/testData/completion/className/orderingUnderscoreInName/c/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def foo(): - return "public" \ No newline at end of file diff --git a/python/testData/completion/className/orderingUnderscoreInName/orderingUnderscoreInName.py b/python/testData/completion/className/orderingUnderscoreInName/orderingUnderscoreInName.py deleted file mode 100644 index 370908487427..000000000000 --- a/python/testData/completion/className/orderingUnderscoreInName/orderingUnderscoreInName.py +++ /dev/null @@ -1 +0,0 @@ -foo \ No newline at end of file diff --git a/python/testData/completion/className/orderingUnderscoreInPath/_a/__init__.py b/python/testData/completion/className/orderingUnderscoreInPath/_a/__init__.py deleted file mode 100644 index 2eee95c09391..000000000000 --- a/python/testData/completion/className/orderingUnderscoreInPath/_a/__init__.py +++ /dev/null @@ -1 +0,0 @@ -foo = "private" \ No newline at end of file diff --git a/python/testData/completion/className/orderingUnderscoreInPath/b/__init__.py b/python/testData/completion/className/orderingUnderscoreInPath/b/__init__.py deleted file mode 100644 index fdb01c315d8c..000000000000 --- a/python/testData/completion/className/orderingUnderscoreInPath/b/__init__.py +++ /dev/null @@ -1 +0,0 @@ -foo = "non-private" \ No newline at end of file diff --git a/python/testData/completion/className/orderingUnderscoreInPath/orderingUnderscoreInPath.py b/python/testData/completion/className/orderingUnderscoreInPath/orderingUnderscoreInPath.py deleted file mode 100644 index 370908487427..000000000000 --- a/python/testData/completion/className/orderingUnderscoreInPath/orderingUnderscoreInPath.py +++ /dev/null @@ -1 +0,0 @@ -foo \ No newline at end of file diff --git a/python/testData/completion/className/reformatUpdatedFromImport/module.py b/python/testData/completion/className/reformatUpdatedFromImport/module.py index aa34879b43d1..e644c1507252 100644 --- a/python/testData/completion/className/reformatUpdatedFromImport/module.py +++ b/python/testData/completion/className/reformatUpdatedFromImport/module.py @@ -2,5 +2,5 @@ class Foo: pass -class Bar: +class UniqueBar: pass diff --git a/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.after.py b/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.after.py index 3a3167de79a9..085d7e931b8d 100644 --- a/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.after.py +++ b/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.after.py @@ -1,6 +1,6 @@ from module import ( Foo, - Bar, + UniqueBar, ) -print(Foo(), Bar) +print(Foo(), UniqueBar) diff --git a/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.py b/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.py index 3e864d239db4..4bd1935fdfc8 100644 --- a/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.py +++ b/python/testData/completion/className/reformatUpdatedFromImport/reformatUpdatedFromImport.py @@ -1,3 +1,3 @@ from module import Foo -print(Foo(), Ba) +print(Foo(), UniqueBa) diff --git a/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/site-packages/mypkg/mod.py b/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/site-packages/mypkg/mod.py index b9bfa6f12339..60e433b5e658 100644 --- a/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/site-packages/mypkg/mod.py +++ b/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/site-packages/mypkg/mod.py @@ -1,2 +1,2 @@ -def func(): +def test_func(): pass diff --git a/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/main.py b/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/main.py index de908dbaf4da..c4a5ffb7e72f 100644 --- a/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/main.py +++ b/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/main.py @@ -1 +1 @@ -fun +test_fun diff --git a/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/mod.py b/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/mod.py index b9bfa6f12339..60e433b5e658 100644 --- a/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/mod.py +++ b/python/testData/completion/className/thirdPartyPackageTestsNotSuggested/src/mod.py @@ -1,2 +1,2 @@ -def func(): +def test_func(): pass diff --git a/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/a.after.py b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/a.after.py new file mode 100644 index 000000000000..1f5533bad545 --- /dev/null +++ b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/a.after.py @@ -0,0 +1,5 @@ +from mod import unique_class + + +match "foo": + case unique_classfd \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/a.py b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/a.py new file mode 100644 index 000000000000..fa5f83ae5dd5 --- /dev/null +++ b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/a.py @@ -0,0 +1,2 @@ +match "foo": + case unique_ \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/mod.py b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/mod.py new file mode 100644 index 000000000000..2e48c3fce569 --- /dev/null +++ b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsidePatterns/mod.py @@ -0,0 +1,7 @@ +def unique_func(): + pass + +unique_var = 42 + +class unique_class: + pass \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/a.after.py b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/a.after.py new file mode 100644 index 000000000000..5827f00dc398 --- /dev/null +++ b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/a.after.py @@ -0,0 +1,3 @@ +from mod import unique_class + +x: unique_class \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/a.py b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/a.py new file mode 100644 index 000000000000..2f51913aff8a --- /dev/null +++ b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/a.py @@ -0,0 +1 @@ +x: unique_ \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/mod.py b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/mod.py new file mode 100644 index 000000000000..2e48c3fce569 --- /dev/null +++ b/python/testData/completion/importableFunctionsAndVariablesNotSuggestedInsideTypeHints/mod.py @@ -0,0 +1,7 @@ +def unique_func(): + pass + +unique_var = 42 + +class unique_class: + pass \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/a.after.py b/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/a.after.py new file mode 100644 index 000000000000..a6e65fa15ab1 --- /dev/null +++ b/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/a.after.py @@ -0,0 +1,5 @@ +from typing import Final + + +class C: + attr: Final() \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/a.py b/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/a.py new file mode 100644 index 000000000000..ead3c7071c0d --- /dev/null +++ b/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/a.py @@ -0,0 +1,2 @@ +class C: + attr: Fina \ No newline at end of file diff --git a/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/typing.py b/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/typing.py new file mode 100644 index 000000000000..dd9baa68e1bd --- /dev/null +++ b/python/testData/completion/importableFunctionsFromTypingSuggestedInsideTypeHints/typing.py @@ -0,0 +1,22 @@ + +@_SpecialForm +def Final(self, parameters): + """Special typing construct to indicate final names to type checkers. + + A final name cannot be re-assigned or overridden in a subclass. + + For example:: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties. + """ + item = _type_check(parameters, f'{self} accepts only single type.') + return _GenericAlias(self, (item,)) \ No newline at end of file diff --git a/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/a.after.py b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/a.after.py new file mode 100644 index 000000000000..5734d5f92d4d --- /dev/null +++ b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/a.after.py @@ -0,0 +1,2 @@ +class C: + unique_ \ No newline at end of file diff --git a/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/a.py b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/a.py new file mode 100644 index 000000000000..5734d5f92d4d --- /dev/null +++ b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/a.py @@ -0,0 +1,2 @@ +class C: + unique_ \ No newline at end of file diff --git a/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/mod.py b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/mod.py new file mode 100644 index 000000000000..92eb3873cfd4 --- /dev/null +++ b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideClassBody/mod.py @@ -0,0 +1 @@ +unique_var = 42 \ No newline at end of file diff --git a/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/a.after.py b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/a.after.py new file mode 100644 index 000000000000..deedde108dc7 --- /dev/null +++ b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/a.after.py @@ -0,0 +1,2 @@ +match "foo": + case \ No newline at end of file diff --git a/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/a.py b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/a.py new file mode 100644 index 000000000000..bfd42737ab44 --- /dev/null +++ b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/a.py @@ -0,0 +1,2 @@ +match "foo": + cas \ No newline at end of file diff --git a/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/mod.py b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/mod.py new file mode 100644 index 000000000000..2391b1fabd8b --- /dev/null +++ b/python/testData/completion/importableNamesNotSuggestedImmediatelyInsideMatchStatement/mod.py @@ -0,0 +1,2 @@ +def case_fold(s): + ... \ No newline at end of file diff --git a/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/a.after.py b/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/a.after.py new file mode 100644 index 000000000000..1ec41feb9857 --- /dev/null +++ b/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/a.after.py @@ -0,0 +1,5 @@ +from mod import unique_var + + +class C: + attr = unique_var \ No newline at end of file diff --git a/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/a.py b/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/a.py new file mode 100644 index 000000000000..811a836c10b5 --- /dev/null +++ b/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/a.py @@ -0,0 +1,2 @@ +class C: + attr = unique_ \ No newline at end of file diff --git a/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/mod.py b/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/mod.py new file mode 100644 index 000000000000..92eb3873cfd4 --- /dev/null +++ b/python/testData/completion/importableNamesSuggestedInsideOtherStatementsInsideClassBody/mod.py @@ -0,0 +1 @@ +unique_var = 42 \ No newline at end of file diff --git a/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/a.after.py b/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/a.after.py new file mode 100644 index 000000000000..68b521be6810 --- /dev/null +++ b/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/a.after.py @@ -0,0 +1,3 @@ +from typing import Tuple + +attr: Tuple \ No newline at end of file diff --git a/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/a.py b/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/a.py new file mode 100644 index 000000000000..a366c1d52850 --- /dev/null +++ b/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/a.py @@ -0,0 +1 @@ +attr: Tup \ No newline at end of file diff --git a/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/typing.py b/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/typing.py new file mode 100644 index 000000000000..9f69c1b70867 --- /dev/null +++ b/python/testData/completion/importableVariablesFromTypingSuggestedInsideTypeHints/typing.py @@ -0,0 +1,12 @@ +Tuple = _TupleType(tuple, -1, inst=False, name='Tuple') +Tuple.__doc__ = \ + """Deprecated alias to builtins.tuple. + + Tuple[X, Y] is the cross-product type of X and Y. + + Example: Tuple[T1, T2] is a tuple of two elements corresponding + to type variables T1 and T2. Tuple[int, float, str] is a tuple + of an int, a float and a string. + + To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. + """ \ No newline at end of file diff --git a/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/a.after.py b/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/a.after.py new file mode 100644 index 000000000000..1ca5315b399c --- /dev/null +++ b/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/a.after.py @@ -0,0 +1 @@ +unique_ \ No newline at end of file diff --git a/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/a.py b/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/a.py new file mode 100644 index 000000000000..45102254ea7d --- /dev/null +++ b/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/a.py @@ -0,0 +1 @@ +unique_ \ No newline at end of file diff --git a/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/pkg/__init__.py b/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/pkg/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/pkg/_mod.py b/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/pkg/_mod.py new file mode 100644 index 000000000000..92eb3873cfd4 --- /dev/null +++ b/python/testData/completion/notReExportedNamesFromPrivateModulesNotSuggested/pkg/_mod.py @@ -0,0 +1 @@ +unique_var = 42 \ No newline at end of file diff --git a/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/a.after.py b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/a.after.py new file mode 100644 index 000000000000..01ee641dc9d2 --- /dev/null +++ b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/a.after.py @@ -0,0 +1,3 @@ +from pkg import unique_var + +unique_var \ No newline at end of file diff --git a/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/a.py b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/a.py new file mode 100644 index 000000000000..45102254ea7d --- /dev/null +++ b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/a.py @@ -0,0 +1 @@ +unique_ \ No newline at end of file diff --git a/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/pkg/__init__.py b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/pkg/__init__.py new file mode 100644 index 000000000000..a7eb56eadae8 --- /dev/null +++ b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/pkg/__init__.py @@ -0,0 +1 @@ +from ._mod import unique_var \ No newline at end of file diff --git a/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/pkg/_mod.py b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/pkg/_mod.py new file mode 100644 index 000000000000..92eb3873cfd4 --- /dev/null +++ b/python/testData/completion/reExportedNamesFromPrivateModulesAreSuggested/pkg/_mod.py @@ -0,0 +1 @@ +unique_var = 42 \ No newline at end of file diff --git a/python/testData/completion/tooCommonImportableNamesNotSuggested/a.after.py b/python/testData/completion/tooCommonImportableNamesNotSuggested/a.after.py new file mode 100644 index 000000000000..6fcc24c9280a --- /dev/null +++ b/python/testData/completion/tooCommonImportableNamesNotSuggested/a.after.py @@ -0,0 +1 @@ +mai \ No newline at end of file diff --git a/python/testData/completion/tooCommonImportableNamesNotSuggested/a.py b/python/testData/completion/tooCommonImportableNamesNotSuggested/a.py new file mode 100644 index 000000000000..6fcc24c9280a --- /dev/null +++ b/python/testData/completion/tooCommonImportableNamesNotSuggested/a.py @@ -0,0 +1 @@ +mai \ No newline at end of file diff --git a/python/testData/completion/tooCommonImportableNamesNotSuggested/mod.py b/python/testData/completion/tooCommonImportableNamesNotSuggested/mod.py new file mode 100644 index 000000000000..336e825c89f5 --- /dev/null +++ b/python/testData/completion/tooCommonImportableNamesNotSuggested/mod.py @@ -0,0 +1,2 @@ +def main(): + pass diff --git a/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java b/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java index 364dfff7e848..eb61a5d6d47b 100644 --- a/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java @@ -4,7 +4,6 @@ package com.jetbrains.python; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; @@ -123,11 +122,6 @@ public class PyClassNameCompletionTest extends PyTestCase { ); } - // PY-20976 - public void testOrderingUnderscoreInPath() { - doTestCompletionOrder("b.foo", "_a.foo"); - } - // PY-20976 public void testOrderingSymbolBeforeModule() { doTestCompletionOrder("b.foo", "a.foo"); @@ -153,21 +147,16 @@ public class PyClassNameCompletionTest extends PyTestCase { runWithAdditionalFileInLibDir( "sys.py", "path = 10", - (__) -> doTestCompletionOrder("combinedOrdering.path", "first.foo.path", "sys.path", "_second.bar.path") + (__) -> doTestCompletionOrder("combinedOrdering.path1", "first.foo.path", "sys.path") ); } - // PY-20976 - public void testOrderingUnderscoreInName() { - doTestCompletionOrder("c.foo", "b._foo", "a.__foo__"); - } - // PY-44586 public void testNoDuplicatesForStubsAndOverloads() { doExtendedCompletion(); List allVariants = myFixture.getLookupElementStrings(); assertNotNull(allVariants); - assertEquals(1, Collections.frequency(allVariants, "my_func")); + assertEquals(1, Collections.frequency(allVariants, "func")); } // PY-45541 @@ -176,12 +165,12 @@ public class PyClassNameCompletionTest extends PyTestCase { LookupElement reexportedFunc = ContainerUtil.find(lookupElements, variant -> variant.getLookupString().equals("my_func")); assertNotNull(reexportedFunc); TestLookupElementPresentation funcPresentation = TestLookupElementPresentation.renderReal(reexportedFunc); - assertEquals(" (pkg)", funcPresentation.getTailText()); + assertEquals("pkg", funcPresentation.getTypeText()); LookupElement notExportedVar = ContainerUtil.find(lookupElements, variant -> variant.getLookupString().equals("my_var")); assertNotNull(notExportedVar); TestLookupElementPresentation varPresentation = TestLookupElementPresentation.renderReal(notExportedVar); - assertEquals(" (pkg.mod)", varPresentation.getTailText()); + assertEquals("pkg.mod", varPresentation.getTypeText()); } // PY-45566 @@ -208,7 +197,7 @@ public class PyClassNameCompletionTest extends PyTestCase { assertNotNull(variants); List variantQNames = ContainerUtil.mapNotNull(variants, PyClassNameCompletionTest::getElementQualifiedName); assertDoesntContain(variantQNames, "mypkg.test.test_mod.test_func"); - assertContainsElements(variantQNames, "mod.func", "tests.test_func", "mypkg.mod.func"); + assertContainsElements(variantQNames, "mod.test_func", "tests.test_func", "mypkg.mod.test_func"); }); }