Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
小 白蛋
Intellij Community
Commits
90b71d79
Commit
90b71d79
authored
9 years ago
by
Dmitry Trofimov
Browse files
Options
Download
Email Patches
Plain Diff
Generate type annotation in comment for Python 2.x
parent
d04be991
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java
+109
-19
...thon/codeInsight/intentions/PyAnnotateTypesIntention.java
python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
+43
-27
...ight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment.py
+2
-0
...ta/intentions/PyAnnotateTypesIntentionTest/typeComment.py
python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment_after.py
+3
-0
...entions/PyAnnotateTypesIntentionTest/typeComment_after.py
python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java
+5
-0
...rains/python/intentions/PyAnnotateTypesIntentionTest.java
with
162 additions
and
46 deletions
+162
-46
python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java
+
109
-
19
View file @
90b71d79
...
...
@@ -16,13 +16,18 @@
package
com.jetbrains.python.codeInsight.intentions
;
import
com.google.common.base.Function
;
import
com.google.common.collect.Lists
;
import
com.intellij.codeInsight.CodeInsightUtilCore
;
import
com.intellij.codeInsight.intention.IntentionAction
;
import
com.intellij.codeInsight.template.*
;
import
com.intellij.openapi.editor.Document
;
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.openapi.util.Pair
;
import
com.intellij.openapi.util.TextRange
;
import
com.intellij.psi.PsiDocumentManager
;
import
com.intellij.psi.PsiElement
;
import
com.intellij.psi.PsiFile
;
import
com.intellij.util.IncorrectOperationException
;
...
...
@@ -31,8 +36,9 @@ 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
java.util.List
;
import
static
com
.
jetbrains
.
python
.
codeInsight
.
intentions
.
SpecifyTypeInPy3AnnotationsIntention
.*;
import
static
com
.
jetbrains
.
python
.
codeInsight
.
intentions
.
TypeIntention
.
getCallable
;
import
static
com
.
jetbrains
.
python
.
codeInsight
.
intentions
.
TypeIntention
.
resolvesToFunction
;
...
...
@@ -55,8 +61,6 @@ public class PyAnnotateTypesIntention implements IntentionAction {
public
boolean
isAvailable
(
@NotNull
Project
project
,
Editor
editor
,
PsiFile
file
)
{
if
(!(
file
instanceof
PyFile
)
||
file
instanceof
PyDocstringFile
)
return
false
;
if
(!
LanguageLevel
.
forElement
(
file
).
isPy3K
())
return
false
;
updateText
();
final
PsiElement
elementAt
=
PyUtil
.
findNonWhitespaceAtOffset
(
file
,
editor
.
getCaretModel
().
getOffset
());
...
...
@@ -78,7 +82,106 @@ public class PyAnnotateTypesIntention implements IntentionAction {
final
PsiElement
elementAt
=
PyUtil
.
findNonWhitespaceAtOffset
(
file
,
editor
.
getCaretModel
().
getOffset
());
final
PyCallable
callable
=
getCallable
(
elementAt
);
if
(
isPy3k
(
file
))
{
generatePy3kTypeAnnotations
(
project
,
editor
,
elementAt
,
callable
);
}
else
{
if
(
callable
instanceof
PyFunction
)
{
generateTypeCommentAnnotations
(
project
,
editor
,
elementAt
,
(
PyFunction
)
callable
);
}
}
}
private
static
void
generateTypeCommentAnnotations
(
Project
project
,
Editor
editor
,
PsiElement
at
,
PyFunction
function
)
{
StringBuilder
replacementTextBuilder
=
new
StringBuilder
(
"# type: ("
);
PyParameter
[]
params
=
function
.
getParameterList
().
getParameters
();
List
<
Pair
<
Integer
,
String
>>
templates
=
Lists
.
newArrayList
();
for
(
int
i
=
0
;
i
<
params
.
length
;
i
++)
{
String
type
=
parameterType
(
params
[
i
]);
templates
.
add
(
Pair
.
create
(
replacementTextBuilder
.
length
(),
type
));
replacementTextBuilder
.
append
(
type
);
if
(
i
<
params
.
length
-
1
)
{
replacementTextBuilder
.
append
(
", "
);
}
}
replacementTextBuilder
.
append
(
") -> "
);
String
returnType
=
returnType
(
function
);
templates
.
add
(
Pair
.
create
(
replacementTextBuilder
.
length
(),
returnType
));
replacementTextBuilder
.
append
(
returnType
);
final
PyStatementList
statements
=
function
.
getStatementList
();
final
String
indentation
=
PyIndentUtil
.
getExpectedElementIndent
(
statements
);
replacementTextBuilder
.
insert
(
0
,
indentation
);
replacementTextBuilder
.
insert
(
0
,
"\n"
);
final
PsiDocumentManager
manager
=
PsiDocumentManager
.
getInstance
(
project
);
final
Document
document
=
manager
.
getDocument
(
function
.
getContainingFile
());
if
(
document
!=
null
)
{
final
PsiElement
beforeStatements
=
statements
.
getPrevSibling
();
int
offset
=
beforeStatements
.
getTextRange
().
getStartOffset
();
if
(
":"
.
equals
(
beforeStatements
.
getText
()))
{
offset
+=
1
;
}
try
{
document
.
insertString
(
offset
,
replacementTextBuilder
.
toString
());
}
finally
{
manager
.
commitDocument
(
document
);
}
function
=
CodeInsightUtilCore
.
forcePsiPostprocessAndRestoreElement
(
function
);
if
(
function
!=
null
)
{
final
TemplateBuilder
builder
=
TemplateBuilderFactory
.
getInstance
().
createTemplateBuilder
(
function
);
for
(
Pair
<
Integer
,
String
>
template
:
templates
)
{
builder
.
replaceRange
(
TextRange
.
from
(
offset
-
function
.
getTextRange
().
getStartOffset
()
+
replacementTextBuilder
.
toString
().
indexOf
(
'#'
)
+
template
.
first
,
template
.
second
.
length
()),
template
.
second
);
}
startTemplate
(
project
,
function
,
builder
);
}
}
}
private
static
void
startTemplate
(
Project
project
,
PyCallable
callable
,
TemplateBuilder
builder
)
{
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
);
}
}
private
static
boolean
isPy3k
(
PsiFile
file
)
{
return
LanguageLevel
.
forElement
(
file
).
isPy3K
();
}
private
static
void
generatePy3kTypeAnnotations
(
@NotNull
Project
project
,
Editor
editor
,
PsiElement
elementAt
,
PyCallable
callable
)
{
final
TemplateBuilder
builder
=
TemplateBuilderFactory
.
getInstance
().
createTemplateBuilder
(
callable
);
PyExpression
returnType
=
annotateReturnType
(
project
,
editor
.
getDocument
(),
elementAt
,
false
);
...
...
@@ -88,7 +191,7 @@ public class PyAnnotateTypesIntention implements IntentionAction {
}
if
(
callable
instanceof
PyFunction
)
{
PyFunction
function
=
(
PyFunction
)
callable
;
PyFunction
function
=
(
PyFunction
)
callable
;
PyParameter
[]
params
=
function
.
getParameterList
().
getParameters
();
for
(
int
i
=
params
.
length
-
1
;
i
>=
0
;
i
--)
{
...
...
@@ -112,20 +215,7 @@ public class PyAnnotateTypesIntention implements IntentionAction {
}
}
if
(
callable
!=
null
)
{
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
);
}
startTemplate
(
project
,
callable
,
builder
);
}
}
...
...
This diff is collapsed.
Click to expand it.
python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
+
43
-
27
View file @
90b71d79
...
...
@@ -91,16 +91,9 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
final
String
defaultParamText
=
defaultParamValue
==
null
?
null
:
defaultParamValue
.
getText
();
String
paramType
=
PyNames
.
OBJECT
;
String
paramType
=
parameterType
(
parameter
)
;
PyFunction
function
=
PsiTreeUtil
.
getParentOfType
(
parameter
,
PyFunction
.
class
);
if
(
function
!=
null
)
{
final
PySignature
signature
=
PySignatureCacheManager
.
getInstance
(
parameter
.
getProject
()).
findSignature
(
function
);
if
(
signature
!=
null
)
{
paramType
=
ObjectUtils
.
chooseNotNull
(
signature
.
getArgTypeQualifiedName
(
paramName
),
paramType
);
}
}
final
PyNamedParameter
namedParameter
=
elementGenerator
.
createParameter
(
paramName
,
defaultParamText
,
paramType
,
LanguageLevel
.
forElement
(
parameter
));
...
...
@@ -124,18 +117,39 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
return
parameter
;
}
public
static
PyExpression
annotateReturnType
(
Project
project
,
Document
document
,
PsiElement
resolved
,
boolean
createTemplate
)
{
PyCallable
callable
=
getCallable
(
resolved
);
static
String
parameterType
(
PyParameter
parameter
)
{
String
paramType
=
PyNames
.
OBJECT
;
PyFunction
function
=
PsiTreeUtil
.
getParentOfType
(
parameter
,
PyFunction
.
class
);
if
(
function
!=
null
)
{
final
PySignature
signature
=
PySignatureCacheManager
.
getInstance
(
parameter
.
getProject
()).
findSignature
(
function
);
String
parameterName
=
parameter
.
getName
();
if
(
signature
!=
null
&&
parameterName
!=
null
)
{
paramType
=
ObjectUtils
.
chooseNotNull
(
signature
.
getArgTypeQualifiedName
(
parameterName
),
paramType
);
}
}
return
paramType
;
}
static
String
returnType
(
@NotNull
PyFunction
function
)
{
String
returnType
=
PyNames
.
OBJECT
;
final
PySignature
signature
=
PySignatureCacheManager
.
getInstance
(
function
.
getProject
()).
findSignature
(
function
);
if
(
signature
!=
null
)
{
returnType
=
ObjectUtils
.
chooseNotNull
(
signature
.
getReturnTypeQualifiedName
(),
returnType
);
}
return
returnType
;
}
public
static
PyExpression
annotateReturnType
(
Project
project
,
Document
document
,
PsiElement
resolved
,
boolean
createTemplate
)
{
PyCallable
callable
=
getCallable
(
resolved
);
if
(
callable
instanceof
PyFunction
)
{
PyFunction
function
=
(
PyFunction
)
callable
;
final
PySignature
signature
=
PySignatureCacheManager
.
getInstance
(
project
).
findSignature
(
function
);
if
(
signature
!=
null
)
{
returnType
=
ObjectUtils
.
chooseNotNull
(
signature
.
getReturnTypeQualifiedName
(),
returnType
);
}
String
returnType
=
returnType
(
function
);
final
String
annotationText
=
" -> "
+
returnType
;
...
...
@@ -143,20 +157,22 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
assert
prevElem
!=
null
;
final
PsiDocumentManager
manager
=
PsiDocumentManager
.
getInstance
(
project
);
Document
documentWithCollable
=
manager
.
getDocument
(
callable
.
getContainingFile
());
try
{
final
TextRange
range
=
prevElem
.
getTextRange
();
manager
.
doPostponedOperationsAndUnblockDocument
(
documentWithCollable
);
if
(
prevElem
.
getNode
().
getElementType
()
==
PyTokenTypes
.
COLON
)
{
documentWithCollable
.
insertString
(
range
.
getStartOffset
(),
annotationText
);
Document
documentWithCallable
=
manager
.
getDocument
(
callable
.
getContainingFile
());
if
(
documentWithCallable
!=
null
)
{
try
{
final
TextRange
range
=
prevElem
.
getTextRange
();
manager
.
doPostponedOperationsAndUnblockDocument
(
documentWithCallable
);
if
(
prevElem
.
getNode
().
getElementType
()
==
PyTokenTypes
.
COLON
)
{
documentWithCallable
.
insertString
(
range
.
getStartOffset
(),
annotationText
);
}
else
{
documentWithCallable
.
insertString
(
range
.
getEndOffset
(),
annotationText
+
":"
);
}
}
else
{
documentWithC
o
llable
.
insertString
(
range
.
getEndOffset
(),
annotationText
+
":"
);
finally
{
manager
.
commitDocument
(
documentWithC
a
llable
);
}
}
finally
{
manager
.
commitDocument
(
documentWithCollable
);
}
callable
=
CodeInsightUtilCore
.
forcePsiPostprocessAndRestoreElement
(
callable
);
...
...
This diff is collapsed.
Click to expand it.
python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment.py
0 → 100644
+
2
-
0
View file @
90b71d79
def
fo
<
caret
>
o
(
x
,
y
):
pass
\ No newline at end of file
This diff is collapsed.
Click to expand it.
python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment_after.py
0 → 100644
+
3
-
0
View file @
90b71d79
def
foo
(
x
,
y
):
# type: (object, object) -> object
pass
\ No newline at end of file
This diff is collapsed.
Click to expand it.
python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java
+
5
-
0
View file @
90b71d79
...
...
@@ -42,6 +42,11 @@ public class PyAnnotateTypesIntentionTest extends PyIntentionTestCase {
}
}
public
void
testTypeComment
()
{
doTest
(
PyBundle
.
message
(
"INTN.annotate.types"
),
LanguageLevel
.
PYTHON27
);
}
private
void
doTest
()
{
doTest
(
PyBundle
.
message
(
"INTN.annotate.types"
),
LanguageLevel
.
PYTHON30
);
}
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help