diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java index 2c8848c58ece..fb5dff644ec5 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java @@ -19,14 +19,11 @@ import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.debugger.PySignature; -import com.jetbrains.python.debugger.PySignatureCacheManager; import com.jetbrains.python.documentation.DocStringUtil; import com.jetbrains.python.documentation.PyDocstringGenerator; import com.jetbrains.python.documentation.doctest.PyDocstringFile; @@ -70,10 +67,8 @@ public class PyGenerateDocstringIntention extends BaseIntentionAction { private boolean isAvailableForFunction(PyFunction function) { if (function.getDocStringValue() != null) { - final PyDocstringGenerator docstringGenerator = new PyDocstringGenerator(function); - setParametersWithTypesFromDebuggerSignature(docstringGenerator, function); - - if (docstringGenerator.haveParametersToAdd()) { + final PyDocstringGenerator docstringGenerator = new PyDocstringGenerator(function).withDefaultParameters(); + if (docstringGenerator.hasParametersToAdd()) { myText = PyBundle.message("INTN.add.parameters.to.docstring"); return true; } @@ -108,7 +103,8 @@ public class PyGenerateDocstringIntention extends BaseIntentionAction { } final PyDocstringGenerator docstringGenerator = new PyDocstringGenerator(docStringOwner); if (docStringOwner instanceof PyFunction) { - setParametersWithTypesFromDebuggerSignature(docstringGenerator, (PyFunction)docStringOwner); + docstringGenerator.useTypesFromDebuggerSignature(true); + docstringGenerator.withDefaultParameters(); if (docStringOwner.getDocStringValue() == null) { docstringGenerator.addReturn(); } @@ -120,18 +116,4 @@ public class PyGenerateDocstringIntention extends BaseIntentionAction { editor.getCaretModel().moveCaretRelatively(0, 1, false, false, false); } } - - private static void setParametersWithTypesFromDebuggerSignature(@NotNull PyDocstringGenerator generator, @NotNull PyFunction myFunction) { - PySignature signature = PySignatureCacheManager.getInstance(myFunction.getProject()).findSignature(myFunction); - for (PyParameter functionParam : myFunction.getParameterList().getParameters()) { - String paramName = functionParam.getName(); - if (!functionParam.isSelf() && !StringUtil.isEmpty(paramName)) { - String type = signature != null ? signature.getArgTypeQualifiedName(paramName) : null; - generator.withParam(paramName); - if (type != null) { - generator.withParamTypedByName(paramName, type); - } - } - } - } } diff --git a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java index 3bafa3f9ff9d..66c73bb545ae 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java +++ b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java @@ -15,8 +15,6 @@ */ package com.jetbrains.python.documentation; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.intellij.codeInsight.CodeInsightUtilCore; import com.intellij.codeInsight.template.*; @@ -30,6 +28,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; +import com.jetbrains.python.debugger.PySignature; +import com.jetbrains.python.debugger.PySignatureCacheManager; import com.jetbrains.python.documentation.docstrings.DocStringProvider; import com.jetbrains.python.documentation.docstrings.TagBasedDocStringBuilder; import com.jetbrains.python.documentation.docstrings.TagBasedDocStringUpdater; @@ -38,7 +38,6 @@ import com.jetbrains.python.toolbox.Substring; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; import java.util.List; /** @@ -46,7 +45,7 @@ import java.util.List; */ public class PyDocstringGenerator { - private final List myParams = Lists.newArrayList(); + private List myParams = Lists.newArrayList(); private boolean myGenerateReturn; // Updated after buildAndInsert @@ -54,6 +53,10 @@ public class PyDocstringGenerator { private PyDocStringOwner myDocStringOwner; private final StructuredDocString myOriginalDocString; private final DocStringFormat myDocStringFormat; + private String myQuotes = "\"\"\""; + private boolean myUseTypesFromDebuggerSignature = false; + private boolean myNewMode = false; + private boolean addFirstEmptyLine = false; public PyDocstringGenerator(@NotNull PyDocStringOwner docStringOwner) { myDocStringOwner = docStringOwner; @@ -85,12 +88,60 @@ public class PyDocstringGenerator { return this; } + @NotNull + public PyDocstringGenerator withQuotes(@NotNull String quotes) { + myQuotes = quotes; + return this; + } + + @NotNull + public PyDocstringGenerator useTypesFromDebuggerSignature(boolean use) { + myUseTypesFromDebuggerSignature = use; + return this; + } + @NotNull public PyDocstringGenerator addReturn() { myGenerateReturn = true; return this; } + @NotNull + public PyDocstringGenerator forceNewMode() { + myNewMode = true; + return this; + } + + @NotNull + public PyDocstringGenerator withDefaultParameters() { + // Populate parameters lift if no one was specified explicitly + if (myParams.isEmpty()) { + if (myDocStringOwner instanceof PyFunction) { + PySignature signature = null; + if (myUseTypesFromDebuggerSignature) { + signature = PySignatureCacheManager.getInstance(myDocStringOwner.getProject()).findSignature((PyFunction)myDocStringOwner); + } + for (PyParameter param : ((PyFunction)myDocStringOwner).getParameterList().getParameters()) { + String paramName = param.getName(); + if (StringUtil.isEmpty(paramName) || param.isSelf() || + myOriginalDocString != null && myOriginalDocString.getParamTypeSubstring(paramName) != null) { + continue; + } + String type = signature != null ? signature.getArgTypeQualifiedName(paramName) : null; + withParam(paramName); + if (type != null) { + withParamTypedByName(paramName, type); + } + } + } + } + return this; + } + + public boolean hasParametersToAdd() { + return !myParams.isEmpty(); + } + @Nullable private PyStringLiteralExpression getDocStringExpression() { return myDocStringOwner.getDocStringExpression(); @@ -165,6 +216,7 @@ public class PyDocstringGenerator { } } + @NotNull private String getDefaultType() { DocstringParam param = getParamToEdit(); if (StringUtil.isEmpty(param.getType())) { @@ -177,12 +229,16 @@ public class PyDocstringGenerator { @NotNull public String buildDocString() { - if (myOriginalDocString != null) { - return updateDocString(); - } - else { + if (isNewMode()) { return createDocString(); } + else { + return updateDocString(); + } + } + + private boolean isNewMode() { + return myNewMode || myOriginalDocString == null; } @NotNull @@ -224,10 +280,10 @@ public class PyDocstringGenerator { } if (builder.getLines().size() > 1) { - return "\"\"\"\n" + builder.buildContent(indentation, true) + '\n' + indentation + "\"\"\""; + return myQuotes + '\n' + builder.buildContent(indentation, true) + '\n' + indentation + myQuotes; } else { - return "\"\"\"" + builder.buildContent(indentation, false) + "\"\"\""; + return myQuotes + builder.buildContent(indentation, false) + myQuotes; } } else { @@ -260,26 +316,7 @@ public class PyDocstringGenerator { } return updater.getDocStringText(); } - return "\"\"\"\"\"\""; - } - - public boolean haveParametersToAdd() { - return !collectParametersToAdd().isEmpty(); - } - - @NotNull - private Collection collectParametersToAdd() { - return Collections2.filter(myParams, new Predicate() { - @Override - public boolean apply(DocstringParam param) { - if (param.isReturnValue()) { - return myOriginalDocString.getReturnTypeSubstring() == null; - } - else { - return myOriginalDocString.getParamTypeSubstring(param.myName) == null; - } - } - }); + return myQuotes + myQuotes; } private DocstringParam getParamToEdit() { diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index 9497aed251e6..1d558fb8d9da 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -22,7 +22,6 @@ import com.intellij.lang.documentation.ExternalDocumentationProvider; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; @@ -561,26 +560,6 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i return result; } - public static void insertDocStub(@NotNull PyFunction function, - @NotNull PyStatementList insertPlace, - @NotNull Project project, - @Nullable Editor editor) { - final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - final String ws = "\n" + getStatementListIndent(insertPlace); - final String docContent = ws + generateDocumentationContentStub(function, ws, true); - final PyExpressionStatement string = elementGenerator.createDocstring("\"\"\"" + docContent + "\"\"\""); - final PyStatement[] statements = insertPlace.getStatements(); - if (statements.length != 0) { - insertPlace.addBefore(string, statements[0]); - } - final PyStringLiteralExpression docstring = function.getDocStringExpression(); - if (editor != null && docstring != null) { - final int offset = docstring.getTextOffset(); - editor.getCaretModel().moveToOffset(offset); - editor.getCaretModel().moveCaretRelatively(0, 1, false, false, false); - } - } - @NotNull private static String getStatementListIndent(@NotNull PyStatementList statementList) { final PsiWhiteSpace whitespace = PsiTreeUtil.getPrevSiblingOfType(statementList, PsiWhiteSpace.class); diff --git a/python/src/com/jetbrains/python/editor/PythonDocCommentUtil.java b/python/src/com/jetbrains/python/editor/PythonDocCommentUtil.java index ca0150e0bf77..d37c5b7a3881 100644 --- a/python/src/com/jetbrains/python/editor/PythonDocCommentUtil.java +++ b/python/src/com/jetbrains/python/editor/PythonDocCommentUtil.java @@ -21,6 +21,7 @@ import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl; /** * User : catherine @@ -40,6 +41,8 @@ public class PythonDocCommentUtil { if (docStringOwner == func) { PyStringLiteralExpression str = docStringOwner.getDocStringExpression(); String text = element.getText(); + final int prefix = PyStringLiteralExpressionImpl.getPrefixLength(text); + text = text.substring(prefix); if (str != null && text.equals(str.getText()) && (text.startsWith("\"\"\"") || text.startsWith("'''"))) { if (offset == str.getTextRange().getStartOffset()) { diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java index 10486dd6d0a8..34af5ccd34cb 100644 --- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java @@ -35,7 +35,7 @@ import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; -import com.jetbrains.python.documentation.PythonDocumentationProvider; +import com.jetbrains.python.documentation.PyDocstringGenerator; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl; import org.jetbrains.annotations.NotNull; @@ -257,20 +257,17 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { } private static void insertDocStringStub(Editor editor, PsiElement element) { - PythonDocumentationProvider provider = new PythonDocumentationProvider(); - PyFunction fun = PsiTreeUtil.getParentOfType(element, PyFunction.class); - if (fun != null) { - String docStub = provider.generateDocumentationContentStub(fun, false); - docStub += element.getParent().getText().substring(0,3); - if (docStub.length() != 0) { - editor.getDocument().insertString(editor.getCaretModel().getOffset(), docStub); - return; - } - } - PyElement klass = PsiTreeUtil.getParentOfType(element, PyClass.class, PyFile.class); - if (klass != null && element != null) { - editor.getDocument().insertString(editor.getCaretModel().getOffset(), - PythonDocCommentUtil.generateDocForClass(klass, element.getParent().getText().substring(0, 3))); + PyDocStringOwner docOwner = PsiTreeUtil.getParentOfType(element, PyFunction.class, PyClass.class); + if (docOwner != null) { + final int caretOffset = editor.getCaretModel().getOffset(); + final String quotes = editor.getDocument().getText(TextRange.from(caretOffset - 3, 3)); + final String docString = new PyDocstringGenerator(docOwner) + .withDefaultParameters() + .addReturn() + .withQuotes(quotes) + .forceNewMode() + .buildDocString(); + editor.getDocument().replaceString(caretOffset - 3, caretOffset, docString); } }