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"));
+ }
}