From c83359f9746ca6f0269a1a7017b585c1a5cab9b8 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 18 Nov 2011 20:03:38 +0900 Subject: [PATCH] Special case quotes at start and end of words Single quote at start of word is not considered a part of a word any more. Single quote at the end of a word now behave like capitalization: lookup in the dictionary is done *disregarding* a final quote, and it is forcefully added back into the suggestions afterwards. Bug: 5566368 Change-Id: I14dd3815f4b743edba56d64a3abdf4b73d863a6a --- .../android/inputmethod/latin/LatinIME.java | 13 +++++- .../android/inputmethod/latin/Suggest.java | 43 +++++++++++++------ .../inputmethod/latin/WordComposer.java | 22 ++++++++-- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 167e500f6..d72f8652d 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1503,6 +1503,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mComposingStateManager.onFinishComposingText(); } } + if (code == Keyboard.CODE_SINGLE_QUOTE && !isCursorTouchingWord()) { + mHasUncommittedTypedChars = false; + } final KeyboardSwitcher switcher = mKeyboardSwitcher; if (switcher.isShiftedOrShiftLocked()) { if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT @@ -1775,7 +1778,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // a boolean flag. Right now this is handled with a slight hack in // WhitelistDictionary#shouldForciblyAutoCorrectFrom. final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected( - mSuggest.getUnigramDictionaries(), typedWord, preferCapitalization()); + mSuggest.getUnigramDictionaries(), + // If the typed string ends with a single quote, for dictionary lookup purposes + // we behave as if the single quote was not here. Here, we are looking up the + // typed string in the dictionary (to avoid autocorrecting from an existing + // word, so for consistency this lookup should be made WITHOUT the trailing + // single quote. + wordComposer.isLastCharASingleQuote() + ? typedWord.subSequence(0, typedWord.length() - 1) : typedWord, + preferCapitalization()); if (mCorrectionMode == Suggest.CORRECTION_FULL || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) { autoCorrectionAvailable |= (!allowsToBeAutoCorrected); diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 97e91745c..5a3c348a9 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -20,6 +20,7 @@ import android.content.Context; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; import java.io.File; @@ -81,6 +82,8 @@ public class Suggest implements Dictionary.WordCallback { public static final String DICT_KEY_USER_BIGRAM = "user_bigram"; public static final String DICT_KEY_WHITELIST ="whitelist"; + private static String SINGLE_QUOTE_AS_STRING = String.valueOf((char)Keyboard.CODE_SINGLE_QUOTE); + private static final boolean DBG = LatinImeLogger.sDBG; private AutoCorrection mAutoCorrection; @@ -101,11 +104,12 @@ public class Suggest implements Dictionary.WordCallback { private ArrayList mSuggestions = new ArrayList(); ArrayList mBigramSuggestions = new ArrayList(); - private CharSequence mTypedWord; + private CharSequence mConsideredWord; // TODO: Remove these member variables by passing more context to addWord() callback method private boolean mIsFirstCharCapitalized; private boolean mIsAllUpperCase; + private boolean mIsLastCharASingleQuote; private int mCorrectionMode = CORRECTION_BASIC; @@ -295,17 +299,19 @@ public class Suggest implements Dictionary.WordCallback { mAutoCorrection.init(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsAllUpperCase = wordComposer.isAllUpperCase(); + mIsLastCharASingleQuote = wordComposer.isLastCharASingleQuote(); collectGarbage(mSuggestions, mPrefMaxSuggestions); Arrays.fill(mScores, 0); - // Save a lowercase version of the original word - String typedWord = wordComposer.getTypedWord(); + final String typedWord = wordComposer.getTypedWord(); + final String consideredWord = mIsLastCharASingleQuote + ? typedWord.substring(0, typedWord.length() - 1) : typedWord; if (typedWord != null) { // Treating USER_TYPED as UNIGRAM suggestion for logging now. LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.DataType.UNIGRAM); } - mTypedWord = typedWord; + mConsideredWord = consideredWord; if (wordComposer.size() <= 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM || mCorrectionMode == CORRECTION_BASIC)) { @@ -321,7 +327,7 @@ public class Suggest implements Dictionary.WordCallback { for (final Dictionary dictionary : mBigramDictionaries.values()) { dictionary.getBigrams(wordComposer, prevWordForBigram, this); } - if (TextUtils.isEmpty(typedWord)) { + if (TextUtils.isEmpty(consideredWord)) { // Nothing entered: return all bigrams for the previous word int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); for (int i = 0; i < insertCount; ++i) { @@ -330,7 +336,7 @@ public class Suggest implements Dictionary.WordCallback { } else { // Word entered: return only bigrams that match the first char of the typed word @SuppressWarnings("null") - final char currentChar = typedWord.charAt(0); + final char currentChar = consideredWord.charAt(0); // TODO: Must pay attention to locale when changing case. final char currentCharUpper = Character.toUpperCase(currentChar); int count = 0; @@ -354,24 +360,32 @@ public class Suggest implements Dictionary.WordCallback { if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST)) continue; final Dictionary dictionary = mUnigramDictionaries.get(key); - dictionary.getWords(wordComposer, this, proximityInfo); + if (mIsLastCharASingleQuote) { + final WordComposer tmpWordComposer = new WordComposer(wordComposer); + tmpWordComposer.deleteLast(); + dictionary.getWords(tmpWordComposer, this, proximityInfo); + } else { + dictionary.getWords(wordComposer, this, proximityInfo); + } } } - final String typedWordString = typedWord == null ? null : typedWord.toString(); + final String consideredWordString = + consideredWord == null ? null : consideredWord.toString(); CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized, - mWhiteListDictionary.getWhitelistedWord(typedWordString)); + mWhiteListDictionary.getWhitelistedWord(consideredWordString)); mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer, - mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode, + mSuggestions, mScores, consideredWord, mAutoCorrectionThreshold, mCorrectionMode, whitelistedWord); if (whitelistedWord != null) { - mSuggestions.add(0, whitelistedWord); + mSuggestions.add(0, mIsLastCharASingleQuote + ? whitelistedWord + SINGLE_QUOTE_AS_STRING : whitelistedWord); } if (typedWord != null) { - mSuggestions.add(0, typedWordString); + mSuggestions.add(0, typedWord.toString()); } Utils.removeDupes(mSuggestions); @@ -424,7 +438,7 @@ public class Suggest implements Dictionary.WordCallback { int pos = 0; // Check if it's the same word, only caps are different - if (Utils.equalsIgnoreCase(mTypedWord, word, offset, length)) { + if (Utils.equalsIgnoreCase(mConsideredWord, word, offset, length)) { // TODO: remove this surrounding if clause and move this logic to // getSuggestedWordBuilder. if (suggestions.size() > 0) { @@ -486,6 +500,9 @@ public class Suggest implements Dictionary.WordCallback { } else { sb.append(word, offset, length); } + if (mIsLastCharASingleQuote) { + sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); + } suggestions.add(pos, sb); if (suggestions.size() > prefMaxSuggestions) { final CharSequence garbage = suggestions.remove(prefMaxSuggestions); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 7f3a54244..612b16071 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -16,9 +16,11 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyDetector; import java.util.ArrayList; +import java.util.Arrays; /** * A place to store the currently composing word with information such as adjacent key codes as well @@ -41,7 +43,9 @@ public class WordComposer { private int mCapsCount; private boolean mAutoCapitalized; - + // Cache this value for performance + private boolean mIsLastCharASingleQuote; + /** * Whether the user chose to capitalize the first char of the word. */ @@ -53,6 +57,7 @@ public class WordComposer { mTypedWord = new StringBuilder(N); mXCoordinates = new int[N]; mYCoordinates = new int[N]; + mIsLastCharASingleQuote = false; } public WordComposer(WordComposer source) { @@ -62,11 +67,12 @@ public class WordComposer { public void init(WordComposer source) { mCodes = new ArrayList(source.mCodes); mTypedWord = new StringBuilder(source.mTypedWord); - mXCoordinates = source.mXCoordinates; - mYCoordinates = source.mYCoordinates; + mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length); + mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length); mCapsCount = source.mCapsCount; mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; mAutoCapitalized = source.mAutoCapitalized; + mIsLastCharASingleQuote = source.mIsLastCharASingleQuote; } /** @@ -77,6 +83,7 @@ public class WordComposer { mTypedWord.setLength(0); mCapsCount = 0; mIsFirstCharCapitalized = false; + mIsLastCharASingleQuote = false; } /** @@ -126,6 +133,7 @@ public class WordComposer { mIsFirstCharCapitalized = isFirstCharCapitalized( newIndex, primaryCode, mIsFirstCharCapitalized); if (Character.isUpperCase(primaryCode)) mCapsCount++; + mIsLastCharASingleQuote = Keyboard.CODE_SINGLE_QUOTE == primaryCode; } /** @@ -157,6 +165,10 @@ public class WordComposer { } if (size() == 0) { mIsFirstCharCapitalized = false; + mIsLastCharASingleQuote = false; + } else { + mIsLastCharASingleQuote = + Keyboard.CODE_SINGLE_QUOTE == mTypedWord.codePointAt(mTypedWord.length() - 1); } } @@ -179,6 +191,10 @@ public class WordComposer { return mIsFirstCharCapitalized; } + public boolean isLastCharASingleQuote() { + return mIsLastCharASingleQuote; + } + /** * Whether or not all of the user typed chars are upper case * @return true if all user typed chars are upper case, false otherwise