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 super T> 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");
});
}