From 07b93b3d4be10d62676cf130da39da9334a9a39c Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 12 Feb 2013 17:58:41 +0400 Subject: [PATCH] Now docstring intention available if type is not specified in docstring and annotation intention available if type is not specified in annotation fixed PY-8782 Specify return type in doctring: intention is not available on function call with collected types --- .../SpecifyTypeInDocstringIntention.java | 28 +++++++ .../SpecifyTypeInPy3AnnotationsIntention.java | 35 ++++++++- .../codeInsight/intentions/TypeIntention.java | 73 ++----------------- 3 files changed, 70 insertions(+), 66 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java index 0bf0ba965623..e4f2d377c9cf 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java @@ -6,11 +6,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyBundle; import com.jetbrains.python.debugger.PySignature; import com.jetbrains.python.debugger.PySignatureCacheManager; import com.jetbrains.python.documentation.PyDocstringGenerator; +import com.jetbrains.python.documentation.StructuredDocString; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; @@ -69,4 +71,30 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention { protected void updateText(boolean isReturn) { myText = isReturn? PyBundle.message("INTN.specify.return.type") : PyBundle.message("INTN.specify.type"); } + + @Override + protected boolean isTypeDefined(PyExpression problemElement) { + return isDefinedInDocstring(problemElement); + } + + private boolean isDefinedInDocstring(PyExpression problemElement) { + PsiReference reference = problemElement.getReference(); + PyFunction pyFunction = PsiTreeUtil.getParentOfType(problemElement, PyFunction.class); + if (pyFunction != null && (problemElement instanceof PyParameter || reference != null && reference.resolve() instanceof PyParameter)) { + final String docstring = pyFunction.getDocStringValue(); + if (docstring != null) { + String name = problemElement.getName(); + if (problemElement instanceof PyQualifiedExpression) { + final PyExpression qualifier = ((PyQualifiedExpression)problemElement).getQualifier(); + if (qualifier != null) { + name = qualifier.getText(); + } + } + StructuredDocString structuredDocString = StructuredDocString.parse(docstring); + return structuredDocString != null && structuredDocString.getParamType(name) != null; + } + return false; + } + return false; + } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java index fa748997776c..437f67a721b4 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java @@ -83,7 +83,7 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention { TemplateManager.getInstance(project).startTemplate(editor, template); } - private static void annotateReturnType(Project project, PsiElement resolved) { + private void annotateReturnType(Project project, PsiElement resolved) { PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); Callable callable = getCallable(resolved); @@ -123,6 +123,39 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention { } } + @Override + protected boolean isTypeDefined(PyExpression problemElement) { + + return isDefinedInAnnotation(problemElement); + } + + private boolean isDefinedInAnnotation(PyExpression problemElement) { + if (LanguageLevel.forElement(problemElement).isOlderThan(LanguageLevel.PYTHON30)) { + return false; + } + PsiReference reference = problemElement.getReference(); + final PsiElement resolved = reference != null? reference.resolve() : null; + PyParameter parameter = getParameter(problemElement, resolved); + + if (parameter instanceof PyNamedParameter && (((PyNamedParameter)parameter).getAnnotation() != null)) return true; + + if (resolved instanceof PyTargetExpression) { // return type + final PyExpression assignedValue = ((PyTargetExpression)resolved).findAssignedValue(); + if (assignedValue instanceof PyCallExpression) { + final PyExpression callee = ((PyCallExpression)assignedValue).getCallee(); + if (callee != null) { + final PsiReference psiReference = callee.getReference(); + if (psiReference != null && psiReference.resolve() == null) return false; + } + final Callable callable = ((PyCallExpression)assignedValue).resolveCalleeFunction(getResolveContext(problemElement)); + + if (callable instanceof PyFunction && ((PyFunction)callable).getAnnotation() != null) return true; + } + } + return false; + } + + @Override protected void updateText(boolean isReturn) { myText = isReturn? PyBundle.message("INTN.specify.return.type.in.annotation") : PyBundle.message("INTN.specify.type.in.annotation"); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java index f827d6fe12d3..95a08e8431ca 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java @@ -9,10 +9,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyNames; -import com.jetbrains.python.documentation.StructuredDocString; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; -import com.jetbrains.python.psi.types.PyDynamicallyEvaluatedType; import com.jetbrains.python.psi.types.PyReturnTypeReference; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; @@ -31,7 +29,10 @@ public abstract class TypeIntention implements IntentionAction { PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); if (elementAt == null) return false; - if (isAvailableForReturn(elementAt)) return true; + if (isAvailableForReturn(elementAt)) { + updateText(true); + return true; + } final PyExpression problemElement = getProblemElement(elementAt); if (problemElement == null) return false; @@ -49,7 +50,7 @@ public abstract class TypeIntention implements IntentionAction { return false; } } - return isTypeUndefined(problemElement); + return !isTypeDefined(problemElement); } @Nullable @@ -67,43 +68,7 @@ public abstract class TypeIntention implements IntentionAction { protected abstract void updateText(boolean isReturn); - private static boolean isTypeUndefined(PyExpression problemElement) { - final PyType type = problemElement.getType(TypeEvalContext.fastStubOnly(problemElement.getContainingFile())); - if (type == null || type instanceof PyReturnTypeReference || type instanceof PyDynamicallyEvaluatedType) { - PsiReference reference = problemElement.getReference(); - if (problemElement instanceof PyQualifiedExpression) { - final PyExpression qualifier = ((PyQualifiedExpression)problemElement).getQualifier(); - if (qualifier != null && !qualifier.getText().equals(PyNames.CANONICAL_SELF)) reference = qualifier.getReference(); - } - - if (isDefinedInDocstring(problemElement, reference)) return false; - return !isDefinedInAnnotation(problemElement, reference); - } - return false; - } - - private static boolean isDefinedInAnnotation(PyExpression problemElement, PsiReference reference) { - if (LanguageLevel.forElement(problemElement).isOlderThan(LanguageLevel.PYTHON30)) { - return false; - } - final PsiElement resolved = reference != null? reference.resolve() : null; - PyParameter parameter = getParameter(problemElement, resolved); - - if (parameter instanceof PyNamedParameter && (((PyNamedParameter)parameter).getAnnotation() != null)) return true; - - if (resolved instanceof PyTargetExpression) { // return type - final PyExpression assignedValue = ((PyTargetExpression)resolved).findAssignedValue(); - if (assignedValue instanceof PyCallExpression) { - final PyExpression callee = ((PyCallExpression)assignedValue).getCallee(); - if (callee != null) { - final PsiReference psiReference = callee.getReference(); - if (psiReference != null && psiReference.resolve() == null) return false; - } - final Callable callable = ((PyCallExpression)assignedValue).resolveCalleeFunction(getResolveContext(problemElement)); - - if (callable instanceof PyFunction && ((PyFunction)callable).getAnnotation() != null) return true; - } - } + protected boolean isTypeDefined(PyExpression problemElement) { return false; } @@ -115,26 +80,6 @@ public abstract class TypeIntention implements IntentionAction { return parameter; } - private static boolean isDefinedInDocstring(PyExpression problemElement, PsiReference reference) { - PyFunction pyFunction = PsiTreeUtil.getParentOfType(problemElement, PyFunction.class); - if (pyFunction != null && (problemElement instanceof PyParameter || reference != null && reference.resolve() instanceof PyParameter)) { - final String docstring = pyFunction.getDocStringValue(); - if (docstring != null) { - String name = problemElement.getName(); - if (problemElement instanceof PyQualifiedExpression) { - final PyExpression qualifier = ((PyQualifiedExpression)problemElement).getQualifier(); - if (qualifier != null) { - name = qualifier.getText(); - } - } - StructuredDocString structuredDocString = StructuredDocString.parse(docstring); - return structuredDocString != null && structuredDocString.getParamType(name) != null; - } - return false; - } - return false; - } - private boolean isAvailableForReturn(PsiElement elementAt) { PyCallExpression callExpression = getCallExpression(elementAt); @@ -160,7 +105,6 @@ public abstract class TypeIntention implements IntentionAction { return false; } } - updateText(true); return true; } } @@ -172,7 +116,6 @@ public abstract class TypeIntention implements IntentionAction { if (parentFunction != null) { final ASTNode nameNode = parentFunction.getNameNode(); if (nameNode != null && nameNode.getPsi() == elementAt) { - updateText(true); return true; } } @@ -204,7 +147,7 @@ public abstract class TypeIntention implements IntentionAction { } @Nullable - protected static Callable getCallable(PsiElement elementAt) { + protected Callable getCallable(PsiElement elementAt) { PyCallExpression callExpression = getCallExpression(elementAt); if (callExpression != null && elementAt != null) { @@ -214,7 +157,7 @@ public abstract class TypeIntention implements IntentionAction { return PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); } - private static PyResolveContext getResolveContext(@NotNull PsiElement origin) { + protected PyResolveContext getResolveContext(@NotNull PsiElement origin) { return PyResolveContext.defaultContext().withTypeEvalContext(TypeEvalContext.fastStubOnly(origin.getContainingFile())); }