private void buildFromDocstring(@NotNull final PsiElement elementDefinition, boolean isProperty) {
    PyClass pyClass = null;
    final PyStringLiteralExpression docStringExpression =
        ((PyDocStringOwner) elementDefinition).getDocStringExpression();

    if (elementDefinition instanceof PyClass) {
      pyClass = (PyClass) elementDefinition;
      myBody.add(PythonDocumentationProvider.describeDecorators(pyClass, TagItalic, BR, LCombUp));
      myBody.add(PythonDocumentationProvider.describeClass(pyClass, TagBold, true, false));
    } else if (elementDefinition instanceof PyFunction) {
      PyFunction pyFunction = (PyFunction) elementDefinition;
      if (!isProperty) {
        pyClass = pyFunction.getContainingClass();
        if (pyClass != null) {
          myBody
              .addWith(
                  TagSmall, PythonDocumentationProvider.describeClass(pyClass, TagCode, true, true))
              .addItem(BR)
              .addItem(BR);
        }
      }
      myBody
          .add(PythonDocumentationProvider.describeDecorators(pyFunction, TagItalic, BR, LCombUp))
          .add(PythonDocumentationProvider.describeFunction(pyFunction, TagBold, LCombUp));
      if (docStringExpression == null) {
        addInheritedDocString(pyFunction, pyClass);
      }
    } else if (elementDefinition instanceof PyFile) {
      addModulePath((PyFile) elementDefinition);
    }
    if (docStringExpression != null) {
      myBody.addItem(BR);
      addFormattedDocString(myElement, docStringExpression.getStringValue(), myBody, myEpilog);
    }
  }
  /**
   * Adds type and description representation from function docstring
   *
   * @param parameter parameter of a function
   * @return true if type from docstring was added
   */
  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 Pair<String, String> typeAndDescr = getTypeAndDescription(docString, parameter);

      final String type = typeAndDescr.first;
      final String description = typeAndDescr.second;

      if (type != null) {
        final PyType pyType = PyTypeParser.getTypeByName(parameter, type);
        if (pyType instanceof PyClassType) {
          myBody
              .addItem(": ")
              .addWith(
                  new LinkWrapper(PythonDocumentationProvider.LINK_TYPE_PARAM),
                  $(pyType.getName()));
        } else {
          myBody.addItem(": ").addItem(type);
        }
      }

      if (description != null) {
        myEpilog.addItem(BR).addItem(description);
      }

      return type != null;
    }

    return false;
  }
 private void addModulePath(PyFile followed) {
   // what to prepend to a module description?
   final VirtualFile file = followed.getVirtualFile();
   if (file == null) {
     myProlog.addWith(TagSmall, $(PyBundle.message("QDOC.module.path.unknown")));
   } else {
     final String path = file.getPath();
     RootFinder finder = new RootFinder(path);
     RootVisitorHost.visitRoots(followed, finder);
     final String rootPath = finder.getResult();
     if (rootPath != null) {
       String afterPart = path.substring(rootPath.length());
       myProlog.addWith(TagSmall, $(rootPath).addWith(TagBold, $(afterPart)));
     } else {
       myProlog.addWith(TagSmall, $(path));
     }
   }
 }
  private static void addFormattedDocString(
      PsiElement element,
      @NotNull String docstring,
      ChainIterable<String> formattedOutput,
      ChainIterable<String> unformattedOutput) {
    final Project project = element.getProject();

    List<String> formatted = PyStructuredDocstringFormatter.formatDocstring(element, docstring);
    if (formatted != null) {
      unformattedOutput.add(formatted);
      return;
    }

    boolean isFirstLine;
    final List<String> result = new ArrayList<String>();
    String[] lines = removeCommonIndentation(docstring);

    // reconstruct back, dropping first empty fragment as needed
    isFirstLine = true;
    int tabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(PythonFileType.INSTANCE);
    for (String line : lines) {
      if (isFirstLine && ourSpacesPattern.matcher(line).matches())
        continue; // ignore all initial whitespace
      if (isFirstLine) {
        isFirstLine = false;
      } else {
        result.add(BR);
      }
      int leadingTabs = 0;
      while (leadingTabs < line.length() && line.charAt(leadingTabs) == '\t') {
        leadingTabs++;
      }
      if (leadingTabs > 0) {
        line = StringUtil.repeatSymbol(' ', tabSize * leadingTabs) + line.substring(leadingTabs);
      }
      result.add(combUp(line));
    }
    formattedOutput.add(result);
  }
  private void buildFromParameter(
      @NotNull final TypeEvalContext context,
      @Nullable final PsiElement outerElement,
      @NotNull final PsiElement elementDefinition) {
    myBody.addItem(combUp("Parameter " + PyUtil.getReadableRepr(elementDefinition, false)));
    boolean typeFromDocstringAdded =
        addTypeAndDescriptionFromDocstring((PyNamedParameter) elementDefinition);
    if (outerElement instanceof PyExpression) {
      PyType type = context.getType((PyExpression) outerElement);
      if (type != null) {
        String typeString = null;
        if (type instanceof PyDynamicallyEvaluatedType) {
          if (!typeFromDocstringAdded) {
            typeString = "\nDynamically inferred type: ";
          }
        } else {
          if (outerElement.getReference() != null) {
            PsiElement target = outerElement.getReference().resolve();

            if (target instanceof PyTargetExpression) {
              final String targetName = ((PyTargetExpression) target).getName();
              if (targetName != null
                  && targetName.equals(((PyNamedParameter) elementDefinition).getName())) {
                typeString = "\nReassigned value has type: ";
              }
            }
          }
        }
        if (typeString == null && !typeFromDocstringAdded) {
          typeString = "\nInferred type: ";
        }
        if (typeString != null) {
          myBody.addItem(combUp(typeString));
          PythonDocumentationProvider.describeTypeWithLinks(
              myBody, elementDefinition, type, context);
        }
      }
    }
  }
 @Nullable
 private PsiElement resolveToDocStringOwner() {
   // here the ^Q target is already resolved; the resolved element may point to intermediate
   // assignments
   if (myElement instanceof PyTargetExpression) {
     final String targetName = myElement.getText();
     myReassignmentChain.addWith(
         TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", targetName)).addItem(BR));
     final PyExpression assignedValue = ((PyTargetExpression) myElement).findAssignedValue();
     if (assignedValue instanceof PyReferenceExpression) {
       final PsiElement resolved = resolveWithoutImplicits((PyReferenceExpression) assignedValue);
       if (resolved != null) {
         return resolved;
       }
     }
     return assignedValue;
   }
   if (myElement instanceof PyReferenceExpression) {
     myReassignmentChain.addWith(
         TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", myElement.getText())).addItem(BR));
     return resolveWithoutImplicits((PyReferenceExpression) myElement);
   }
   // it may be a call to a standard wrapper
   if (myElement instanceof PyCallExpression) {
     final PyCallExpression call = (PyCallExpression) myElement;
     final Pair<String, PyFunction> wrapInfo =
         PyCallExpressionHelper.interpretAsModifierWrappingCall(call, myOriginalElement);
     if (wrapInfo != null) {
       String wrapperName = wrapInfo.getFirst();
       PyFunction wrappedFunction = wrapInfo.getSecond();
       myReassignmentChain.addWith(
           TagSmall, $(PyBundle.message("QDOC.wrapped.in.$0", wrapperName)).addItem(BR));
       return wrappedFunction;
     }
   }
   return myElement;
 }
  public PyDocumentationBuilder(PsiElement element, PsiElement originalElement) {
    myElement = element;
    myOriginalElement = originalElement;
    myResult = new ChainIterable<String>();
    myProlog = new ChainIterable<String>();
    myBody = new ChainIterable<String>();
    myEpilog = new ChainIterable<String>();

    myResult
        .add(myProlog)
        .addWith(TagCode, myBody)
        .add(myEpilog); // pre-assemble; then add stuff to individual cats as needed
    myResult = wrapInTag("html", wrapInTag("body", myResult));
    myReassignmentChain = new ChainIterable<String>();
  }
 private void buildForKeyword(@NotNull final String name) {
   try {
     final FileReader reader =
         new FileReader(PythonHelpersLocator.getHelperPath("/tools/python_keywords/" + name));
     try {
       final String text = FileUtil.loadTextAndClose(reader);
       myEpilog.addItem(text);
     } catch (IOException ignored) {
     } finally {
       try {
         reader.close();
       } catch (IOException ignored) {
       }
     }
   } catch (FileNotFoundException ignored) {
   }
 }
  private void buildFromAttributeDoc() {
    PyClass cls = PsiTreeUtil.getParentOfType(myElement, PyClass.class);
    assert cls != null;
    String type =
        PyUtil.isInstanceAttribute((PyExpression) myElement)
            ? "Instance attribute "
            : "Class attribute ";
    myProlog
        .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();
    if (docString != null) {
      addFormattedDocString(myElement, docString, myBody, myEpilog);
    }
  }
 private void addPredefinedMethodDoc(PyFunction fun, String mothodName) {
   PyClassType objectType =
       PyBuiltinCache.getInstance(fun)
           .getObjectType(); // old- and new-style classes share the __xxx__ stuff
   if (objectType != null) {
     PyClass objectClass = objectType.getPyClass();
     PyFunction predefinedMethod = objectClass.findMethodByName(mothodName, false);
     if (predefinedMethod != null) {
       PyStringLiteralExpression predefinedDocstring = predefinedMethod.getDocStringExpression();
       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);
         myEpilog.addItem(BR).addItem(BR).addItem(PyBundle.message("QDOC.copied.from.builtin"));
       }
     }
   }
 }
  public String build() {
    final TypeEvalContext context =
        TypeEvalContext.userInitiated(myElement.getProject(), myElement.getContainingFile());
    final PsiElement outerElement =
        myOriginalElement != null ? myOriginalElement.getParent() : null;

    final PsiElement elementDefinition = resolveToDocStringOwner();
    final boolean isProperty = buildFromProperty(elementDefinition, outerElement, context);

    if (myProlog.isEmpty() && !isProperty && !isAttribute()) {
      myProlog.add(myReassignmentChain);
    }

    if (elementDefinition instanceof PyDocStringOwner) {
      buildFromDocstring(elementDefinition, isProperty);
    } else if (isAttribute()) {
      buildFromAttributeDoc();
    } else if (elementDefinition instanceof PyNamedParameter) {
      buildFromParameter(context, outerElement, elementDefinition);
    } else if (elementDefinition != null && outerElement instanceof PyReferenceExpression) {
      myBody.addItem(combUp("\nInferred type: "));
      PythonDocumentationProvider.describeExpressionTypeWithLinks(
          myBody, (PyReferenceExpression) outerElement, context);
    }

    if (elementDefinition != null
        && PythonDialectsTokenSetProvider.INSTANCE
            .getKeywordTokens()
            .contains(elementDefinition.getNode().getElementType())) {
      buildForKeyword(elementDefinition.getText());
    }
    if (myBody.isEmpty() && myEpilog.isEmpty()) {
      return null; // got nothing substantial to say!
    } else {
      return myResult.toString();
    }
  }
  private void addInheritedDocString(
      @NotNull final PyFunction pyFunction, @Nullable final PyClass pyClass) {
    boolean notFound = true;
    final String methodName = pyFunction.getName();
    if (pyClass == null || methodName == null) {
      return;
    }
    final boolean isConstructor = PyNames.INIT.equals(methodName);
    Iterable<PyClass> classes = pyClass.getAncestorClasses(null);
    if (isConstructor) {
      // look at our own class again and maybe inherit class's doc
      classes = new ChainIterable<PyClass>(pyClass).add(classes);
    }
    for (PyClass ancestor : classes) {
      PyStringLiteralExpression docstringElement = null;
      PyFunction inherited = null;
      boolean isFromClass = false;
      if (isConstructor) docstringElement = pyClass.getDocStringExpression();
      if (docstringElement != null) {
        isFromClass = true;
      } else {
        inherited = ancestor.findMethodByName(methodName, false);
      }
      if (inherited != null) {
        docstringElement = inherited.getDocStringExpression();
      }
      if (docstringElement != null) {
        final String inheritedDoc = docstringElement.getStringValue();
        if (inheritedDoc.length() > 1) {
          myEpilog.addItem(BR).addItem(BR);
          String ancestor_name = ancestor.getName();
          String marker =
              (pyClass == ancestor)
                  ? PythonDocumentationProvider.LINK_TYPE_CLASS
                  : PythonDocumentationProvider.LINK_TYPE_PARENT;
          final String ancestor_link =
              $().addWith(new LinkWrapper(marker + ancestor_name), $(ancestor_name)).toString();
          if (isFromClass) {
            myEpilog.addItem(PyBundle.message("QDOC.copied.from.class.$0", ancestor_link));
          } else {
            myEpilog.addItem(PyBundle.message("QDOC.copied.from.$0.$1", ancestor_link, methodName));
          }
          myEpilog.addItem(BR).addItem(BR);
          ChainIterable<String> formatted = new ChainIterable<String>();
          ChainIterable<String> unformatted = new ChainIterable<String>();
          addFormattedDocString(pyFunction, inheritedDoc, formatted, unformatted);
          myEpilog.addWith(TagCode, formatted).add(unformatted);
          notFound = false;
          break;
        }
      }
    }

    if (notFound) {
      // above could have not worked because inheritance is not searched down to 'object'.
      // for well-known methods, copy built-in doc string.
      // TODO: also handle predefined __xxx__ that are not part of 'object'.
      if (PyNames.UnderscoredAttributes.contains(methodName)) {
        addPredefinedMethodDoc(pyFunction, methodName);
      }
    }
  }
  private boolean buildFromProperty(
      PsiElement elementDefinition,
      @Nullable final PsiElement outerElement,
      @NotNull final TypeEvalContext context) {
    if (myOriginalElement == null) {
      return false;
    }
    final String elementName = myOriginalElement.getText();
    if (!PyNames.isIdentifier(elementName)) {
      return false;
    }
    if (!(outerElement instanceof PyQualifiedExpression)) {
      return false;
    }
    final PyExpression qualifier = ((PyQualifiedExpression) outerElement).getQualifier();
    if (qualifier == null) {
      return false;
    }
    final PyType type = context.getType(qualifier);
    if (!(type instanceof PyClassType)) {
      return false;
    }
    final PyClass cls = ((PyClassType) type).getPyClass();
    final Property property = cls.findProperty(elementName, true, null);
    if (property == null) {
      return false;
    }

    final AccessDirection direction = AccessDirection.of((PyElement) outerElement);
    final Maybe<PyCallable> accessor = property.getByDirection(direction);
    myProlog
        .addItem("property ")
        .addWith(TagBold, $().addWith(TagCode, $(elementName)))
        .addItem(" of ")
        .add(PythonDocumentationProvider.describeClass(cls, TagCode, true, true));
    if (accessor.isDefined() && property.getDoc() != null) {
      myBody.addItem(": ").addItem(property.getDoc()).addItem(BR);
    } else {
      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();
        if (docstring != null) {
          myProlog
              .addItem(BR)
              .addWith(TagItalic, $("Copied from getter:"))
              .addItem(BR)
              .addItem(docstring.getStringValue());
        }
      }
      myBody.addItem(BR);
    }
    myBody.addItem(BR);
    if (accessor.isDefined() && accessor.value() == null) elementDefinition = null;
    final String accessorKind = getAccessorKind(direction);
    if (elementDefinition != null) {
      myEpilog.addWith(TagSmall, $(BR, BR, accessorKind, " of property")).addItem(BR);
    }

    if (!(elementDefinition instanceof PyDocStringOwner)) {
      myBody
          .addWith(
              TagItalic,
              elementDefinition != null ? $("Declaration: ") : $(accessorKind + " is not defined."))
          .addItem(BR);
      if (elementDefinition != null) {
        myBody.addItem(combUp(PyUtil.getReadableRepr(elementDefinition, false)));
      }
    }
    return true;
  }