diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 38ce4460e311..53826839a82d 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -195,6 +195,11 @@ Python + + com.jetbrains.python.codeInsight.intentions.PyDocStubIntention + Python + + diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 27a549619bd1..a3c535f5b10a 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -167,6 +167,9 @@ INTN.triple.quoted.string=Convert triple-quoted string to single-quoted string # PyTransformConditionalExpressionIntention INTN.transform.into.if.else.statement=Transform conditional expression into if/else statement +# PyConvertTripleQuotedStringIntention +INTN.doc.string.stub=Insert documentation string stub + # Conflict checker CONFLICT.name.$0.obscured=Name ''{0}'' obscured by local definitions CONFLICT.name.$0.obscured.cannot.convert=Name ''{0}'' obscured. Cannot convert. diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyDocStubIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyDocStubIntention.java new file mode 100644 index 000000000000..6cc7527ff193 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyDocStubIntention.java @@ -0,0 +1,60 @@ +package com.jetbrains.python.codeInsight.intentions; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.documentation.PythonDocumentationProvider; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; + +/** + * User: catherine + * Intention to add documentation string for function + * (with checked format) + */ +public class PyDocStubIntention extends BaseIntentionAction { + + @NotNull + public String getFamilyName() { + return PyBundle.message("INTN.doc.string.stub"); + } + + @NotNull + @Override + public String getText() { + return PyBundle.message("INTN.doc.string.stub"); + } + + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + PyFunction function = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyFunction.class); + if (function != null) { + final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), + PyDocStringOwner.class); + if (docStringOwner != null) { + if (docStringOwner.getDocStringExpression() != null) return false; + } + if (function.getStatementList() != null && function.getStatementList().getStatements().length != 0) + return true; + } + return false; + } + + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PyFunction function = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyFunction.class); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + PythonDocumentationProvider documentationProvider = new PythonDocumentationProvider(); + PyStatementList list = function.getStatementList(); + PsiWhiteSpace whitespace = PsiTreeUtil.getPrevSiblingOfType(list, PsiWhiteSpace.class); + String docContent = documentationProvider.generateDocumentationContentStub(function, (whitespace != null? whitespace.getText() : "\n")); + PyExpressionStatement string = elementGenerator.createFromText(LanguageLevel.forElement(function), PyExpressionStatement.class, + "\"\"\"" + docContent + "\"\"\""); + if (list.getStatements().length != 0) + list.addBefore(string, list.getStatements()[0]); + } + +} diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index 012315982f63..b2bd68dfebfd 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -804,7 +804,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i return ret; } - public String generateDocumentationContentStub(PyFunction element, String substring, String offset) { + public String generateDocumentationContentStub(PyFunction element, String offset) { Project project = element.getProject(); PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(project); String result = ""; @@ -812,7 +812,6 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i result += generateContent(element, offset, EPYDOC_PREFIX); else if (documentationSettings.isReSTFormat()) result += generateContent(element, offset, RST_PREFIX); - result += substring; return result; } diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java index 17ff790b3664..8129f994a2c1 100644 --- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java @@ -66,7 +66,8 @@ public class PythonEnterHandler implements EnterHandlerDelegate { PyFunction fun = PsiTreeUtil.getParentOfType(element, PyFunction.class); if (fun != null) { PsiWhiteSpace whitespace = PsiTreeUtil.getPrevSiblingOfType(fun.getStatementList(), PsiWhiteSpace.class); - String docStub = provider.generateDocumentationContentStub(fun, element.getParent().getText().substring(0,3), whitespace.getText()); + String docStub = provider.generateDocumentationContentStub(fun, (whitespace != null ? whitespace.getText() : "\n")); + docStub += element.getParent().getText().substring(0,3); if (docStub != null && docStub.length() != 0) { editor.getDocument().insertString(editor.getCaretModel().getOffset(), docStub); return Result.Continue; diff --git a/python/testData/intentions/afterDocStub.py b/python/testData/intentions/afterDocStub.py new file mode 100644 index 000000000000..aaf19b71a0e2 --- /dev/null +++ b/python/testData/intentions/afterDocStub.py @@ -0,0 +1,7 @@ +def foo(a, b): + """ + :param a: + :param b: + :return: + """ + pass \ No newline at end of file diff --git a/python/testData/intentions/beforeDocStub.py b/python/testData/intentions/beforeDocStub.py new file mode 100644 index 000000000000..ec6c73751860 --- /dev/null +++ b/python/testData/intentions/beforeDocStub.py @@ -0,0 +1,2 @@ +def foo(a, b): + pass \ 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 6a21242632eb..a8714a284c79 100644 --- a/python/testSrc/com/jetbrains/python/PyIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/PyIntentionTest.java @@ -1,6 +1,9 @@ package com.jetbrains.python; +import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.intention.IntentionAction; +import com.jetbrains.python.documentation.DocStringFormat; +import com.jetbrains.python.documentation.PyDocumentationSettings; import com.jetbrains.python.fixtures.PyLightFixtureTestCase; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; @@ -174,4 +177,12 @@ public class PyIntentionTest extends PyLightFixtureTestCase { public void testTransformConditionalExpression() { //PY-3094 doTest(PyBundle.message("INTN.transform.into.if.else.statement")); } + + public void testDocStub() { + CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance(); + codeInsightSettings.JAVADOC_STUB_ON_ENTER = true; + PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getProject()); + documentationSettings.setFormat(DocStringFormat.REST); + doTest(PyBundle.message("INTN.doc.string.stub")); + } }