void (@NotNull PsiElement element, @NotNull ActionContext context, @NotNull ModPsiUpdater updater) { TextRange selection = context.selection(); final Document document = element.getContainingFile().getViewProvider().getDocument(); if (!selection.isEmpty()) { // does not check if Unicode escape is inside char or string literal (garbage in, garbage out) 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 = indexOfUnicodeEscape(text, anchor + 1); if (index < 0) { break; } replacement.append(text, anchor, index); int hexStart = index + 1; while (text.charAt(hexStart) == 'u') { hexStart++; } anchor = hexStart + 4; final int c = Integer.parseInt(text.substring(hexStart, anchor), 16); replacement.appendCodePoint(c); } replacement.append(text, anchor, textLength); final int start = selection.getStartOffset(); final int end = selection.getEndOffset(); document.replaceString(start, end, replacement); } else { final int lineNumber = document.getLineNumber(context.offset()); final int lineStartOffset = document.getLineStartOffset(lineNumber); final String line = document.getText(new TextRange(lineStartOffset, document.getLineEndOffset(lineNumber))); final int column = context.offset() - lineStartOffset; final int index1 = indexOfUnicodeEscape(line, column); final int index2 = indexOfUnicodeEscape(line, column + 1); // if the caret is between two unicode escapes, replace the one to the right final int escapeStart = index2 == column ? index2 : index1; int hexStart = escapeStart + 1; while (line.charAt(hexStart) == 'u') { hexStart++; } final char c = (char)Integer.parseInt(line.substring(hexStart, hexStart + 4), 16); if (Character.isHighSurrogate(c)) { hexStart += 4; if (line.charAt(hexStart++) == '\\' && line.charAt(hexStart++) == 'u') { while (line.charAt(hexStart) == 'u') hexStart++; final char d = (char)Integer.parseInt(line.substring(hexStart, hexStart + 4), 16); document.replaceString(lineStartOffset + escapeStart, lineStartOffset + hexStart + 4, String.valueOf(new char[] {c, d})); return; } } else if (Character.isLowSurrogate(c)) { if (escapeStart >= 6 && StringUtil.isHexDigit(line.charAt(escapeStart - 1)) && StringUtil.isHexDigit(line.charAt(escapeStart - 2)) && StringUtil.isHexDigit(line.charAt(escapeStart - 3)) && StringUtil.isHexDigit(line.charAt(escapeStart - 4))) { int i = escapeStart - 5; while (i > 0 && line.charAt(i) == 'u') i--; if (line.charAt(i) == '\\' && (i == 0 || line.charAt(i - 1) != '\\')) { final char b = (char)Integer.parseInt(line.substring(escapeStart - 4, escapeStart), 16); document.replaceString(lineStartOffset + i, lineStartOffset + hexStart + 4, String.valueOf(new char[] {b, c})); return; } } } document.replaceString(lineStartOffset + escapeStart, lineStartOffset + hexStart + 4, String.valueOf(c)); } }