private void buildFromDocstring(@NotNull final PyDocStringOwner elementDefinition, boolean isProperty) {
    final PyStringLiteralExpression ownDocstring = getEffectiveDocStringExpression(elementDefinition);
    final PyStringLiteralExpression effectiveDocstring = modifyDocStringByOwnerType(ownDocstring, elementDefinition, isProperty);

    if (PyUtil.isTopLevel(elementDefinition)) {
      final PsiFile containing = elementDefinition.getContainingFile();
      if (containing instanceof PyFile) {
        final HtmlChunk linkToModule = getLinkToModule((PyFile)containing);
        if (linkToModule != null) {
          myProlog.append(BOTTOM_ELEMENT
                            .children(
                              HtmlChunk.icon("AllIcons.Nodes.Package", AllIcons.Nodes.Package),
                              HtmlChunk.nbsp(),
                              linkToModule.code()
                            ));
        }
      }
    }

    if (elementDefinition instanceof PyClass pyClass) {
      myBody.append(PythonDocumentationProvider.describeDecorators(pyClass, HtmlChunk.br()));
      myBody.append(PythonDocumentationProvider.describeClass(pyClass, myContext));
      if (effectiveDocstring != null) {
        // add class attributes described either in the init doc or class doc
        addAttributesSection(effectiveDocstring);

        if (effectiveDocstring == ownDocstring) {
          final PyFunction init = pyClass.findMethodByName(PyNames.INIT, false, myContext);
          if (init != null) {
            // add init parameters described in the class doc
            addFunctionSpecificSections(effectiveDocstring, init);
          }
        }
      }
    }
    else if (elementDefinition instanceof PyFunction pyFunction) {
      myBody.append(PythonDocumentationProvider.describeDecorators(pyFunction, HtmlChunk.br()));
      myBody.append(PythonDocumentationProvider.describeFunction(pyFunction, myContext, false));

      final PyClass pyClass = pyFunction.getContainingClass();
      if (!isProperty && pyClass != null) {
        final HtmlChunk link = getLinkToClass(pyClass, true);
        if (link != null) {
          myProlog.append(BOTTOM_ELEMENT
                            .children(
                              HtmlChunk.icon("AllIcons.Nodes.Class", AllIcons.Nodes.Class),
                              HtmlChunk.nbsp(),
                              link.code()
                            ));
        }
      }
      if (effectiveDocstring != null) {
        addFunctionSpecificSections(effectiveDocstring, pyFunction);
        // if function is init without doc we will take attributes from the class doc
        if (effectiveDocstring != ownDocstring && PyUtil.isInitOrNewMethod(pyFunction)) {
          addAttributesSection(effectiveDocstring);
        }
      }
    }
    else if (elementDefinition instanceof PyFile pyFile) {
      addModulePath(pyFile);
    }
    else if (elementDefinition instanceof PyTargetExpression target) {
      addTargetDocumentation(target, isProperty);
    }

    final String formatterInput = effectiveDocstring != null && !isProperty ? effectiveDocstring.getStringValue() : null;

    if (StringUtil.isEmpty(formatterInput) && myFormatterFragments.isEmpty()) return;

    final DocstringFormatterRequest output = PyStructuredDocstringFormatter.formatDocstring(myElement, new DocstringFormatterRequest(
      StringUtil.notNullize(formatterInput), myFormatterFragments), List.of(PyStructuredDocstringFormatter.FORMATTER_FRAGMENTS_FLAG));

    if (output != null) {
      myContent.appendRaw(output.getBody());
      fillFormattedSections(output.getFragments());
    }
    else {
      // if docstring turned out PLAIN, but suddenly we have fragments or body to handle
      if (effectiveDocstring != null) {
        myContent.append(updateLines(myElement, Strings.notNullize(formatterInput)));
      }
      fillFormattedSections(myFormatterFragments);
    }
  }
private void buildFromDocstring(@NotNull final PyDocStringOwner elementDefinition, boolean isProperty)