@Override
  protected void invoke(@NotNull ActionContext context, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
    TextRange selection = context.selection();
    if (!selection.isEmpty()) {
      // does not check if octal escape is inside char or string literal (garbage in, garbage out)
      final Document document = element.getContainingFile().getFileDocument();
      while (selection.getEndOffset() < document.getTextLength()) {
        char nextChar = document.getCharsSequence().charAt(selection.getEndOffset());
        if (nextChar >= '0' && nextChar <= '7') {
          selection = selection.grown(1);
        } else {
          break;
        }
      }
      final String text = document.getText(selection);
      final int textLength = selection.getLength();
      final StringBuilder replacement = new StringBuilder(textLength);
      int anchor = 0;
      while (true) {
        final int index = indexOfOctalEscape(text, anchor + 1);
        if (index < 0) {
          break;
        }
        replacement.append(text, anchor, index);
        anchor = appendUnicodeEscape(text, index, replacement);
      }
      replacement.append(text, anchor, textLength);
      final int start = selection.getStartOffset();
      final int end = selection.getEndOffset();
      document.replaceString(start, end, replacement);
    }
    else if (element instanceof PsiLiteralExpression literalExpression) {
      final String newLiteralText = buildReplacementText(element, context);
      PsiReplacementUtil.replaceExpression(literalExpression, newLiteralText);
    }
    else if (element instanceof PsiFragment fragment) {
      final String newFragmentText = buildReplacementText(element, context);
      PsiReplacementUtil.replaceFragment(fragment, newFragmentText);
    }
  }
@Override
  protected void invoke(@NotNull ActionContext context, @NotNull PsiElement element, @NotNull ModPsiUpdater updater)