From b3e325357fd221788eb45f441bc3c232543630cf Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 24 Aug 2015 00:12:13 +0300 Subject: [PATCH] Fix broken tests of docstring generation on enter --- .../PyGenerateDocstringIntention.java | 8 +- .../SpecifyTypeInDocstringIntention.java | 1 + .../documentation/PyDocstringGenerator.java | 104 +++++++++++------- .../python/editor/PythonEnterHandler.java | 3 +- 4 files changed, 72 insertions(+), 44 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java index fb5dff644ec5..475b6f110739 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java @@ -103,8 +103,12 @@ public class PyGenerateDocstringIntention extends BaseIntentionAction { } final PyDocstringGenerator docstringGenerator = new PyDocstringGenerator(docStringOwner); if (docStringOwner instanceof PyFunction) { - docstringGenerator.useTypesFromDebuggerSignature(true); - docstringGenerator.withDefaultParameters(); + docstringGenerator + .useTypesFromDebuggerSignature(true) + .addReturn() + .withDefaultParameters() + .addFirstEmptyLine(); + if (docStringOwner.getDocStringValue() == null) { docstringGenerator.addReturn(); } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java index 08ea3fa4a05c..9ec0e61c4bd7 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java @@ -78,6 +78,7 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention { final boolean isReturn = "rtype".equals(kind); final PyDocstringGenerator docstringGenerator = new PyDocstringGenerator(pyFunction); + docstringGenerator.addFirstEmptyLine(); final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction); String name = isReturn ? "" : StringUtil.notNullize(problemElement.getName()); final String typeFromSignature = signature != null ? StringUtil.notNullize(signature.getArgTypeQualifiedName(name)) : ""; diff --git a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java index 66c73bb545ae..dbfa0ed576cb 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java +++ b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java @@ -30,7 +30,6 @@ 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; import com.jetbrains.python.psi.*; @@ -45,30 +44,21 @@ import java.util.List; */ public class PyDocstringGenerator { - private List myParams = Lists.newArrayList(); + private final List myParams = Lists.newArrayList(); private boolean myGenerateReturn; // Updated after buildAndInsert @NotNull 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; + private boolean myAddFirstEmptyLine = false; public PyDocstringGenerator(@NotNull PyDocStringOwner docStringOwner) { myDocStringOwner = docStringOwner; - myDocStringFormat = DocStringUtil.getDocStringFormat(docStringOwner); - final DocStringProvider provider = myDocStringFormat.getProvider(); final PyStringLiteralExpression docStringExpression = myDocStringOwner.getDocStringExpression(); - if (docStringExpression != null) { - myOriginalDocString = provider.parseDocString(docStringExpression); - } - else { - myOriginalDocString = null; - } + myNewMode = docStringExpression == null; } @NotNull @@ -106,6 +96,12 @@ public class PyDocstringGenerator { return this; } + @NotNull + public PyDocstringGenerator addFirstEmptyLine() { + myAddFirstEmptyLine = true; + return this; + } + @NotNull public PyDocstringGenerator forceNewMode() { myNewMode = true; @@ -122,18 +118,38 @@ public class PyDocstringGenerator { 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) { + final String paramName = param.getName(); + final StructuredDocString docString = getStructuredDocString(); + if (StringUtil.isEmpty(paramName) || param.isSelf() || docString != null && docString.getParamTypeSubstring(paramName) != null) { continue; } - String type = signature != null ? signature.getArgTypeQualifiedName(paramName) : null; + final String signatureType = signature != null ? signature.getArgTypeQualifiedName(paramName) : null; + String type = null; + if (signatureType != null) { + type = signatureType; + } + else if (PyCodeInsightSettings.getInstance().INSERT_TYPE_DOCSTUB) { + type = ""; + } withParam(paramName); if (type != null) { withParamTypedByName(paramName, type); } } + if (myGenerateReturn) { + final RaiseVisitor visitor = new RaiseVisitor(); + final PyStatementList statementList = ((PyFunction)myDocStringOwner).getStatementList(); + statementList.accept(visitor); + if (visitor.myHasReturn) { + // will add :return: placeholder in Sphinx/Epydoc docstrings + myParams.add(new DocstringParam("", null, true)); + if (PyCodeInsightSettings.getInstance().INSERT_TYPE_DOCSTUB) { + withReturnValue(""); + } + } + } } + } return this; } @@ -147,6 +163,21 @@ public class PyDocstringGenerator { return myDocStringOwner.getDocStringExpression(); } + @NotNull + private DocStringFormat getDocStringFormat() { + return DocStringUtil.getDocStringFormat(myDocStringOwner); + } + + @Nullable + private StructuredDocString getStructuredDocString() { + final PyStringLiteralExpression expression = getDocStringExpression(); + final DocStringFormat format = getDocStringFormat(); + if (format == DocStringFormat.PLAIN || expression == null) { + return null; + } + return format.getProvider().parseDocString(expression); + } + public static String generateRaiseOrReturn(@NotNull PyFunction element, String offset, String prefix, boolean checkReturn) { final StringBuilder builder = new StringBuilder(); if (checkReturn) { @@ -238,16 +269,18 @@ public class PyDocstringGenerator { } private boolean isNewMode() { - return myNewMode || myOriginalDocString == null; + return myNewMode; } @NotNull private String createDocString() { - if (myDocStringFormat == DocStringFormat.EPYTEXT || myDocStringFormat == DocStringFormat.REST) { - final TagBasedDocStringBuilder builder = new TagBasedDocStringBuilder(myDocStringFormat == DocStringFormat.EPYTEXT ? "@" : ":"); - builder.addEmptyLine(); - String indentation = getDocStringIndentation(); - boolean addedReturn = false; + final String indentation = getDocStringIndentation(); + final DocStringFormat format = getDocStringFormat(); + if (format == DocStringFormat.EPYTEXT || format == DocStringFormat.REST) { + final TagBasedDocStringBuilder builder = new TagBasedDocStringBuilder(format == DocStringFormat.EPYTEXT ? "@" : ":"); + if (myAddFirstEmptyLine) { + builder.addEmptyLine(); + } for (DocstringParam param : myParams) { if (param.isReturnValue()) { if (param.getType() != null) { @@ -256,7 +289,6 @@ public class PyDocstringGenerator { else { builder.addReturnValueDescription(""); } - addedReturn = true; } else { if (param.getType() != null) { @@ -267,18 +299,6 @@ public class PyDocstringGenerator { } } } - if (myGenerateReturn && myDocStringOwner instanceof PyFunction) { - final RaiseVisitor visitor = new RaiseVisitor(); - final PyStatementList statementList = ((PyFunction)myDocStringOwner).getStatementList(); - statementList.accept(visitor); - if (!addedReturn && visitor.myHasReturn) { - builder.addReturnValueDescription(""); - } - if (visitor.myHasRaise) { - builder.addExceptionDescription(visitor.getRaiseTargetText(), ""); - } - } - if (builder.getLines().size() > 1) { return myQuotes + '\n' + builder.buildContent(indentation, true) + '\n' + indentation + myQuotes; } @@ -287,7 +307,7 @@ public class PyDocstringGenerator { } } else { - return ""; + return myQuotes + '\n' + indentation + myQuotes; } } @@ -302,10 +322,12 @@ public class PyDocstringGenerator { @NotNull private String updateDocString() { - if (myDocStringFormat == DocStringFormat.EPYTEXT || myDocStringFormat == DocStringFormat.REST) { - final String prefix = myDocStringFormat == DocStringFormat.EPYTEXT ? "@" : ":"; - //noinspection unchecked - TagBasedDocStringUpdater updater = new TagBasedDocStringUpdater((TagBasedDocString)myOriginalDocString, prefix, getDocStringIndentation()); + final DocStringFormat format = getDocStringFormat(); + if (format == DocStringFormat.EPYTEXT || format == DocStringFormat.REST) { + final String prefix = format == DocStringFormat.EPYTEXT ? "@" : ":"; + //noinspection unchecked,ConstantConditions + TagBasedDocStringUpdater updater = new TagBasedDocStringUpdater((TagBasedDocString)getStructuredDocString(), + prefix, getDocStringIndentation()); for (DocstringParam param : myParams) { if (param.isReturnValue()) { updater.addReturnValue(param.getType()); diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java index 34af5ccd34cb..6fe09413b01a 100644 --- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java @@ -262,8 +262,9 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { 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() + .useTypesFromDebuggerSignature(true) + .withDefaultParameters() .withQuotes(quotes) .forceNewMode() .buildDocString();