IDEA-162289 (Replace unicode escape with character intention doesn't work for surroga...
authorBas Leijdekkers <basleijdekkers@gmail.com>
Mon, 10 Oct 2016 18:51:44 +0000 (20:51 +0200)
committerBas Leijdekkers <basleijdekkers@gmail.com>
Mon, 10 Oct 2016 19:20:33 +0000 (21:20 +0200)
plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java
plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1.java [new file with mode: 0644]
plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs1_after.java [new file with mode: 0644]
plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2.java [new file with mode: 0644]
plugins/IntentionPowerPak/test/com/siyeh/ipp/unicode/unescape/SurrogatePairs2_after.java [new file with mode: 0644]
plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/unicode/UnicodeUnescapeIntentionTest.java

index 0137f987a32e6e3d970d357f5914ade04bd17002..7bd009b720ef4162708213ca954bf35bb321cfa0 100644 (file)
@@ -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 (file)
index 0000000..e990b89
--- /dev/null
@@ -0,0 +1,6 @@
+class SurrogatePairs1 {
+
+  void m() {
+    System.out.println("<caret>\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 (file)
index 0000000..21398d6
--- /dev/null
@@ -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 (file)
index 0000000..adc2f0a
--- /dev/null
@@ -0,0 +1,6 @@
+class SurrogatePairs1 {
+
+  void m() {
+    System.out.println("\uD801\uDC38<caret>");
+  }
+}
\ 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 (file)
index 0000000..b6e311f
--- /dev/null
@@ -0,0 +1,6 @@
+class SurrogatePairs1 {
+
+  void m() {
+    System.out.println("𐐸");
+  }
+}
\ No newline at end of file
index e5d8eff90db622da257e0f1136a9a25fe4fbf0ba..bb68178ed41433aff2ea964f378542018e7fb0b7 100644 (file)
@@ -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(); }