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
This commit is contained in:
Ekaterina Tuzova
2013-02-12 17:58:41 +04:00
parent aeb7b075c6
commit 07b93b3d4b
3 changed files with 70 additions and 66 deletions
@@ -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;
}
}
@@ -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");
@@ -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()));
}