[core] TextWithMnemonic: support Japanese-style mnemonics (as suffix); tests (IDEA...
authorTagir Valeev <Tagir.Valeev@jetbrains.com>
Tue, 11 Aug 2020 08:16:31 +0000 (15:16 +0700)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Tue, 11 Aug 2020 10:02:03 +0000 (10:02 +0000)
GitOrigin-RevId: 66040ba78f713df705f89caa1b18a7f579c43d56

platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java
platform/platform-impl/src/com/intellij/ui/popup/ActionStepBuilder.java
platform/util/testSrc/com/intellij/util/text/TextWithMnemonicTest.java [new file with mode: 0644]
platform/util/ui/src/com/intellij/openapi/util/text/TextWithMnemonic.java

index ec1df0ed64eed519ddd0234fad236bc081e98be0..4a5a7bead57733a9fa2d7a3c65039e85d4e3dabd 100644 (file)
@@ -226,17 +226,6 @@ public final class Presentation implements Cloneable {
     setTextWithMnemonic(presentation.getTextWithPossibleMnemonic());
   }
 
-  public static String restoreTextWithMnemonic(@Nullable String text, final int mnemonic) {
-    if (text == null) return null;
-    TextWithMnemonic textWithMnemonic = TextWithMnemonic.fromPlainText(text);
-    for (int i = 0; i < text.length(); i++) {
-      if (Character.toUpperCase(text.charAt(i)) == mnemonic) {
-        return textWithMnemonic.setMnemonicAt(i).toString();
-      }
-    }
-    return textWithMnemonic.toString();
-  }
-
   public @ActionDescription String getDescription() {
     return myDescriptionSupplier.get();
   }
index 414fd15a265ce2973853d4d1d749c345f8692d27..a4905949f83d8406a88561fb540b00e9243ca8aa 100644 (file)
@@ -6,6 +6,7 @@ import com.intellij.openapi.actionSystem.impl.MenuItemPresentationFactory;
 import com.intellij.openapi.actionSystem.impl.PresentationFactory;
 import com.intellij.openapi.actionSystem.impl.Utils;
 import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.util.text.TextWithMnemonic;
 import com.intellij.ui.SizedIcon;
 import com.intellij.util.ObjectUtils;
 import com.intellij.util.ui.EmptyIcon;
@@ -19,8 +20,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
-import static com.intellij.openapi.actionSystem.Presentation.restoreTextWithMnemonic;
-
 class ActionStepBuilder {
   private final List<PopupFactoryImpl.ActionItem> myListModel;
   private final DataContext myDataContext;
@@ -145,7 +144,9 @@ class ActionStepBuilder {
         myCurrentNumber++;
       }
       else if (myHonorActionMnemonics) {
-        text = restoreTextWithMnemonic(text, action.getTemplatePresentation().getMnemonic());
+        if (text != null) {
+          text = TextWithMnemonic.fromPlainText(text, (char)action.getTemplatePresentation().getMnemonic()).toString();
+        }
       }
 
       boolean hideIcon = Boolean.TRUE.equals(presentation.getClientProperty(MenuItemPresentationFactory.HIDE_ICON));
diff --git a/platform/util/testSrc/com/intellij/util/text/TextWithMnemonicTest.java b/platform/util/testSrc/com/intellij/util/text/TextWithMnemonicTest.java
new file mode 100644 (file)
index 0000000..37c9209
--- /dev/null
@@ -0,0 +1,67 @@
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.util.text;
+
+import com.intellij.openapi.util.text.TextWithMnemonic;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TextWithMnemonicTest {
+  @Test
+  public void noMnemonic() {
+    TextWithMnemonic hello = TextWithMnemonic.fromPlainText("hello");
+    assertEquals("hello", hello.getText());
+    assertEquals("hello", hello.toString());
+    assertEquals(0, hello.getMnemonic());
+    assertEquals(-1, hello.getMnemonicIndex());
+  }
+
+  @Test
+  public void mnemonicInString() {
+    TextWithMnemonic hello = TextWithMnemonic.fromPlainText("hello", 'e');
+    assertEquals("hello", hello.getText());
+    assertEquals("h_ello", hello.toString());
+    assertEquals('E', hello.getMnemonic());
+    assertEquals(1, hello.getMnemonicIndex());
+  }
+
+  @Test
+  public void parse() {
+    TextWithMnemonic hello = TextWithMnemonic.parse("h&ello");
+    assertEquals("hello", hello.getText());
+    assertEquals("h_ello", hello.toString());
+    assertEquals('E', hello.getMnemonic());
+    assertEquals(1, hello.getMnemonicIndex());
+  }
+
+  @Test
+  public void parseUnderscore() {
+    TextWithMnemonic hello = TextWithMnemonic.parse("h_ello");
+    assertEquals("hello", hello.getText());
+    assertEquals("h_ello", hello.toString());
+    assertEquals('E', hello.getMnemonic());
+    assertEquals(1, hello.getMnemonicIndex());
+  }
+
+  @Test
+  public void parseJapanese() {
+    TextWithMnemonic hello = TextWithMnemonic.parse("hello(&H)");
+    assertEquals("hello", hello.getText());
+    assertEquals("hello(_H)", hello.toString());
+    assertEquals('H', hello.getMnemonic());
+    assertEquals(6, hello.getMnemonicIndex());
+  }
+  
+  @Test
+  public void append() {
+    assertEquals("H_ello world!", TextWithMnemonic.parse("H&ello").append(" world!").toString());
+    assertEquals("Hello _world!(H)", TextWithMnemonic.parse("Hello(&H)").append(" world!").toString());
+  }
+  
+  @Test
+  public void replaceFirst() {
+    assertEquals("_Hello wonderful world!", TextWithMnemonic.parse("&Hello {0} world!").replaceFirst("{0}", "wonderful").toString());
+    assertEquals("Hello wonderful _world!", TextWithMnemonic.parse("Hello {0} &world!").replaceFirst("{0}", "wonderful").toString());
+    assertEquals("Hello wonderful world!(_W)", TextWithMnemonic.parse("Hello {0} world!(&W)").replaceFirst("{0}", "wonderful").toString());
+  }
+}
index 996fc9e003c2fe8900ecac6d4eb32c992a6210cf..2755386cdde10579fdc34f1e7cf2466236a2bcd6 100644 (file)
@@ -15,11 +15,19 @@ public final class TextWithMnemonic {
   public static final Pattern MNEMONIC = Pattern.compile(" ?\\(_?[A-Z]\\)");
 
   @NotNull private final String myText;
+  /**
+   * Mnemonic index (-1 = no mnemonic)
+   */
   private final int myMnemonicIndex;
+  /**
+   * A text that should be appended to myText to display a mnemonic
+   */
+  private final String myMnemonicSuffix;
 
-  private TextWithMnemonic(@NotNull String text, int mnemonicIndex) {
+  private TextWithMnemonic(@NotNull String text, int mnemonicIndex, String mnemonicSuffix) {
     myText = StringUtil.internEmptyString(text);
     myMnemonicIndex = mnemonicIndex;
+    myMnemonicSuffix = mnemonicSuffix;
   }
 
   /**
@@ -34,7 +42,7 @@ public final class TextWithMnemonic {
    * @return a mnemonic character (upper-cased) if mnemonic is set; 0 otherwise
    */
   public int getMnemonic() {
-    return hasMnemonic() ? Character.toUpperCase(myText.charAt(myMnemonicIndex)) : 0;
+    return hasMnemonic() ? Character.toUpperCase((myText + myMnemonicSuffix).charAt(myMnemonicIndex)) : 0;
   }
 
   /**
@@ -72,10 +80,10 @@ public final class TextWithMnemonic {
    * @return a TextWithMnemonic object with mnemonic set at given index
    */
   public TextWithMnemonic setMnemonicAt(int index) {
-    if (index < 0 || index >= myText.length()) {
+    if (index < 0 || index >= myText.length() + myMnemonicSuffix.length()) {
       throw new IndexOutOfBoundsException(String.valueOf(index));
     }
-    return index == myMnemonicIndex ? this : new TextWithMnemonic(myText, index);
+    return index == myMnemonicIndex ? this : new TextWithMnemonic(myText, index, myMnemonicSuffix);
   }
 
   /**
@@ -85,7 +93,7 @@ public final class TextWithMnemonic {
    * @return TextWithMnemonic object which text is the concatenation of this object text and supplied text.
    */
   public TextWithMnemonic append(@NotNull String textToAppend) {
-    return new TextWithMnemonic(myText + textToAppend, myMnemonicIndex);
+    return new TextWithMnemonic(myText + textToAppend, myMnemonicIndex, myMnemonicSuffix);
   }
 
   /**
@@ -105,7 +113,7 @@ public final class TextWithMnemonic {
     int resultIndex = myMnemonicIndex < index ? myMnemonicIndex : 
                       myMnemonicIndex >= index + target.length() ? myMnemonicIndex - target.length() + replacement.length() :
                       -1;
-    return new TextWithMnemonic(resultText, resultIndex);
+    return new TextWithMnemonic(resultText, resultIndex, myMnemonicSuffix);
   }
 
   /**
@@ -116,7 +124,26 @@ public final class TextWithMnemonic {
   @NotNull
   @Contract(pure = true)
   public static TextWithMnemonic fromPlainText(@NotNull String text) {
-    return new TextWithMnemonic(text, -1);
+    return new TextWithMnemonic(text, -1, "");
+  }
+
+  /**
+   * Creates a TextWithMnemonic object from a plain text without mnemonic.
+   * @param text a plain text to create a TextWithMnemonic object from
+   * @param mnemonicChar mnemonic character
+   * @return new TextWithMnemonic object which has given mnemonic character. 
+   * If the text doesn't contain the supplied character then mnemonicChar is appended in parentheses.
+   */
+  @NotNull
+  @Contract(pure = true)
+  public static TextWithMnemonic fromPlainText(@NotNull String text, char mnemonicChar) {
+    mnemonicChar = Character.toUpperCase(mnemonicChar);
+    for (int i = 0; i < text.length(); i++) {
+      if (Character.toUpperCase(text.charAt(i)) == mnemonicChar) {
+        return new TextWithMnemonic(text, i, "");
+      }
+    }
+    return new TextWithMnemonic(text, text.length() + 2, "(" + mnemonicChar + ")");
   }
 
   /**
@@ -157,7 +184,12 @@ public final class TextWithMnemonic {
         }
         plainText.append(ch);
       }
-      return new TextWithMnemonic(plainText.toString(), mnemonicIndex);
+      String plain = plainText.toString();
+      int length = plain.length();
+      if (length > 3 && mnemonicIndex == length - 2 && plain.charAt(length - 1) == ')' && plain.charAt(length - 3) == '(') {
+        return new TextWithMnemonic(plain.substring(0, length - 3), mnemonicIndex, plain.substring(length - 3));
+      }
+      return new TextWithMnemonic(plain, mnemonicIndex, "");
     }
     return fromPlainText(text);
   }
@@ -168,12 +200,13 @@ public final class TextWithMnemonic {
     if (o == null || getClass() != o.getClass()) return false;
     TextWithMnemonic mnemonic = (TextWithMnemonic)o;
     return myMnemonicIndex == mnemonic.myMnemonicIndex &&
-           myText.equals(mnemonic.myText);
+           myText.equals(mnemonic.myText) &&
+           myMnemonicSuffix.equals(mnemonic.myMnemonicSuffix);
   }
 
   @Override
   public int hashCode() {
-    return myText.hashCode() * 31 + myMnemonicIndex;
+    return (myText.hashCode() * 31 + myMnemonicIndex) * 31 + myMnemonicSuffix.hashCode();
   }
 
   /**
@@ -183,8 +216,9 @@ public final class TextWithMnemonic {
   @Override
   public String toString() {
     if (myMnemonicIndex > -1) {
-      String prefix = StringUtil.escapeMnemonics(myText.substring(0, myMnemonicIndex));
-      String suffix = myText.substring(myMnemonicIndex);
+      String completeText = myText + myMnemonicSuffix;
+      String prefix = StringUtil.escapeMnemonics(completeText.substring(0, myMnemonicIndex));
+      String suffix = completeText.substring(myMnemonicIndex);
       return prefix + "_" + suffix;
     }
     return StringUtil.escapeMnemonics(myText);