Use documentation from Python stubs for Quick Documentation view (PY-22685)
[idea/community.git] / python / src / com / jetbrains / python / documentation / PyDocumentationBuilder.java
index d0eb4b77dfc11cdc7e996f46a4c2fa685c56e2db..8e64f8d24bceceb45f7e37d433e00d1d0be33045 100644 (file)
@@ -42,6 +42,7 @@ import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
 import com.jetbrains.python.psi.resolve.RootVisitor;
 import com.jetbrains.python.psi.resolve.RootVisitorHost;
 import com.jetbrains.python.psi.types.*;
+import com.jetbrains.python.pyi.PyiUtil;
 import com.jetbrains.python.toolbox.ChainIterable;
 import com.jetbrains.python.toolbox.Maybe;
 import org.jetbrains.annotations.NotNull;
@@ -232,7 +233,7 @@ public class PyDocumentationBuilder {
       final PyCallable getter = property.getGetter().valueOrNull();
       if (getter != null && getter != myElement && getter instanceof PyFunction) {
         // not in getter, getter's doc comment may be useful
-        final PyStringLiteralExpression docstring = ((PyFunction)getter).getDocStringExpression();
+        final PyStringLiteralExpression docstring = getEffectiveDocStringExpression((PyFunction)getter);
         if (docstring != null) {
           myProlog
             .addItem(BR).addWith(TagItalic, $("Copied from getter:")).addItem(BR)
@@ -275,7 +276,7 @@ public class PyDocumentationBuilder {
 
   private void buildFromDocstring(@NotNull final PsiElement elementDefinition, boolean isProperty) {
     PyClass pyClass = null;
-    final PyStringLiteralExpression docStringExpression = ((PyDocStringOwner)elementDefinition).getDocStringExpression();
+    final PyStringLiteralExpression docStringExpression = getEffectiveDocStringExpression((PyDocStringOwner)elementDefinition);
 
     if (elementDefinition instanceof PyClass) {
       pyClass = (PyClass)elementDefinition;
@@ -363,7 +364,7 @@ public class PyDocumentationBuilder {
       PyStringLiteralExpression docstringElement = null;
       PyFunction inherited = null;
       boolean isFromClass = false;
-      if (isConstructor) docstringElement = pyClass.getDocStringExpression();
+      if (isConstructor) docstringElement = getEffectiveDocStringExpression(pyClass);
       if (docstringElement != null) {
         isFromClass = true;
       }
@@ -371,7 +372,7 @@ public class PyDocumentationBuilder {
         inherited = ancestor.findMethodByName(methodName, false, null);
       }
       if (inherited != null) {
-        docstringElement = inherited.getDocStringExpression();
+        docstringElement = getEffectiveDocStringExpression(inherited);
       }
       if (docstringElement != null) {
         final String inheritedDoc = docstringElement.getStringValue();
@@ -414,7 +415,7 @@ public class PyDocumentationBuilder {
       final PyClass objectClass = objectType.getPyClass();
       final PyFunction predefinedMethod = objectClass.findMethodByName(mothodName, false, null);
       if (predefinedMethod != null) {
-        final PyStringLiteralExpression predefinedDocstring = predefinedMethod.getDocStringExpression();
+        final PyStringLiteralExpression predefinedDocstring = getEffectiveDocStringExpression(predefinedMethod);
         final String predefinedDoc = predefinedDocstring != null ? predefinedDocstring.getStringValue() : null;
         if (predefinedDoc != null && predefinedDoc.length() > 1) { // only a real-looking doc string counts
           addFormattedDocString(fun, predefinedDoc, myBody, myBody);
@@ -472,7 +473,7 @@ public class PyDocumentationBuilder {
   private boolean addTypeAndDescriptionFromDocstring(@NotNull final PyNamedParameter parameter) {
     final PyFunction function = PsiTreeUtil.getParentOfType(parameter, PyFunction.class);
     if (function != null) {
-      final String docString = PyPsiUtils.strValue(function.getDocStringExpression());
+      final String docString = PyPsiUtils.strValue(getEffectiveDocStringExpression(function));
       final Pair<String, String> typeAndDescr = getTypeAndDescription(docString, parameter);
 
       final String type = typeAndDescr.first;
@@ -518,7 +519,7 @@ public class PyDocumentationBuilder {
       .addItem(type).addWith(TagBold, $().addWith(TagCode, $(((PyTargetExpression)myElement).getName())))
       .addItem(" of class ").addWith(PythonDocumentationProvider.LinkMyClass, $().addWith(TagCode, $(cls.getName()))).addItem(BR);
 
-    final String docString = ((PyTargetExpression)myElement).getDocStringValue();
+    final String docString = PyPsiUtils.strValue(getEffectiveDocStringExpression((PyTargetExpression)myElement));
     if (docString != null) {
       addFormattedDocString(myElement, docString, myBody, myEpilog);
     }
@@ -583,6 +584,17 @@ public class PyDocumentationBuilder {
     }
   }
 
+  @Nullable
+  static PyStringLiteralExpression getEffectiveDocStringExpression(@NotNull PyDocStringOwner owner) {
+    final PyStringLiteralExpression expression = owner.getDocStringExpression();
+    if (expression != null && StringUtil.isNotEmpty(PyPsiUtils.strValue(expression))) {
+      return expression;
+    }
+    final PsiElement original = PyiUtil.getOriginalElement(owner);
+    final PyDocStringOwner originalOwner = PyUtil.as(original, PyDocStringOwner.class);
+    return originalOwner != null ? originalOwner.getDocStringExpression() : null;
+  }
+
   private static class RootFinder implements RootVisitor {
     private String myResult;
     private final String myPath;