From 1079665c3c017ee024a2ffdaf3488cc8c37f087a Mon Sep 17 00:00:00 2001
From: Jean Chalard <jchalard@google.com>
Date: Tue, 25 Mar 2014 15:57:47 +0900
Subject: [PATCH] [CB14] Implement backspace in the combiner chain

Bug: 13622107
Bug: 13406701
Change-Id: I0023b398c4451253f9f717e2bd990b8a054004bc
---
 .../inputmethod/event/CombinerChain.java      | 16 +++++++++-
 .../com/android/inputmethod/event/Event.java  |  2 +-
 .../android/inputmethod/latin/Suggest.java    |  7 ++++-
 .../inputmethod/latin/WordComposer.java       | 29 +++++--------------
 .../latin/inputlogic/InputLogic.java          |  2 +-
 .../inputmethod/latin/InputLogicTests.java    | 20 +++++++++++++
 6 files changed, 50 insertions(+), 26 deletions(-)

diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index 5ca9842c1e..8b59dc52a8 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -17,7 +17,9 @@
 package com.android.inputmethod.event;
 
 import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
 
+import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import java.util.ArrayList;
@@ -84,7 +86,19 @@ public class CombinerChain {
             }
         }
         if (null != event) {
-            mCombinedText.append(event.getTextToCommit());
+            // TODO: figure out the generic way of doing this
+            if (Constants.CODE_DELETE == event.mKeyCode) {
+                final int length = mCombinedText.length();
+                if (length > 0) {
+                    final int lastCodePoint = mCombinedText.codePointBefore(length);
+                    mCombinedText.delete(length - Character.charCount(lastCodePoint), length);
+                }
+            } else {
+                final CharSequence textToCommit = event.getTextToCommit();
+                if (!TextUtils.isEmpty(textToCommit)) {
+                    mCombinedText.append(textToCommit);
+                }
+            }
         }
         mStateFeedback.clear();
         for (int i = mCombiners.size() - 1; i >= 0; --i) {
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java
index 2bfe0732d7..646590948f 100644
--- a/java/src/com/android/inputmethod/event/Event.java
+++ b/java/src/com/android/inputmethod/event/Event.java
@@ -229,9 +229,9 @@ public class Event {
         switch (mType) {
         case EVENT_MODE_KEY:
         case EVENT_NOT_HANDLED:
+        case EVENT_TOGGLE:
             return "";
         case EVENT_INPUT_KEYPRESS:
-        case EVENT_TOGGLE:
             return StringUtils.newSingleCodePointString(mCodePoint);
         case EVENT_GESTURE:
         case EVENT_SOFTWARE_GENERATED_STRING:
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index ba64028caf..82dbe3c438 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
 
 import android.text.TextUtils;
 
+import com.android.inputmethod.event.Event;
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.define.ProductionFlag;
@@ -121,7 +122,11 @@ public final class Suggest {
         if (trailingSingleQuotesCount > 0) {
             wordComposerForLookup = new WordComposer(wordComposer);
             for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
-                wordComposerForLookup.deleteLast();
+                // TODO: do not create a fake event for this. Ideally the word composer should know
+                // how to give out the word without trailing quotes and we can remove this entirely
+                wordComposerForLookup.deleteLast(Event.createSoftwareKeypressEvent(
+                        Event.NOT_A_CODE_POINT, Constants.CODE_DELETE,
+                        Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE));
             }
         } else {
             wordComposerForLookup = wordComposer;
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index a60ca3d414..a955f375ba 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -314,29 +314,14 @@ public final class WordComposer {
     }
 
     /**
-     * Delete the last keystroke as a result of hitting backspace.
+     * Delete the last composing unit as a result of hitting backspace.
      */
-    public void deleteLast() {
-        final int size = size();
-        if (size > 0) {
-            // Note: mTypedWord.length() and mCodes.length differ when there are surrogate pairs
-            final int stringBuilderLength = mTypedWord.length();
-            if (stringBuilderLength < size) {
-                throw new RuntimeException(
-                        "In WordComposer: mCodes and mTypedWords have non-matching lengths");
-            }
-            final int lastChar = mTypedWord.codePointBefore(stringBuilderLength);
-            // TODO: with events and composition, this is absolutely not necessarily true.
-            mEvents.remove(mEvents.size() - 1);
-            if (Character.isSupplementaryCodePoint(lastChar)) {
-                mTypedWord.delete(stringBuilderLength - 2, stringBuilderLength);
-            } else {
-                mTypedWord.deleteCharAt(stringBuilderLength - 1);
-            }
-            if (Character.isUpperCase(lastChar)) mCapsCount--;
-            if (Character.isDigit(lastChar)) mDigitsCount--;
-            refreshSize();
-        }
+    public void deleteLast(final Event event) {
+        mCombinerChain.processEvent(mEvents, event);
+        mTypedWord.replace(0, mTypedWord.length(),
+                mCombinerChain.getComposingWordWithCombiningFeedback().toString());
+        mEvents.add(event);
+        refreshSize();
         // We may have deleted the last one.
         if (0 == size()) {
             mIsFirstCharCapitalized = false;
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 36b30eabec..fdba0d1f3b 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -922,7 +922,7 @@ public final class InputLogic {
                 mWordComposer.reset();
                 mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
             } else {
-                mWordComposer.deleteLast();
+                mWordComposer.deleteLast(inputTransaction.mEvent);
             }
             mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
             handler.postUpdateSuggestionStrip();
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index ab6245635d..d4e6ad87ae 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -454,4 +454,24 @@ public class InputLogicTests extends InputTestsBase {
         assertEquals("predictions after recorrection", "Obama",
                 suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
     }
+
+    public void testComposingMultipleBackspace() {
+        final String WORD_TO_TYPE = "radklro";
+        final int TIMES_TO_TYPE = 3;
+        final int TIMES_TO_BACKSPACE = 8;
+        type(WORD_TO_TYPE);
+        type(Constants.CODE_DELETE);
+        type(Constants.CODE_DELETE);
+        type(Constants.CODE_DELETE);
+        type(WORD_TO_TYPE);
+        type(Constants.CODE_DELETE);
+        type(Constants.CODE_DELETE);
+        type(WORD_TO_TYPE);
+        type(Constants.CODE_DELETE);
+        type(Constants.CODE_DELETE);
+        type(Constants.CODE_DELETE);
+        assertEquals("composing with multiple backspace",
+                WORD_TO_TYPE.length() * TIMES_TO_TYPE - TIMES_TO_BACKSPACE,
+                mEditText.getText().length());
+    }
 }
-- 
GitLab