Fix broken tests of docstring generation on enter
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Sun, 23 Aug 2015 21:12:13 +0000 (00:12 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Wed, 2 Sep 2015 11:34:58 +0000 (14:34 +0300)
python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java
python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java
python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java
python/src/com/jetbrains/python/editor/PythonEnterHandler.java

index fb5dff644ec56bac3a10bda414c4607be0f90d0f..475b6f1107390e90232e403a6b2de61e67c5d890 100644 (file)
@@ -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();
       }
index 08ea3fa4a05c8b51dbe04ff0fd69dda32659a9a5..9ec0e61c4bd76ed8f42d8ab8031ad8456a9282d2 100644 (file)
@@ -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)) : "";
index 66c73bb545aed6d158f64b425597686832f9558f..dbfa0ed576cb2787184e2fe3b607b690532d5aa5 100644 (file)
@@ -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<DocstringParam> myParams = Lists.newArrayList();
+  private final List<DocstringParam> 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());
index 34af5ccd34cb0d8e9ceb33bf090693e2e8f9170d..6fe09413b01a9df28b2ccbe987dd993fbf73fd0f 100644 (file)
@@ -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();