diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java index 0137f987a32e..7bd009b720ef 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ public class UnicodeUnescapeIntention extends Intention { protected void processIntention(Editor editor, @NotNull PsiElement element) { final SelectionModel selectionModel = editor.getSelectionModel(); if (selectionModel.hasSelection()) { - // does not check if octal escape is inside char or string literal (garbage in, garbage out) + // does not check if Unicode escape is inside char or string literal (garbage in, garbage out) final Document document = editor.getDocument(); final int start = selectionModel.getSelectionStart(); final int end = selectionModel.getSelectionEnd(); @@ -74,13 +74,36 @@ public class UnicodeUnescapeIntention extends Intention { final int column = caretModel.getLogicalPosition().column; final int index1 = indexOfUnicodeEscape(line, column); final int index2 = indexOfUnicodeEscape(line, column + 1); - final int escapeStart = index2 == column ? index2 : index1; // if caret is between two unicode escape, replace the right one + // 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 int c = Integer.parseInt(line.substring(hexStart, hexStart + 4), 16); - document.replaceString(lineStartOffset + escapeStart, lineStartOffset + hexStart + 4, String.valueOf((char) c)); + 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)); } } diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1.java new file mode 100644 index 000000000000..e990b8940140 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1.java @@ -0,0 +1,6 @@ +class SurrogatePairs1 { + + void m() { + System.out.println("\uD801\uDC37"); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1_after.java new file mode 100644 index 000000000000..21398d675adf --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1_after.java @@ -0,0 +1,6 @@ +class SurrogatePairs1 { + + void m() { + System.out.println("𐐷"); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2.java new file mode 100644 index 000000000000..adc2f0abf415 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2.java @@ -0,0 +1,6 @@ +class SurrogatePairs1 { + + void m() { + System.out.println("\uD801\uDC38"); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2_after.java new file mode 100644 index 000000000000..b6e311f07cbb --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2_after.java @@ -0,0 +1,6 @@ +class SurrogatePairs1 { + + void m() { + System.out.println("𐐸"); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/unicode/UnicodeUnescapeIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/unicode/UnicodeUnescapeIntentionTest.java index e5d8eff90db6..bb68178ed414 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/unicode/UnicodeUnescapeIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/unicode/UnicodeUnescapeIntentionTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.siyeh.ipp.unicode; import com.siyeh.IntentionPowerPackBundle; @@ -10,6 +25,8 @@ public class UnicodeUnescapeIntentionTest extends IPPTestCase { public void testSimple() { doTest(); } public void testSelection() { doTest(); } + public void testSurrogatePairs1() { doTest(); } + public void testSurrogatePairs2() { doTest(); } public void testNoException() { assertIntentionNotAvailable(); } public void testU() { assertIntentionNotAvailable(); }