private static void doReplace(@NotNull Project project,
                                @NotNull Editor editor,
                                @NotNull FindModel aModel,
                                int caretOffset,
                                boolean toPrompt,
                                ReplaceDelegate delegate) {
    FindManager findManager = FindManager.getInstance(project);
    final FindModel model = aModel.clone();
    int occurrences = 0;

    List<Pair<TextRange, String>> rangesToChange = new ArrayList<>();

    boolean replaced = false;
    boolean reallyReplaced = false;

    int offset = caretOffset;
    Document document = editor.getDocument();
    while (offset >= 0 && offset < document.getTextLength()) {
      caretOffset = offset;
      FindResult result = doSearch(project, editor, offset, !replaced, model, toPrompt);
      if (result == null) {
        break;
      }
      int startResultOffset = result.getStartOffset();
      model.setFromCursor(true);

      int startOffset = result.getStartOffset();
      int endOffset = result.getEndOffset();
      String foundString = document.getCharsSequence().subSequence(startOffset, endOffset).toString();
      String toReplace;
      try {
        toReplace = findManager.getStringToReplace(foundString, model, startOffset, document.getCharsSequence());
      }
      catch (FindManager.MalformedReplacementStringException e) {
        if (!ApplicationManager.getApplication().isUnitTestMode()) {
          Messages.showErrorDialog(project, e.getMessage(), FindBundle.message("find.replace.invalid.replacement.string.title"));
        }
        break;
      }

      if (toPrompt) {
        int promptResult = findManager.showPromptDialog(model, FindBundle.message("find.replace.dialog.title"));
        if (promptResult == FindManager.PromptResult.SKIP) {
          offset = model.isForward() ? result.getEndOffset() : startResultOffset;
          continue;
        }
        if (promptResult == FindManager.PromptResult.CANCEL) {
          break;
        }
        if (promptResult == FindManager.PromptResult.ALL) {
          toPrompt = false;
        }
      }
      int newOffset;
      if (delegate == null || delegate.shouldReplace(result, toReplace)) {
        if (toPrompt) {
          //[SCR 7258]
          if (!reallyReplaced) {
            editor.getCaretModel().moveToOffset(0);
            reallyReplaced = true;
          }
        }
        TextRange textRange = doReplace(project, document, model, result, toReplace, toPrompt, rangesToChange);
        replaced = true;
        newOffset = model.isForward() ? textRange.getEndOffset() : textRange.getStartOffset();
        if (textRange.isEmpty()) ++newOffset;
        occurrences++;
      }
      else {
        newOffset = model.isForward() ? result.getEndOffset() : result.getStartOffset();
      }

      if (newOffset == offset) {
        newOffset += model.isForward() ? 1 : -1;
      }
      offset = newOffset;
    }

    if (replaced) {
      if (toPrompt) {
        if (caretOffset > document.getTextLength()) {
          caretOffset = document.getTextLength();
        }
        editor.getCaretModel().moveToOffset(caretOffset);
      }
      else {
        CharSequence text = document.getCharsSequence();
        final StringBuilder newText = new StringBuilder(document.getTextLength());
        rangesToChange.sort(Comparator.comparingInt(o -> o.getFirst().getStartOffset()));
        int offsetBefore = 0;
        for (Pair<TextRange, String> pair : rangesToChange) {
          TextRange range = pair.getFirst();
          String replace = pair.getSecond();
          newText.append(text, offsetBefore, range.getStartOffset()); //before change
          if (delegate == null || delegate.shouldReplace(range, replace)) {
            newText.append(replace);
          }
          else {
            newText.append(text.subSequence(range.getStartOffset(), range.getEndOffset()));
          }
          offsetBefore = range.getEndOffset();
          if (offsetBefore < caretOffset) {
            caretOffset += replace.length() - range.getLength();
          }
        }
        newText.append(text, offsetBefore, text.length()); //tail
        if (caretOffset > newText.length()) {
          caretOffset = newText.length();
        }
        final int finalCaretOffset = caretOffset;
        CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
          document.setText(newText);
          editor.getCaretModel().moveToOffset(finalCaretOffset);
          if (model.isGlobal()) {
            editor.getSelectionModel().removeSelection();
          }
        }), null, null);
      }
    }

    ReplaceInProjectManager.reportNumberReplacedOccurrences(project, occurrences);
  }
private static void doReplace(@NotNull Project project,
                                @NotNull Editor editor,
                                @NotNull FindModel aModel,
                                int caretOffset,
                                boolean toPrompt,
                                ReplaceDelegate delegate)