mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
PY-83039 Don't trigger PyLiteralTypeCompletionContributor in cases like x = y.foo<caret>
It should be activated only for the simplest cases when the caret is either inside an immediate string literal or its prefix is an unqualified reference expression. It makes little sense trying to detect if something like the literal string `"y.foobar"` is a possible value for `x`. It's a relatively heavy completion contributor. It starts evaluating the type of `x` flow-sensitively, analyzing all preceding function calls to take into account `NoReturn`. It affects common workflows like typing out `df = pd.` to create a new Pandas dataframe. (cherry picked from commit f17fe2ce86ee100a3480a574c7f57a1bd67ec2d8) IJ-CR-172165 GitOrigin-RevId: 4434488b1d2dae3ab7efd72e775b0c730268e51c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
679566f2c9
commit
4aff3ed154
@@ -18,6 +18,24 @@ import com.jetbrains.python.psi.types.PyType
|
||||
import com.jetbrains.python.psi.types.PyTypeUtil
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
|
||||
/**
|
||||
* Provides literal type variants in the following cases:
|
||||
* ```python
|
||||
* x: Literal["foo", "bar"]
|
||||
* x = <caret>
|
||||
* x = fo<caret>
|
||||
* x = "fo<caret>"
|
||||
* ```
|
||||
*
|
||||
* or
|
||||
*
|
||||
* ```python
|
||||
* def f(x: Literal["foo", "bar"]): ...
|
||||
* f(<caret>)
|
||||
* f(fo<caret>)
|
||||
* f("fo<caret>")
|
||||
* ```
|
||||
*/
|
||||
class PyLiteralTypeCompletionContributor : CompletionContributor() {
|
||||
init {
|
||||
extend(CompletionType.BASIC, psiElement(), PyLiteralTypeCompletionProvider())
|
||||
@@ -27,6 +45,7 @@ class PyLiteralTypeCompletionContributor : CompletionContributor() {
|
||||
private class PyLiteralTypeCompletionProvider : CompletionProvider<CompletionParameters?>() {
|
||||
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
|
||||
val position = parameters.position.parent as? PyExpression ?: return
|
||||
if (!(position is PyStringLiteralExpression || position is PyReferenceExpression && !position.isQualified)) return
|
||||
val typeEvalContext = TypeEvalContext.codeCompletion(position.project, position.containingFile)
|
||||
|
||||
val mappedParameters = position.getMappedParameters(PyResolveContext.defaultContext(typeEvalContext))
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from typing import Literal
|
||||
|
||||
x: Literal["upper", "lower"]
|
||||
y = ""
|
||||
x = y.upp<caret>
|
||||
@@ -0,0 +1,8 @@
|
||||
from typing import Literal
|
||||
|
||||
|
||||
def f(x: Literal["upper", "lower"]):
|
||||
pass
|
||||
|
||||
y = ""
|
||||
f(y.up<caret>)
|
||||
@@ -75,6 +75,16 @@ class PyLiteralTypeCompletionTest : PyTestCase() {
|
||||
myFixture.testCompletionVariants("nestedArgumentLists.py")
|
||||
}
|
||||
|
||||
// PY-83039
|
||||
fun testNoLiteralVariantsOnQualifiedReferenceInAssignmentValue() {
|
||||
doTestCompletionVariantsDoesNotContain("noLiteralVariantsOnQualifiedReferenceInAssignmentValue.py", "\"upper\"")
|
||||
}
|
||||
|
||||
// PY-83039
|
||||
fun testNoLiteralVariantsOnQualifiedReferenceInCallArgument() {
|
||||
doTestCompletionVariantsDoesNotContain("noLiteralVariantsOnQualifiedReferenceInCallArgument.py", "\"upper\"")
|
||||
}
|
||||
|
||||
override fun getTestDataPath(): String {
|
||||
return super.getTestDataPath() + "/completion/literalType"
|
||||
}
|
||||
@@ -84,4 +94,10 @@ class PyLiteralTypeCompletionTest : PyTestCase() {
|
||||
assertNotNull(result)
|
||||
assertContainsElements(result!!, *items)
|
||||
}
|
||||
|
||||
private fun doTestCompletionVariantsDoesNotContain(fileBefore: String, vararg items: String) {
|
||||
val result = myFixture.getCompletionVariants(fileBefore)
|
||||
assertNotNull(result)
|
||||
assertDoesntContain(result!!, *items)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user