diff --git a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java index 3c781f8e3287..84768901228d 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java +++ b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java @@ -112,19 +112,22 @@ public class PyDocstringGenerator { throw new IllegalArgumentException("TemplateBuilder can be created only for one parameter"); } - builder.replaceRange(TextRange.create(getStartOffset(), getEndOffset()), getDefaultType()); + int offset = getStartOffset(); + if (offset > 0) { + builder.replaceRange(TextRange.create(offset, getEndOffset()), getDefaultType()); - Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate(); + Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate(); - OpenFileDescriptor descriptor = new OpenFileDescriptor( - myProject, - myFile.getVirtualFile(), - myDocStringOwner.getTextOffset() + myDocStringOwner.getTextLength() - ); - Editor targetEditor = FileEditorManager.getInstance(myProject).openTextEditor(descriptor, true); - if (targetEditor != null) { - targetEditor.getCaretModel().moveToOffset(myDocStringExpression.getTextOffset()); - TemplateManager.getInstance(myProject).startTemplate(targetEditor, template); + OpenFileDescriptor descriptor = new OpenFileDescriptor( + myProject, + myFile.getVirtualFile(), + myDocStringOwner.getTextOffset() + myDocStringOwner.getTextLength() + ); + Editor targetEditor = FileEditorManager.getInstance(myProject).openTextEditor(descriptor, true); + if (targetEditor != null) { + targetEditor.getCaretModel().moveToOffset(myDocStringExpression.getTextOffset()); + TemplateManager.getInstance(myProject).startTemplate(targetEditor, template); + } } } @@ -177,14 +180,13 @@ public class PyDocstringGenerator { return docstring != null ? docstring.getText() : "\"\"\"\"\"\""; } - public static Collection getParamsToAdd(StructuredDocString structuredDocString, + public static Collection getParamsToAdd(final StructuredDocString structuredDocString, List params) { - final List existingParameters = - structuredDocString != null ? structuredDocString.getParameters() : Lists.newArrayList(); return Collections2.filter(params, new Predicate() { @Override public boolean apply(DocstringParam input) { - return !existingParameters.contains(input.getName()); + Substring s = structuredDocString != null ? structuredDocString.getParamByNameAndKind(input.getName(), input.getKind()) : null; + return s == null; } }); } @@ -309,9 +311,14 @@ public class PyDocstringGenerator { } public int getStartOffset() { + Pair offsets = getOffsets(); + return offsets != null ? offsets.first : -1; + } + + private Pair getOffsets() { DocstringParam paramToEdit = getParamToEdit(); String paramName = paramToEdit.getName(); - return myParamTypesOffset.get(paramName).first; + return myParamTypesOffset.get(paramName); } private DocstringParam getParamToEdit() { @@ -322,12 +329,11 @@ public class PyDocstringGenerator { } public int getEndOffset() { - DocstringParam paramToEdit = getParamToEdit(); - String paramName = paramToEdit.getName(); - return myParamTypesOffset.get(paramName).second; + Pair offsets = getOffsets(); + return offsets != null ? offsets.second : -1; } - private static class DocstringParam { + public static class DocstringParam { private String myKind; private String myName; private String myType; diff --git a/python/src/com/jetbrains/python/documentation/StructuredDocString.java b/python/src/com/jetbrains/python/documentation/StructuredDocString.java index 3556855e8f1c..24d9caae9ed2 100644 --- a/python/src/com/jetbrains/python/documentation/StructuredDocString.java +++ b/python/src/com/jetbrains/python/documentation/StructuredDocString.java @@ -203,6 +203,16 @@ public abstract class StructuredDocString { return results; } + @Nullable + public Substring getParamByNameAndKind(@NotNull String name, String kind) { + for (Substring s: getTagArguments(kind)) { + if (name.equals(s.getValue())) { + return s; + } + } + return null; + } + public abstract List getParameters(); public abstract List getKeywordArguments(); diff --git a/python/testData/intentions/afterTypeInDocstring7.py b/python/testData/intentions/afterTypeInDocstring7.py new file mode 100644 index 000000000000..ff44a7ebf6a0 --- /dev/null +++ b/python/testData/intentions/afterTypeInDocstring7.py @@ -0,0 +1,7 @@ +def foo(bar, baz): + """Do foo. + :type bar: object + :param bar: something + :return: something else + """ + return bar + baz \ No newline at end of file diff --git a/python/testData/intentions/beforeTypeInDocstring7.py b/python/testData/intentions/beforeTypeInDocstring7.py new file mode 100644 index 000000000000..08f7cc93b116 --- /dev/null +++ b/python/testData/intentions/beforeTypeInDocstring7.py @@ -0,0 +1,6 @@ +def foo(bar, baz): + """Do foo. + :param bar: something + :return: something else + """ + return bar + baz \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyIntentionTest.java b/python/testSrc/com/jetbrains/python/PyIntentionTest.java index 2c4e4b3df5a0..a9f78c5041be 100644 --- a/python/testSrc/com/jetbrains/python/PyIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/PyIntentionTest.java @@ -41,6 +41,7 @@ public class PyIntentionTest extends PyTestCase { /** * Ensures that intention with given hint is not active. + * * @param hint */ private void doNegativeTest(String hint) { @@ -201,6 +202,7 @@ public class PyIntentionTest extends PyTestCase { final IntentionAction action = myFixture.getAvailableIntention(PyBundle.message("INTN.convert.dict.literal.to.dict.constructor")); assertNull(action); } + public void testDictLiteralFormToConstructor3() { myFixture.configureByFile("intentions/beforeDictLiteralFormToConstructor3" + ".py"); final IntentionAction action = myFixture.getAvailableIntention(PyBundle.message("INTN.convert.dict.literal.to.dict.constructor")); @@ -254,7 +256,7 @@ public class PyIntentionTest extends PyTestCase { public void testTransformConditionalExpression() { //PY-3094 doTest(PyBundle.message("INTN.transform.into.if.else.statement")); } - + public void testImportFromToImport() { doTest("Convert to 'import sys'"); } @@ -279,6 +281,10 @@ public class PyIntentionTest extends PyTestCase { doNegativeTest(PyBundle.message("INTN.specify.return.type")); } + public void testTypeInDocstring7() { //PY-8930 + doDocReferenceTest(); + } + private void doDocReferenceTest() { doTest(PyBundle.message("INTN.specify.type")); }