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);
    }
  }
 @Override
 public void visitPyStringLiteralExpression(PyStringLiteralExpression node) {
   final Pair<PsiElement, TextRange> data =
       node.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
   if (data != null) {
     final PsiElement parent = data.getFirst();
     final String text = parent.getText();
     final Pair<String, String> detectedQuotes = PythonStringUtil.getQuotes(text);
     final Pair<String, String> quotes =
         detectedQuotes != null ? detectedQuotes : Pair.create("'", "'");
     final TextRange range = data.getSecond();
     final String substring = range.substring(text);
     myResult.append(quotes.getFirst() + substring + quotes.getSecond());
   } else {
     ASTNode child = node.getNode().getFirstChildNode();
     while (child != null) {
       String text = child.getText();
       if (child.getElementType() == TokenType.WHITE_SPACE) {
         if (text.contains("\n")) {
           if (!text.contains("\\")) {
             myResult.append("\\");
           }
           myResult.append(text);
         }
       } else {
         myResult.append(text);
       }
       child = child.getTreeNext();
     }
   }
 }
 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"));
       }
     }
   }
 }
  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;
  }
  public void performAction(IntroduceOperation operation) {
    final PsiFile file = operation.getFile();
    if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) {
      return;
    }
    final Editor editor = operation.getEditor();
    if (editor.getSettings().isVariableInplaceRenameEnabled()) {
      final TemplateState templateState =
          TemplateManagerImpl.getTemplateState(operation.getEditor());
      if (templateState != null && !templateState.isFinished()) {
        return;
      }
    }

    PsiElement element1 = null;
    PsiElement element2 = null;
    final SelectionModel selectionModel = editor.getSelectionModel();
    boolean singleElementSelection = false;
    if (selectionModel.hasSelection()) {
      element1 = file.findElementAt(selectionModel.getSelectionStart());
      element2 = file.findElementAt(selectionModel.getSelectionEnd() - 1);
      if (element1 instanceof PsiWhiteSpace) {
        int startOffset = element1.getTextRange().getEndOffset();
        element1 = file.findElementAt(startOffset);
      }
      if (element2 instanceof PsiWhiteSpace) {
        int endOffset = element2.getTextRange().getStartOffset();
        element2 = file.findElementAt(endOffset - 1);
      }
      if (element1 == element2) {
        singleElementSelection = true;
      }
    } else {
      if (smartIntroduce(operation)) {
        return;
      }
      final CaretModel caretModel = editor.getCaretModel();
      final Document document = editor.getDocument();
      int lineNumber = document.getLineNumber(caretModel.getOffset());
      if ((lineNumber >= 0) && (lineNumber < document.getLineCount())) {
        element1 = file.findElementAt(document.getLineStartOffset(lineNumber));
        element2 = file.findElementAt(document.getLineEndOffset(lineNumber) - 1);
      }
    }
    final Project project = operation.getProject();
    if (element1 == null || element2 == null) {
      showCannotPerformError(project, editor);
      return;
    }

    element1 = PyRefactoringUtil.getSelectedExpression(project, file, element1, element2);
    if (element1 == null) {
      showCannotPerformError(project, editor);
      return;
    }

    if (singleElementSelection && element1 instanceof PyStringLiteralExpression) {
      final PyStringLiteralExpression literal = (PyStringLiteralExpression) element1;
      // Currently introduce for substrings of a multi-part string literals is not supported
      if (literal.getStringNodes().size() > 1) {
        showCannotPerformError(project, editor);
        return;
      }
      final int offset = element1.getTextOffset();
      final TextRange selectionRange =
          TextRange.create(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
      final TextRange elementRange = element1.getTextRange();
      if (!elementRange.equals(selectionRange) && elementRange.contains(selectionRange)) {
        final TextRange innerRange = literal.getStringValueTextRange();
        final TextRange intersection = selectionRange.shiftRight(-offset).intersection(innerRange);
        final TextRange finalRange = intersection != null ? intersection : selectionRange;
        final String text = literal.getText();
        if (getFormatValueExpression(literal) != null && breaksStringFormatting(text, finalRange)
            || getNewStyleFormatValueExpression(literal) != null
                && breaksNewStyleStringFormatting(text, finalRange)
            || breaksStringEscaping(text, finalRange)) {
          showCannotPerformError(project, editor);
          return;
        }
        element1.putUserData(
            PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange));
      }
    }

    if (!checkIntroduceContext(file, editor, element1)) {
      return;
    }
    operation.setElement(element1);
    performActionOnElement(operation);
  }