diff --git a/python/resources/intentionDescriptions/PyAnnotateTypesIntention/after.html.template b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/after.html.template
new file mode 100644
index 000000000000..19a3444d8d77
--- /dev/null
+++ b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/after.html.template
@@ -0,0 +1,2 @@
+def foo(var: object) -> object:
+ pass
\ No newline at end of file
diff --git a/python/resources/intentionDescriptions/PyAnnotateTypesIntention/before.html.template b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/before.html.template
new file mode 100644
index 000000000000..12d4a09279d0
--- /dev/null
+++ b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/before.html.template
@@ -0,0 +1,2 @@
+def foo(var):
+ pass
\ No newline at end of file
diff --git a/python/resources/intentionDescriptions/PyAnnotateTypesIntention/description.html b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/description.html
new file mode 100644
index 000000000000..df913398c765
--- /dev/null
+++ b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/description.html
@@ -0,0 +1,9 @@
+
+
+
+ This intention used to specify annotations for parameters and return type of a function.
+
+ If there is type information collected in run-time, it is used to set the default values of types.
+
+
+
\ No newline at end of file
diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml
index 96e89eec959c..911f5041744a 100644
--- a/python/src/META-INF/python-core-common.xml
+++ b/python/src/META-INF/python-core-common.xml
@@ -185,6 +185,11 @@
Python
+
+ com.jetbrains.python.codeInsight.intentions.PyAnnotateTypesIntention
+ Python
+
+
com.jetbrains.python.codeInsight.intentions.TypeAssertionIntention
Python
diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 51fd13b7e041..1c97f3f02175 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -258,6 +258,9 @@ 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
+
#TypeAssertionIntention
INTN.insert.assertion=Insert type assertion
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java
new file mode 100644
index 000000000000..7698c76995b0
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.codeInsight.intentions;
+
+import com.google.common.base.Function;
+import com.intellij.codeInsight.CodeInsightUtilCore;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInsight.template.*;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.IncorrectOperationException;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.documentation.doctest.PyDocstringFile;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+
+import static com.jetbrains.python.codeInsight.intentions.SpecifyTypeInPy3AnnotationsIntention.annotateParameter;
+import static com.jetbrains.python.codeInsight.intentions.SpecifyTypeInPy3AnnotationsIntention.annotateReturnType;
+import static com.jetbrains.python.codeInsight.intentions.TypeIntention.getCallable;
+import static com.jetbrains.python.codeInsight.intentions.TypeIntention.resolvesToFunction;
+
+/**
+ * @author traff
+ */
+public class PyAnnotateTypesIntention implements IntentionAction {
+ private String myText = PyBundle.message("INTN.annotate.types");
+
+ @NotNull
+ public String getText() {
+ return myText;
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return PyBundle.message("INTN.annotate.types");
+ }
+
+ 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, new Function() {
+ @Override
+ public Boolean apply(PyFunction input) {
+ return true;
+ }
+ })) {
+ updateText();
+ return true;
+ }
+ return false;
+ }
+
+ public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
+ final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset());
+ final PyCallable callable = getCallable(elementAt);
+
+
+ final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(callable);
+
+ PyExpression returnType = annotateReturnType(project, editor.getDocument(), elementAt, false);
+
+ if (returnType != null) {
+ builder.replaceElement(returnType, returnType.getText());
+ }
+
+ if (callable instanceof PyFunction) {
+ PyFunction function = (PyFunction) callable;
+ PyParameter[] params = function.getParameterList().getParameters();
+
+ for (int i = params.length - 1; i >= 0; i--) {
+ if (params[i] instanceof PyNamedParameter) {
+ params[i] = annotateParameter(project, editor, (PyNamedParameter)params[i], false);
+ }
+ }
+
+
+ for (int i = params.length - 1; i >= 0; i--) {
+ if (params[i] instanceof PyNamedParameter) {
+ params[i] = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(params[i]);
+ PyAnnotation annotation = ((PyNamedParameter)params[i]).getAnnotation();
+ if (annotation != null) {
+ PyExpression annotationValue = annotation.getValue();
+ builder.replaceElement(annotationValue, annotationValue.getText());
+ }
+ }
+ }
+ }
+ final Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
+
+ int offset = callable.getTextRange().getStartOffset();
+
+ final OpenFileDescriptor descriptor = new OpenFileDescriptor(
+ project,
+ callable.getContainingFile().getVirtualFile(),
+ offset
+ );
+ final Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
+ if (targetEditor != null) {
+ targetEditor.getCaretModel().moveToOffset(offset);
+ TemplateManager.getInstance(project).startTemplate(targetEditor, template);
+ }
+ }
+
+ @Override
+ public boolean startInWriteAction() {
+ return true;
+ }
+
+
+ protected void updateText() {
+ myText = PyBundle.message("INTN.annotate.types");
+ }
+}
\ 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 45f46889f8fc..4afc6751a00d 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
@@ -73,14 +73,17 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
final PyNamedParameter parameter = getParameter(problemElement, resolved);
if (parameter != null) {
- annotateParameter(project, editor, parameter);
+ annotateParameter(project, editor, parameter, true);
}
else {
- annotateReturnType(project, editor.getDocument(), elementAt);
+ annotateReturnType(project, editor.getDocument(), elementAt, true);
}
}
- private static void annotateParameter(Project project, Editor editor, @NotNull PyNamedParameter parameter) {
+ static PyNamedParameter annotateParameter(Project project,
+ Editor editor,
+ @NotNull PyNamedParameter parameter,
+ boolean createTemplate) {
final PyExpression defaultParamValue = parameter.getDefaultValue();
final String paramName = StringUtil.notNullize(parameter.getName());
@@ -106,7 +109,7 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
parameter = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(parameter);
editor.getCaretModel().moveToOffset(parameter.getTextOffset());
final PyAnnotation annotation = parameter.getAnnotation();
- if (annotation != null) {
+ if (annotation != null && createTemplate) {
final PyExpression annotationValue = annotation.getValue();
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(parameter);
@@ -117,42 +120,42 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
final Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
TemplateManager.getInstance(project).startTemplate(editor, template);
}
+
+ return parameter;
}
- private void annotateReturnType(Project project, Document document, PsiElement resolved) {
+ public static PyExpression annotateReturnType(Project project, Document document, PsiElement resolved, boolean createTemplate) {
PyCallable callable = getCallable(resolved);
String returnType = PyNames.OBJECT;
if (callable instanceof PyFunction) {
- PyFunction function = (PyFunction) callable;
+ PyFunction function = (PyFunction)callable;
final PySignature signature = PySignatureCacheManager.getInstance(project).findSignature(
function);
if (signature != null) {
returnType = ObjectUtils.chooseNotNull(signature.getReturnTypeQualifiedName(), returnType);
}
- }
-
- if (callable instanceof PyFunction) {
final String annotationText = " -> " + returnType;
final PsiElement prevElem = PyPsiUtils.getPrevNonCommentSibling(((PyFunction)callable).getStatementList(), true);
assert prevElem != null;
final PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
+ Document documentWithCollable = manager.getDocument(callable.getContainingFile());
try {
final TextRange range = prevElem.getTextRange();
- manager.doPostponedOperationsAndUnblockDocument(document);
+ manager.doPostponedOperationsAndUnblockDocument(documentWithCollable);
if (prevElem.getNode().getElementType() == PyTokenTypes.COLON) {
- document.insertString(range.getStartOffset(), annotationText);
+ documentWithCollable.insertString(range.getStartOffset(), annotationText);
}
else {
- document.insertString(range.getEndOffset(), annotationText + ":");
+ documentWithCollable.insertString(range.getEndOffset(), annotationText + ":");
}
}
finally {
- manager.commitDocument(document);
+ manager.commitDocument(documentWithCollable);
}
@@ -161,22 +164,28 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
assert annotation != null;
final PyExpression annotationValue = annotation.getValue();
assert annotationValue != null : "Generated function must have annotation";
- final int offset = annotationValue.getTextOffset();
- final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(annotationValue);
- builder.replaceRange(TextRange.create(0, returnType.length()), returnType);
- final Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
- final OpenFileDescriptor descriptor = new OpenFileDescriptor(
- project,
- callable.getContainingFile().getVirtualFile(),
- offset
- );
- final Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
- if (targetEditor != null) {
- targetEditor.getCaretModel().moveToOffset(offset);
- TemplateManager.getInstance(project).startTemplate(targetEditor, template);
+ if (createTemplate) {
+ final int offset = annotationValue.getTextOffset();
+
+ final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(annotationValue);
+ builder.replaceRange(TextRange.create(0, returnType.length()), returnType);
+ final Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
+ final OpenFileDescriptor descriptor = new OpenFileDescriptor(
+ project,
+ callable.getContainingFile().getVirtualFile(),
+ offset
+ );
+ final Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
+ if (targetEditor != null) {
+ targetEditor.getCaretModel().moveToOffset(offset);
+ TemplateManager.getInstance(project).startTemplate(targetEditor, template);
+ }
}
+ return annotationValue;
}
+
+ return null;
}
@Override
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java
index effca1d4e9b9..355b2e0f3267 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.codeInsight.intentions;
+import com.google.common.base.Function;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
@@ -80,7 +81,7 @@ public abstract class TypeIntention implements IntentionAction {
}
@Nullable
- protected static PyExpression getProblemElement(@Nullable PsiElement elementAt) {
+ public static PyExpression getProblemElement(@Nullable PsiElement elementAt) {
PyExpression problemElement = PsiTreeUtil.getParentOfType(elementAt, PyNamedParameter.class, PyReferenceExpression.class);
if (problemElement == null) return null;
if (problemElement instanceof PyQualifiedExpression) {
@@ -108,13 +109,22 @@ public abstract class TypeIntention implements IntentionAction {
}
private boolean isAvailableForReturn(@NotNull final PsiElement elementAt) {
+ return resolvesToFunction(elementAt, new Function() {
+ @Override
+ public Boolean apply(PyFunction input) {
+ return !isReturnTypeDefined(input);
+ }
+ });
+ }
+
+ static boolean resolvesToFunction(@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 !isReturnTypeDefined(parentFunction);
+ return isAvailableForFunction.apply(parentFunction);
}
}
}
@@ -126,18 +136,19 @@ public abstract class TypeIntention implements IntentionAction {
final PsiReference reference = callee.getReference();
if (reference instanceof PsiPolyVariantReference) {
final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false);
- if (results.length == 1) {
- final PsiElement result = results[0].getElement();
- if (!(result instanceof PyFunction)) return false;
- final PsiFile psiFile = result.getContainingFile();
- if (psiFile == null) return false;
- final VirtualFile virtualFile = psiFile.getVirtualFile();
- if (virtualFile != null) {
- if (ProjectRootManager.getInstance(psiFile.getProject()).getFileIndex().isInLibraryClasses(virtualFile)) {
- return false;
+ for (int i = 0; io(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition_after.py
new file mode 100644
index 000000000000..a313e5f14fb9
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition_after.py
@@ -0,0 +1,2 @@
+def foo(x: object, y: object) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation.py
new file mode 100644
index 000000000000..39e5e0eeeb14
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation_after.py
new file mode 100644
index 000000000000..26ce2df47d40
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation_after.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation.py
new file mode 100644
index 000000000000..6c5aa528d332
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation.py
@@ -0,0 +1,5 @@
+def foo(x, y):
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation_after.py
new file mode 100644
index 000000000000..38197b897a1d
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation_after.py
@@ -0,0 +1,5 @@
+def foo(x: object, y: object) -> object:
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl.py
new file mode 100644
index 000000000000..282d108e2e52
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl_after.py
new file mode 100644
index 000000000000..a313e5f14fb9
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl_after.py
@@ -0,0 +1,2 @@
+def foo(x: object, y: object) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition.py
new file mode 100644
index 000000000000..cb92a3843add
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition_after.py
new file mode 100644
index 000000000000..50cfaf949af5
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition_after.py
@@ -0,0 +1,2 @@
+def foo(x, y) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation.py
new file mode 100644
index 000000000000..39e5e0eeeb14
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation_after.py
new file mode 100644
index 000000000000..26ce2df47d40
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation_after.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation.py
new file mode 100644
index 000000000000..6c5aa528d332
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation.py
@@ -0,0 +1,5 @@
+def foo(x, y):
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation_after.py
new file mode 100644
index 000000000000..5b42ec7130fc
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation_after.py
@@ -0,0 +1,5 @@
+def foo(x, y) -> object:
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage.py
new file mode 100644
index 000000000000..16d57c86b703
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage.py
@@ -0,0 +1,3 @@
+def foo(var):
+ print(var)
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage_after.py
new file mode 100644
index 000000000000..421e1015965a
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage_after.py
@@ -0,0 +1,3 @@
+def foo(var: object):
+ print(var)
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl.py
new file mode 100644
index 000000000000..282d108e2e52
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl_after.py
new file mode 100644
index 000000000000..50cfaf949af5
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl_after.py
@@ -0,0 +1,2 @@
+def foo(x, y) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java
new file mode 100644
index 000000000000..3d2f2d2ea0e4
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.intentions;
+
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
+
+/**
+ * @author traff
+ */
+public class PyAnnotateTypesIntentionTest extends PyIntentionTestCase {
+ public void testCaretOnDefinition() {
+ doTest();
+ }
+
+ public void testCaretOnInvocation() {
+ doTest();
+ }
+
+ public void testCaretOnImportedInvocation() {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30);
+ try {
+ doIntentionTest(PyBundle.message("INTN.annotate.types"), getTestName(true) + ".py", "foo_decl.py");
+ myFixture.checkResultByFile("foo_decl.py", "foo_decl_after.py", false);
+ }
+ finally {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
+ }
+ }
+
+ private void doTest() {
+ doTest(PyBundle.message("INTN.annotate.types"), LanguageLevel.PYTHON30);
+ }
+}
diff --git a/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java b/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java
index 0d2bee5822c6..cd23d8b7c9be 100644
--- a/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java
+++ b/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java
@@ -42,9 +42,13 @@ public abstract class PyIntentionTestCase extends PyTestCase {
}
}
- protected void doIntentionTest(final String hint) {
+ protected void doIntentionTest(final String hint, String ... files) {
final String testFileName = getTestName(true);
- myFixture.configureByFile(testFileName + ".py");
+ if (files.length>0) {
+ myFixture.configureByFiles(files);
+ } else {
+ myFixture.configureByFile(testFileName + ".py");
+ }
final IntentionAction intentionAction = myFixture.findSingleIntention(hint);
assertNotNull(intentionAction);
myFixture.launchAction(intentionAction);
diff --git a/python/testSrc/com/jetbrains/python/intentions/SpecifyTypeInPy3AnnotationsIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/SpecifyTypeInPy3AnnotationsIntentionTest.java
new file mode 100644
index 000000000000..01cc3d2f9c6e
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/intentions/SpecifyTypeInPy3AnnotationsIntentionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.intentions;
+
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
+
+/**
+ * @author traff
+ */
+public class SpecifyTypeInPy3AnnotationsIntentionTest extends PyIntentionTestCase {
+ public void testCaretOnDefinition() {
+ doTestReturnType();
+ }
+
+
+
+ public void testCaretOnInvocation() {
+ doTestReturnType();
+ }
+
+ public void testCaretOnImportedInvocation() {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30);
+ try {
+ doIntentionTest(PyBundle.message("INTN.specify.return.type.in.annotation"), getTestName(true) + ".py", "foo_decl.py");
+ myFixture.checkResultByFile("foo_decl.py", "foo_decl_after.py", false);
+ }
+ finally {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
+ }
+ }
+
+ public void testCaretOnParamUsage() {
+ doTestParam();
+ }
+
+
+ private void doTestReturnType() {
+ doTest(PyBundle.message("INTN.specify.return.type.in.annotation"), LanguageLevel.PYTHON30);
+ }
+
+
+ private void doTestParam() {
+ doTest(PyBundle.message("INTN.specify.type.in.annotation"), LanguageLevel.PYTHON30);
+ }
+}