diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 27843319b227..59bb14beb3c9 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -280,8 +280,12 @@ INTN.add.parameters.to.docstring=Add parameters to docstring INTN.specify.type.in.annotation=Specify type for reference using annotation INTN.specify.return.type.in.annotation=Specify return type using annotation -#PyAnnotateTypesIntention -INTN.annotate.types=Annotate types +#PyAnnotateTypesIntention/PyAnnotateVariableTypeIntention +INTN.add.type.hints.for.function.family=Add type hints for function +INTN.add.type.hints.for.function=Add type hints for function ''{0}'' +INTN.add.type.hint.for.variable.family=Add type hint for variable +INTN.add.type.hint.for.variable=Add type hint for variable ''{0}'' +INTN.add.type.hint.for.variable.PEP484.incompatible.type=Type ''{0}'' cannot be expressed inline in PEP 484 format #TypeAssertionIntention INTN.insert.assertion=Insert type assertion @@ -295,8 +299,6 @@ INTN.convert.static.method.to.function=Convert static method to function #PyConvertMethodToPropertyIntention INTN.convert.method.to.property=Convert method to property -INTN.annotate.variable.type.PEP484.incompatible.type=The type ''{0}'' cannot be expressed in PEP 484 format - # Conflict checker CONFLICT.name.$0.obscured=Name ''{0}'' obscured by local definitions CONFLICT.name.$0.obscured.cannot.convert=Name ''{0}'' obscured. Cannot convert. diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java index 5ac7b1e06532..16494450e7b5 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java @@ -38,32 +38,26 @@ import java.util.List; import static com.jetbrains.python.codeInsight.intentions.SpecifyTypeInPy3AnnotationsIntention.*; import static com.jetbrains.python.codeInsight.intentions.TypeIntention.getMultiCallable; -import static com.jetbrains.python.codeInsight.intentions.TypeIntention.resolvesToFunction; /** * @author traff */ public class PyAnnotateTypesIntention extends PyBaseIntentionAction { - public PyAnnotateTypesIntention() { - setText(PyBundle.message("INTN.annotate.types")); - } - @NotNull public String getFamilyName() { - return PyBundle.message("INTN.annotate.types"); + return PyBundle.message("INTN.add.type.hints.for.function.family"); } public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (!(file instanceof PyFile) || file instanceof PyDocstringFile) return false; - updateText(); - final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); if (elementAt == null) return false; - if (resolvesToFunction(elementAt, input -> true)) { - updateText(); + final PyFunction function = TypeIntention.findSuitableFunction(elementAt, input -> true); + if (function != null) { + setText(PyBundle.message("INTN.add.type.hints.for.function", function.getName())); return true; } return false; @@ -221,8 +215,4 @@ public class PyAnnotateTypesIntention extends PyBaseIntentionAction { startTemplate(project, callable, builder); } } - - protected void updateText() { - setText(PyBundle.message("INTN.annotate.types")); - } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateVariableTypeIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateVariableTypeIntention.java index a96db78ab460..d903f0d86a76 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateVariableTypeIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateVariableTypeIntention.java @@ -44,7 +44,7 @@ public class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction { @NotNull @Override public String getFamilyName() { - return PyBundle.message("INTN.annotate.types"); + return PyBundle.message("INTN.add.type.hint.for.variable.family"); } @Override @@ -57,7 +57,7 @@ public class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction { return false; } - setText(PyBundle.message("INTN.annotate.types")); + setText(PyBundle.message("INTN.add.type.hint.for.variable", resolved.get(0).getName())); return true; } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyTypeHintGenerationUtil.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyTypeHintGenerationUtil.java index a2cf4877c20a..3dc83a8237f4 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyTypeHintGenerationUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyTypeHintGenerationUtil.java @@ -343,7 +343,7 @@ public class PyTypeHintGenerationUtil { // In this order since PyCollectionTypeImpl implements PyClassType } else { - throw new Pep484IncompatibleTypeException(PyBundle.message("INTN.annotate.variable.type.PEP484.incompatible.type", type.getName())); + throw new Pep484IncompatibleTypeException(PyBundle.message("INTN.add.type.hint.for.variable.PEP484.incompatible.type", type.getName())); } } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java index 955af0a4ed98..92c8510f1619 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java @@ -111,25 +111,28 @@ public abstract class TypeIntention extends PyBaseIntentionAction { } private boolean isAvailableForReturn(@NotNull final PsiElement elementAt) { - return resolvesToFunction(elementAt, input -> !isReturnTypeDefined(input)); + return findSuitableFunction(elementAt, input -> !isReturnTypeDefined(input)) != null; } - static boolean resolvesToFunction(@NotNull PsiElement elementAt, Function isAvailableForFunction) { + @Nullable + public static PyFunction findSuitableFunction(@NotNull PsiElement elementAt, Function isAvailableForFunction) { final PyFunction parentFunction = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); if (parentFunction != null) { final ASTNode nameNode = parentFunction.getNameNode(); if (nameNode != null) { final PsiElement prev = elementAt.getContainingFile().findElementAt(elementAt.getTextOffset()-1); if (nameNode.getPsi() == elementAt || nameNode.getPsi() == prev) { - return isAvailableForFunction.apply(parentFunction); + if (isAvailableForFunction.apply(parentFunction)) { + return parentFunction; + } } } } final PyCallExpression callExpression = getCallExpression(elementAt); - if (callExpression == null) return false; + if (callExpression == null) return null; final PyExpression callee = callExpression.getCallee(); - if (callee == null) return false; + if (callee == null) return null; final PsiReference reference = callee.getReference(); if (reference instanceof PsiPolyVariantReference) { final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false); @@ -137,18 +140,20 @@ public abstract class TypeIntention extends PyBaseIntentionAction { if (results[i].getElement() instanceof PyFunction) { final PsiElement result = results[i].getElement(); final PsiFile psiFile = result.getContainingFile(); - if (psiFile == null) return false; + if (psiFile == null) return null; final VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile != null) { if (ProjectRootManager.getInstance(psiFile.getProject()).getFileIndex().isInLibraryClasses(virtualFile)) { - return false; + return null; } } - return isAvailableForFunction.apply((PyFunction)result); + if (isAvailableForFunction.apply((PyFunction)result)) { + return (PyFunction)result; + } } } } - return false; + return null; } protected boolean isReturnTypeDefined(@NotNull PyFunction function) { diff --git a/python/testData/intentions/PyAnnotateVariableTypeIntentionTest/conflictWithAnnotationFunctionTypeIntention.py b/python/testData/intentions/PyAnnotateVariableTypeIntentionTest/conflictWithAnnotationFunctionTypeIntention.py new file mode 100644 index 000000000000..c65d56408f48 --- /dev/null +++ b/python/testData/intentions/PyAnnotateVariableTypeIntentionTest/conflictWithAnnotationFunctionTypeIntention.py @@ -0,0 +1,5 @@ +def func(x): + return 42 + + +var = func('foo') diff --git a/python/testData/intentions/PyAnnotateVariableTypeIntentionTest/conflictWithAnnotationFunctionTypeIntention_after.py b/python/testData/intentions/PyAnnotateVariableTypeIntentionTest/conflictWithAnnotationFunctionTypeIntention_after.py new file mode 100644 index 000000000000..9375b0cc058f --- /dev/null +++ b/python/testData/intentions/PyAnnotateVariableTypeIntentionTest/conflictWithAnnotationFunctionTypeIntention_after.py @@ -0,0 +1,5 @@ +def func(x): + return 42 + + +var: [int] = func('foo') diff --git a/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java index ac012a0e152d..95bbab78d89f 100644 --- a/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java @@ -20,17 +20,17 @@ public class PyAnnotateTypesIntentionTest extends PyIntentionTestCase { runWithLanguageLevel( LanguageLevel.PYTHON34, () -> { - doIntentionTest(PyBundle.message("INTN.annotate.types"), getTestName(true) + ".py", "foo_decl.py"); + doIntentionTest(PyBundle.message("INTN.add.type.hints.for.function.family"), getTestName(true) + ".py", "foo_decl.py"); myFixture.checkResultByFile("foo_decl.py", "foo_decl_after.py", false); } ); } public void testTypeComment() { - doTest(PyBundle.message("INTN.annotate.types"), LanguageLevel.PYTHON27); + doTest(PyBundle.message("INTN.add.type.hints.for.function.family"), LanguageLevel.PYTHON27); } private void doTest() { - doTest(PyBundle.message("INTN.annotate.types"), LanguageLevel.PYTHON34); + doTest(PyBundle.message("INTN.add.type.hints.for.function.family"), LanguageLevel.PYTHON34); } } diff --git a/python/testSrc/com/jetbrains/python/intentions/PyAnnotateVariableTypeIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateVariableTypeIntentionTest.java index 5cc90fc19413..a5ee7a693a97 100644 --- a/python/testSrc/com/jetbrains/python/intentions/PyAnnotateVariableTypeIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateVariableTypeIntentionTest.java @@ -227,6 +227,10 @@ public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase { doAnnotationTest(); } + public void testConflictWithAnnotationFunctionTypeIntention() { + doTest(LanguageLevel.PYTHON36); + } + private void doAnnotationTest() { doTest(LanguageLevel.PYTHON36); } @@ -236,12 +240,12 @@ public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase { } private void doNegativeTest() { - runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doNegativeTest(PyBundle.message("INTN.annotate.types"))); + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doNegativeTest(PyBundle.message("INTN.add.type.hint.for.variable.family"))); } public void doMultiFileAnnotationTest() { runWithLanguageLevel(LanguageLevel.PYTHON36, () -> { - doMultiFileTest(PyBundle.message("INTN.annotate.types")); + doMultiFileTest(PyBundle.message("INTN.add.type.hint.for.variable.family")); }); } @@ -256,6 +260,6 @@ public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase { } private void doTest(@NotNull LanguageLevel languageLevel) { - doTest(PyBundle.message("INTN.annotate.types"), languageLevel); + doTest(PyBundle.message("INTN.add.type.hint.for.variable.family"), languageLevel); } }