@Override
  public void replace(final @NotNull ReplacementInfo info, @NotNull ReplaceOptions options) {
    final PsiElement elementToReplace = StructuralSearchUtil.getPresentableElement(info.getMatch(0));
    if (elementToReplace == null) {
      return;
    }
    final PsiElement elementParent = elementToReplace.getParent();
    String replacementToMake = info.getReplacement();
    final boolean listContext = isListContext(elementToReplace);

    if (elementToReplace instanceof PsiAnnotation && !replacementToMake.isEmpty() &&
        !StringUtil.startsWithChar(replacementToMake, '@')) {
      replacementToMake = "@" + replacementToMake;
    }

    final LanguageFileType fileType = myReplaceOptions.getMatchOptions().getFileType();
    assert fileType != null;
    final PsiElement[] replacements = MatcherImplUtil.createTreeFromText(
      replacementToMake,
      elementToReplace instanceof PsiMember && !isSymbolReplacement(elementToReplace) ?
      PatternTreeContext.Class :
      PatternTreeContext.Block,
      fileType,
      myProject);

    if (elementToReplace instanceof PsiAnnotation && replacements.length == 1) {
      if (replacements[0] instanceof PsiDeclarationStatement declarationStatement &&
          declarationStatement.getFirstChild() instanceof PsiModifierList modifierList) {
        copyUnmatchedElements(elementToReplace, declarationStatement, info);
        for (PsiElement child : modifierList.getChildren()) {
          elementParent.addBefore(child, elementToReplace);
        }
      }
      elementToReplace.delete();
      return;
    }
    if (listContext) {
      if (replacements.length == 1 && replacements[0] instanceof PsiExpressionListStatement statement) {
        PsiElement[] children = statement.getExpressionList().getChildren();
        elementParent.addRangeBefore(children[0], children[children.length - 1], elementToReplace);
      }
      else if (replacements.length > 1) {
        final PsiElement replacement = elementParent.addRangeBefore(replacements[0], replacements[replacements.length - 1], elementToReplace);
        copyUnmatchedElements(elementToReplace, replacement, info);
      }
      else if (replacements.length == 1) {
        PsiElement replacement = getMatchExpr(replacements[0], elementToReplace);
        if (elementToReplace instanceof PsiParameter && replacement instanceof PsiLocalVariable variable) {
          final PsiIdentifier identifier = variable.getNameIdentifier();
          assert identifier != null;
          final String text = variable.getText();

          // chop off unneeded semicolons & initializers
          final String parameterText = text.substring(0, identifier.getStartOffsetInParent() + identifier.getTextLength());
          replacement = JavaPsiFacade.getElementFactory(variable.getProject()).createParameterFromText(parameterText, variable);
        }
        final PsiElement patternElement = patternElements[0];
        if (elementToReplace instanceof PsiAnonymousClass anonymousClass &&
            patternElement instanceof PsiClass && !(patternElement instanceof PsiAnonymousClass) &&
            replacement instanceof PsiClass replacementClass && !(replacement instanceof PsiAnonymousClass)) {
          final PsiExpressionList argumentList = anonymousClass.getArgumentList();
          final PsiElement brace = replacementClass.getLBrace();
          assert brace != null;

          String typeParametersText = "";
          if (!replacementClass.hasTypeParameters()) {
            if (!((PsiClass)patternElement).hasTypeParameters()) {
              final PsiReferenceParameterList parameterList = anonymousClass.getBaseClassReference().getParameterList();
              if (parameterList != null) {
                typeParametersText = parameterList.getText();
              }
            }
          }
          else {
            final PsiTypeParameterList parameterList = replacementClass.getTypeParameterList();
            if (parameterList != null) {
              typeParametersText = parameterList.getText();
            }
          }

          final PsiElementFactory factory = JavaPsiFacade.getElementFactory(elementToReplace.getProject());
          final PsiNewExpression newExpression = (PsiNewExpression)
            factory.createExpressionFromText("new " + replacementClass.getName() + typeParametersText +
                                             (argumentList == null ? "()" : argumentList.getText()) +
                                             replacementClass.getText().substring(brace.getStartOffsetInParent()),
                                             elementToReplace);
          replacement = newExpression.getAnonymousClass();
          assert replacement != null;
        }

        copyUnmatchedElements(elementToReplace, replacement, info);
        replacement = handleSymbolReplacement(replacement, elementToReplace);

        if (replacement instanceof PsiTryStatement tryStatement) {
          final List<? extends PsiElement> unmatchedElements = elementToReplace.getUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY);
          if (unmatchedElements != null && !unmatchedElements.isEmpty()) {
            final PsiElement firstElement = unmatchedElements.get(0);
            if (firstElement instanceof PsiResourceList) addElementAfterAnchor(tryStatement, firstElement, tryStatement.getFirstChild());
            outer:
            for (final PsiElement element : unmatchedElements) {
              if (element instanceof PsiCatchSection catchSection) {
                final PsiCatchSection[] catches = tryStatement.getCatchSections();
                if (catches.length == 0) {
                  addElementAfterAnchor(tryStatement, element, tryStatement.getTryBlock());
                }
                else {
                  final PsiType newType = catchSection.getCatchType();
                  for (PsiCatchSection existingCatch : catches) {
                    final PsiType existingType = existingCatch.getCatchType();
                    if (existingType != null && newType != null) {
                      if (existingType.isAssignableFrom(newType)) {
                        addElementBeforeAnchor(tryStatement, element, existingCatch);
                        continue outer;
                      }
                    }
                  }
                  addElementAfterAnchor(tryStatement, element, catches[catches.length - 1]);
                }
              }
            }
            final PsiElement lastElement = unmatchedElements.get(unmatchedElements.size() - 1);
            if (lastElement instanceof PsiCodeBlock) {
              final PsiElement finallyKeyword = PsiTreeUtil.skipWhitespacesBackward(lastElement);
              assert finallyKeyword != null;
              final PsiElement finallyAnchor = tryStatement.getLastChild();
              addElementAfterAnchor(tryStatement, lastElement, finallyAnchor);
              addElementAfterAnchor(tryStatement, finallyKeyword, finallyAnchor);
            }
          }
        }

        try {
          final PsiElement inserted = elementParent.addBefore(replacement, elementToReplace);

          if (replacement instanceof PsiComment && (elementParent instanceof PsiIfStatement || elementParent instanceof PsiLoopStatement)) {
            elementParent.addAfter(createSemicolon(replacement), inserted);
          }
        }
        catch (IncorrectOperationException e) {
          elementToReplace.replace(replacement);
        }
      }
    }
    else if (replacements.length > 0) {
      PsiElement replacement = ReplacerUtil.copySpacesAndCommentsBefore(elementToReplace, replacements, replacementToMake, elementParent);

      replacement = getMatchExpr(replacement, elementToReplace);

      if (replacement instanceof PsiStatement &&
          !(replacement.getLastChild() instanceof PsiJavaToken) &&
          !(replacement.getLastChild() instanceof PsiComment)
        ) {
        // assert w/o ;
        final PsiElement prevLastChildInParent = replacement.getLastChild().getPrevSibling();

        if (prevLastChildInParent != null) {
          elementParent.addRangeBefore(replacement.getFirstChild(), prevLastChildInParent, elementToReplace);
        }
        else {
          elementParent.addBefore(replacement.getFirstChild(), elementToReplace);
        }

        elementToReplace.getNode().getTreeParent().removeChild(elementToReplace.getNode());
      }
      else {
        copyUnmatchedElements(elementToReplace, replacement, info);

        replacement = handleSymbolReplacement(replacement, elementToReplace);
        elementToReplace.replace(replacement);
      }
    }
    else {
      final PsiElement nextSibling = elementToReplace.getNextSibling();
      elementToReplace.delete();
      if (nextSibling instanceof PsiWhiteSpace && nextSibling.isValid()) {
        nextSibling.delete();
      }
    }

    if (listContext) {
      final int matchSize = info.getMatchesCount();

      for (int i = 0; i < matchSize; ++i) {
        final PsiElement matchElement = info.getMatch(i);
        final PsiElement element = StructuralSearchUtil.getPresentableElement(matchElement);

        if (element == null) continue;
        PsiElement firstToDelete = element;
        PsiElement lastToDelete = element;
        PsiElement prevSibling = element.getPrevSibling();
        PsiElement nextSibling = element.getNextSibling();

        if (prevSibling instanceof PsiWhiteSpace) {
          firstToDelete = prevSibling;
          prevSibling = prevSibling.getPrevSibling();
        }
        else if (prevSibling == null && nextSibling instanceof PsiWhiteSpace) {
          lastToDelete = nextSibling;
        }

        if (element instanceof PsiExpression) {
          final PsiElement parent = element.getParent().getParent();
          if ((parent instanceof PsiCall || parent instanceof PsiAnonymousClass) && PsiUtil.isJavaToken(prevSibling, JavaTokenType.COMMA)) {
            firstToDelete = prevSibling;
          }
        }
        else if (element instanceof PsiParameter && PsiUtil.isJavaToken(prevSibling, JavaTokenType.COMMA)) {
          firstToDelete = prevSibling;
        }
        else if (element instanceof PsiField) {
          while (PsiUtil.isJavaToken(nextSibling, JavaTokenType.COMMA)) {
            lastToDelete = PsiTreeUtil.skipWhitespacesForward(nextSibling);
            nextSibling = PsiTreeUtil.skipWhitespacesForward(lastToDelete);
          }
        }

        element.getParent().deleteChildRange(firstToDelete, lastToDelete);
      }
    }
  }
@Override
  public void replace(final @NotNull ReplacementInfo info, @NotNull ReplaceOptions options)