PY-72661 Literal completion inside nested parenthesis

GitOrigin-RevId: 14f18efcbaa520ac36ddd4892b774ed60a01cfdc
This commit is contained in:
Petr
2024-06-17 20:36:23 +00:00
committed by intellij-monorepo-bot
parent 3d3aefb69d
commit ee91d6a211
8 changed files with 61 additions and 21 deletions
@@ -53,7 +53,7 @@ private class DictLiteralCompletionProvider : CompletionProvider<CompletionParam
val typeEvalContext = TypeEvalContext.codeCompletion(originalElement.project, originalElement.containingFile)
val actualType = typeEvalContext.getType(possibleSequenceExpr)
val quote = getForcedQuote(possibleSequenceExpr, originalElement)
PyCallExpressionHelper.getMappedParameters(possibleSequenceExpr, PyResolveContext.defaultContext(typeEvalContext)).forEach {
PyCallExpressionHelper.getMappedParameters(possibleSequenceExpr, PyResolveContext.defaultContext(typeEvalContext))?.forEach {
addCompletionForTypedDictKeys(it.getType(typeEvalContext), actualType, result, quote)
}
}
@@ -4,7 +4,6 @@ import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.completion.ml.MLRankingIgnorable
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.patterns.StandardPatterns.or
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.ui.IconManager
@@ -12,6 +11,7 @@ import com.intellij.ui.PlatformIcons
import com.intellij.util.ProcessingContext
import com.jetbrains.python.psi.*
import com.jetbrains.python.psi.impl.PyCallExpressionHelper
import com.jetbrains.python.psi.impl.PyPsiUtils
import com.jetbrains.python.psi.resolve.PyResolveContext
import com.jetbrains.python.psi.types.PyLiteralType
import com.jetbrains.python.psi.types.PyType
@@ -20,16 +20,7 @@ import com.jetbrains.python.psi.types.TypeEvalContext
class PyLiteralTypeCompletionContributor : CompletionContributor() {
init {
extend(
CompletionType.BASIC,
or(
psiElement().withSuperParent(2, PyKeywordArgument::class.java),
psiElement().withSuperParent(2, PyArgumentList::class.java),
psiElement().withSuperParent(2, PySubscriptionExpression::class.java),
psiElement().inside(PyAssignmentStatement::class.java),
),
PyLiteralTypeCompletionProvider()
)
extend(CompletionType.BASIC, psiElement(), PyLiteralTypeCompletionProvider())
}
}
@@ -38,11 +29,9 @@ private class PyLiteralTypeCompletionProvider : CompletionProvider<CompletionPar
val position = parameters.position.parent as? PyExpression ?: return
val typeEvalContext = TypeEvalContext.codeCompletion(position.project, position.containingFile)
val callSiteExpr = PsiTreeUtil.getParentOfType(position, PyCallSiteExpression::class.java)
if (callSiteExpr != null) {
val types = PyCallExpressionHelper
.getMappedParameters(position, PyResolveContext.defaultContext(typeEvalContext))
.mapNotNull { it.getArgumentType(typeEvalContext) }
val mappedParameters = PyCallExpressionHelper.getMappedParameters(position, PyResolveContext.defaultContext(typeEvalContext))
if (mappedParameters != null) {
val types = mappedParameters.mapNotNull { it.getArgumentType(typeEvalContext) }
addToResult(position, types, result)
return
}
@@ -51,11 +40,12 @@ private class PyLiteralTypeCompletionProvider : CompletionProvider<CompletionPar
PyParenthesizedExpression::class.java,
PyTupleExpression::class.java) as? PyAssignmentStatement
if (assignmentStatement != null) {
val mapping = assignmentStatement.targetsToValuesMapping.find { it.second === position }
val mapping = assignmentStatement.targetsToValuesMapping.find { PyPsiUtils.flattenParens(it.second) === position }
if (mapping != null) {
val type = typeEvalContext.getType(mapping.first)
addToResult(position, listOfNotNull(type), result)
}
return
}
}
@@ -755,14 +755,15 @@ public final class PyCallExpressionHelper {
* {@code argument} can be (parenthesized) expression or a value of a {@link PyKeywordArgument}
*/
@ApiStatus.Internal
@NotNull
@Nullable
public static List<PyCallableParameter> getMappedParameters(@NotNull PyExpression argument,
@NotNull PyResolveContext resolveContext) {
while (argument.getParent() instanceof PyParenthesizedExpression parenthesizedExpr) {
argument = parenthesizedExpr;
}
if (argument.getParent() instanceof PyKeywordArgument keywordArgument && keywordArgument.getValueExpression() == argument) {
if (argument.getParent() instanceof PyKeywordArgument keywordArgument) {
assert keywordArgument.getValueExpression() == argument;
argument = keywordArgument;
}
@@ -771,7 +772,7 @@ public final class PyCallExpressionHelper {
parent = parent.getParent();
}
if (!(parent instanceof PyCallSiteExpression callSite)) {
return Collections.emptyList();
return null;
}
PyExpression finalArgument = argument;
@@ -0,0 +1,5 @@
from typing import Literal
item: Literal[Literal[Literal["0", 2], "acc"], "5", None]
item=(((<caret>)))
@@ -0,0 +1,8 @@
from typing import Literal
def f(x, y: Literal[Literal["abb"], "bac"]):
pass
f(1, ((("<caret>"))))
@@ -0,0 +1,8 @@
from typing import Literal
def f(x: Literal[Literal[Literal[1, "-1"], "foo"], "6", None]) -> None:
pass
f(x=((((<caret>)))))
@@ -0,0 +1,8 @@
from typing import Literal
class A:
def __getitem__(self, item: Literal[Literal[Literal["1", "2"], "foo"], "5", None]) -> str:
pass
A()[(((("<caret>"))))]
@@ -11,18 +11,38 @@ class PyLiteralTypeCompletionTest : PyTestCase() {
doTestCompletionVariantsContains("inCallExpression.py", "\"1\"", "\"2\"", "\"foo\"", "\"5\"")
}
// PY-72661
fun testNestedParenthesisInCallExpression() {
myFixture.testCompletionVariants("nestedParenthesisInCallExpression.py", "abb", "bac")
}
fun testInKeywordArgument() {
doTestCompletionVariantsContains("inKeywordArgument.py", "\"3\"", "\"foo\"", "\"5\"")
}
// PY-72661
fun testNestedParenthesisInKeywordArgumentValue() {
doTestCompletionVariantsContains("nestedParenthesisInKeywordArgumentValue.py", "\"-1\"", "\"foo\"", "\"6\"")
}
fun testInSubscriptionExpression() {
doTestCompletionVariantsContains("inSubscriptionExpression.py", "\"1\"", "\"foo\"", "\"5\"")
}
// PY-72661
fun testNestedParenthesisInSubscriptionExpression() {
myFixture.testCompletionVariants("nestedParenthesisInSubscriptionExpression.py", "1", "2", "foo", "5")
}
fun testInAssigment() {
doTestCompletionVariantsContains("inAssignment.py", "\"1\"", "\"3\"", "\"foo\"", "\"5\"")
}
// PY-72661
fun testNestedParenthesisInAssigment() {
doTestCompletionVariantsContains("nestedParenthesisInAssigment.py", "\"0\"", "\"acc\"", "\"5\"")
}
fun testInDoubleQuotedString() {
myFixture.testCompletionVariants("inDoubleQuotedString.py", "bar", "foo")
}