Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 264afb10 authored by Jean Chalard's avatar Jean Chalard Committed by Android (Google) Code Review
Browse files

Merge "Remove mPrevWordsInfo"

parents 874a600d 367c199d
Loading
Loading
Loading
Loading
+8 −8
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ public final class Suggest {
                wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords,
                additionalFeaturesOptions, SESSION_TYPING, rawSuggestions);

        final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
        final boolean isOnlyFirstCharCapitalized = wordComposer.isOnlyFirstCharCapitalized();
        // If resumed, then we don't want to upcase everything: resuming on a fully-capitalized
        // words is rarely done to switch to another fully-capitalized word, but usually to a
        // normal, non-capitalized suggestion.
@@ -122,7 +122,7 @@ public final class Suggest {
        } else {
            final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo(
                    suggestionResults.first(), suggestionResults.mLocale, isAllUpperCase,
                    isFirstCharCapitalized, trailingSingleQuotesCount);
                    isOnlyFirstCharCapitalized, trailingSingleQuotesCount);
            firstSuggestion = firstSuggestedWordInfo.mWord;
            if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) {
                whitelistedWord = null;
@@ -142,7 +142,7 @@ public final class Suggest {
        final boolean allowsToBeAutoCorrected = (null != whitelistedWord
                && !whitelistedWord.equals(typedWord))
                || (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord(
                        consideredWord, wordComposer.isFirstCharCapitalized())
                        consideredWord, isOnlyFirstCharCapitalized)
                        && !typedWord.equals(firstSuggestion));

        final boolean hasAutoCorrection;
@@ -173,12 +173,12 @@ public final class Suggest {
        final ArrayList<SuggestedWordInfo> suggestionsContainer =
                new ArrayList<>(suggestionResults);
        final int suggestionsCount = suggestionsContainer.size();
        if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
        if (isOnlyFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
            for (int i = 0; i < suggestionsCount; ++i) {
                final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
                final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
                        wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized,
                        trailingSingleQuotesCount);
                        wordInfo, suggestionResults.mLocale, isAllUpperCase,
                        isOnlyFirstCharCapitalized, trailingSingleQuotesCount);
                suggestionsContainer.set(i, transformedWordInfo);
            }
        }
@@ -292,11 +292,11 @@ public final class Suggest {

    /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
            final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
            final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
            final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) {
        final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
        if (isAllUpperCase) {
            sb.append(wordInfo.mWord.toUpperCase(locale));
        } else if (isFirstCharCapitalized) {
        } else if (isOnlyFirstCharCapitalized) {
            sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale));
        } else {
            sb.append(wordInfo.mWord);
+19 −45
Original line number Diff line number Diff line
@@ -45,9 +45,6 @@ public final class WordComposer {
    // The list of events that served to compose this string.
    private final ArrayList<Event> mEvents;
    private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
    // The information of previous words (before the composing word). Must not be null. Used as
    // context for suggestions.
    private PrevWordsInfo mPrevWordsInfo;
    private String mAutoCorrection;
    private boolean mIsResumed;
    private boolean mIsBatchMode;
@@ -72,9 +69,9 @@ public final class WordComposer {
    private int mCursorPositionWithinWord;

    /**
     * Whether the user chose to capitalize the first char of the word.
     * Whether the composing word has the only first char capitalized.
     */
    private boolean mIsFirstCharCapitalized;
    private boolean mIsOnlyFirstCharCapitalized;

    public WordComposer() {
        mCombinerChain = new CombinerChain("");
@@ -84,7 +81,6 @@ public final class WordComposer {
        mIsBatchMode = false;
        mCursorPositionWithinWord = 0;
        mRejectedBatchModeSuggestion = null;
        mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
        refreshTypedWordCache();
    }

@@ -111,12 +107,11 @@ public final class WordComposer {
        mAutoCorrection = null;
        mCapsCount = 0;
        mDigitsCount = 0;
        mIsFirstCharCapitalized = false;
        mIsOnlyFirstCharCapitalized = false;
        mIsResumed = false;
        mIsBatchMode = false;
        mCursorPositionWithinWord = 0;
        mRejectedBatchModeSuggestion = null;
        mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
        refreshTypedWordCache();
    }

@@ -178,12 +173,6 @@ public final class WordComposer {
        return mInputPointers;
    }

    private static boolean isFirstCharCapitalized(final int index, final int codePoint,
            final boolean previous) {
        if (index == 0) return Character.isUpperCase(codePoint);
        return previous && !Character.isUpperCase(codePoint);
    }

    /**
     * Process an input event.
     *
@@ -203,7 +192,7 @@ public final class WordComposer {
        mCursorPositionWithinWord = mCodePointSize;
        // We may have deleted the last one.
        if (0 == mCodePointSize) {
            mIsFirstCharCapitalized = false;
            mIsOnlyFirstCharCapitalized = false;
        }
        if (Constants.CODE_DELETE != event.mKeyCode) {
            if (newIndex < MAX_WORD_LENGTH) {
@@ -215,8 +204,12 @@ public final class WordComposer {
                    mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
                }
            }
            mIsFirstCharCapitalized = isFirstCharCapitalized(
                    newIndex, primaryCode, mIsFirstCharCapitalized);
            if (0 == newIndex) {
                mIsOnlyFirstCharCapitalized = Character.isUpperCase(primaryCode);
            } else {
                mIsOnlyFirstCharCapitalized = mIsOnlyFirstCharCapitalized
                        && !Character.isUpperCase(primaryCode);
            }
            if (Character.isUpperCase(primaryCode)) mCapsCount++;
            if (Character.isDigit(primaryCode)) mDigitsCount++;
        }
@@ -296,10 +289,8 @@ public final class WordComposer {
     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
     * @param codePoints the code points to set as the composing word.
     * @param coordinates the x, y coordinates of the key in the CoordinateUtils format
     * @param prevWordsInfo the information of previous words, to use as context for suggestions
     */
    public void setComposingWord(final int[] codePoints, final int[] coordinates,
            final PrevWordsInfo prevWordsInfo) {
    public void setComposingWord(final int[] codePoints, final int[] coordinates) {
        reset();
        final int length = codePoints.length;
        for (int i = 0; i < length; ++i) {
@@ -308,7 +299,6 @@ public final class WordComposer {
                    CoordinateUtils.yFromArray(coordinates, i)));
        }
        mIsResumed = true;
        mPrevWordsInfo = prevWordsInfo;
    }

    /**
@@ -319,16 +309,13 @@ public final class WordComposer {
        return mTypedWordCache.toString();
    }

    public PrevWordsInfo getPrevWordsInfoForSuggestion() {
        return mPrevWordsInfo;
    }

    /**
     * Whether or not the user typed a capital letter as the first letter in the word
     * Whether or not the user typed a capital letter as the first letter in the word, and no
     * other letter is capitalized
     * @return capitalization preference
     */
    public boolean isFirstCharCapitalized() {
        return mIsFirstCharCapitalized;
    public boolean isOnlyFirstCharCapitalized() {
        return mIsOnlyFirstCharCapitalized;
    }

    /**
@@ -364,7 +351,7 @@ public final class WordComposer {
    }

    /**
     * Saves the caps mode and the previous word at the start of composing.
     * Saves the caps mode at the start of composing.
     *
     * WordComposer needs to know about the caps mode for several reasons. The first is, we need
     * to know after the fact what the reason was, to register the correct form into the user
@@ -373,12 +360,9 @@ public final class WordComposer {
     * Also, batch input needs to know about the current caps mode to display correctly
     * capitalized suggestions.
     * @param mode the mode at the time of start
     * @param prevWordsInfo the information of previous words
     */
    public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
            final PrevWordsInfo prevWordsInfo) {
    public void setCapitalizedModeAtStartComposingTime(final int mode) {
        mCapitalizedMode = mode;
        mPrevWordsInfo = prevWordsInfo;
    }

    /**
@@ -429,11 +413,10 @@ public final class WordComposer {
        mCapsCount = 0;
        mDigitsCount = 0;
        mIsBatchMode = false;
        mPrevWordsInfo = new PrevWordsInfo(committedWord.toString());
        mCombinerChain.reset();
        mEvents.clear();
        mCodePointSize = 0;
        mIsFirstCharCapitalized = false;
        mIsOnlyFirstCharCapitalized = false;
        mCapitalizedMode = CAPS_MODE_OFF;
        refreshTypedWordCache();
        mAutoCorrection = null;
@@ -443,15 +426,7 @@ public final class WordComposer {
        return lastComposedWord;
    }

    // Call this when the recorded previous word should be discarded. This is typically called
    // when the user inputs a separator that's not whitespace (including the case of the
    // double-space-to-period feature).
    public void discardPreviousWordForSuggestion() {
        mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
    }

    public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
            final PrevWordsInfo prevWordsInfo) {
    public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
        mEvents.clear();
        Collections.copy(mEvents, lastComposedWord.mEvents);
        mInputPointers.set(lastComposedWord.mInputPointers);
@@ -462,7 +437,6 @@ public final class WordComposer {
        mCursorPositionWithinWord = mCodePointSize;
        mRejectedBatchModeSuggestion = null;
        mIsResumed = true;
        mPrevWordsInfo = prevWordsInfo;
    }

    public boolean isBatchMode() {
+10 −44
Original line number Diff line number Diff line
@@ -544,11 +544,8 @@ public final class InputLogic {
            }
        }
        mConnection.endBatchEdit();
        mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
                getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()),
                // Prev word is 1st word before cursor
                getPrevWordsInfoFromNthPreviousWordForSuggestion(
                        settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
        mWordComposer.setCapitalizedModeAtStartComposingTime(
                getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
    }

    /* The sequence number member is only used in onUpdateBatchInput. It is increased each time
@@ -584,10 +581,8 @@ public final class InputLogic {
                    mSpaceState = SpaceState.PHANTOM;
                    keyboardSwitcher.requestUpdatingShiftState(
                            getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState());
                    mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
                            getActualCapsMode(settingsValues,
                                    keyboardSwitcher.getKeyboardShiftMode()),
                                            new PrevWordsInfo(commitParts[0]));
                    mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode(
                            settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
                    ++mAutoCommitSequenceNumber;
                }
            }
@@ -724,15 +719,7 @@ public final class InputLogic {
            mWordComposer.processEvent(inputTransaction.mEvent);
            // If it's the first letter, make note of auto-caps state
            if (mWordComposer.isSingleLetter()) {
                // We pass 2 to getPreviousWordForSuggestion when the previous code point is a word
                // connector. Otherwise, we pass 1 because we were not composing a word yet, so the
                // word we want is the 1st word before the cursor.
                mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
                        inputTransaction.mShiftState,
                        getPrevWordsInfoFromNthPreviousWordForSuggestion(
                                settingsValues.mSpacingAndPunctuations,
                                settingsValues.mSpacingAndPunctuations.isWordConnector(
                                        mConnection.getCodePointBeforeCursor()) ? 2 : 1));
                mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
            }
            mConnection.setComposingText(getTextWithUnderline(
                    mWordComposer.getTypedWord()), 1);
@@ -924,10 +911,8 @@ public final class InputLogic {
                    // No need to reset mSpaceState, it has already be done (that's why we
                    // receive it as a parameter)
                    inputTransaction.setRequiresUpdateSuggestions();
                    mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
                            WordComposer.CAPS_MODE_OFF,
                            getPrevWordsInfoFromNthPreviousWordForSuggestion(
                                    inputTransaction.mSettingsValues.mSpacingAndPunctuations, 1));
                    mWordComposer.setCapitalizedModeAtStartComposingTime(
                            WordComposer.CAPS_MODE_OFF);
                    return;
                }
            } else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
@@ -1107,7 +1092,6 @@ public final class InputLogic {
            final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations
                    .mSentenceSeparatorAndSpace;
            mConnection.commitText(textToInsert, 1);
            mWordComposer.discardPreviousWordForSuggestion();
            return true;
        }
        return false;
@@ -1267,10 +1251,7 @@ public final class InputLogic {
        final int expectedCursorPosition = mConnection.getExpectedSelectionStart();
        if (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations)) {
            // Show predictions.
            mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
                    WordComposer.CAPS_MODE_OFF,
                    getPrevWordsInfoFromNthPreviousWordForSuggestion(
                            settingsValues.mSpacingAndPunctuations, 1));
            mWordComposer.setCapitalizedModeAtStartComposingTime(WordComposer.CAPS_MODE_OFF);
            mLatinIME.mHandler.postUpdateSuggestionStrip();
            return;
        }
@@ -1322,7 +1303,7 @@ public final class InputLogic {
                settingsValues.mSpacingAndPunctuations,
                0 == numberOfCharsInWordBeforeCursor ? 1 : 2);
        mWordComposer.setComposingWord(codePoints,
                mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
                mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
        mWordComposer.setCursorPositionWithinWord(
                typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
        mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
@@ -1450,7 +1431,7 @@ public final class InputLogic {
            // with the typed word, so we need to resume suggestions right away.
            final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
            mWordComposer.setComposingWord(codePoints,
                    mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
                    mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
            mConnection.setComposingText(textToCommit, 1);
        }
        // Don't restart suggestion yet. We'll restart if the user deletes the separator.
@@ -1897,21 +1878,6 @@ public final class InputLogic {
        // strings.
        mLastComposedWord = mWordComposer.commitWord(commitType,
                chosenWordWithSuggestions, separatorString, prevWordsInfo);
        final boolean shouldDiscardPreviousWordForSuggestion;
        if (0 == StringUtils.codePointCount(separatorString)) {
            // Separator is 0-length, we can keep the previous word for suggestion. Either this
            // was a manual pick or the language has no spaces in which case we want to keep the
            // previous word, or it was the keyboard closing or the cursor moving in which case it
            // will be reset anyway.
            shouldDiscardPreviousWordForSuggestion = false;
        } else {
            // Otherwise, we discard if the separator contains any non-whitespace.
            shouldDiscardPreviousWordForSuggestion =
                    !StringUtils.containsOnlyWhitespace(separatorString);
        }
        if (shouldDiscardPreviousWordForSuggestion) {
            mWordComposer.discardPreviousWordForSuggestion();
        }
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -323,7 +323,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
                } else {
                    coordinates = dictInfo.mKeyboard.getCoordinates(codePoints);
                }
                composer.setComposingWord(codePoints, coordinates, null /* previousWord */);
                composer.setComposingWord(codePoints, coordinates);
                // TODO: make a spell checker option to block offensive words or not
                final ArrayList<SuggestedWordInfo> suggestions =
                        dictInfo.mDictionary.getSuggestions(composer, prevWordsInfo,
+19 −35
Original line number Diff line number Diff line
@@ -40,8 +40,7 @@ public class WordComposerTests extends AndroidTestCase {
        final int[] COORDINATES_WITHIN_BMP =
                CoordinateUtils.newCoordinateArray(CODEPOINTS_WITHIN_BMP.length,
                        Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
        final PrevWordsInfo PREV_WORDS_INFO = new PrevWordsInfo("prevword");
        wc.setComposingWord(CODEPOINTS_WITHIN_BMP, COORDINATES_WITHIN_BMP, PREV_WORDS_INFO);
        wc.setComposingWord(CODEPOINTS_WITHIN_BMP, COORDINATES_WITHIN_BMP);
        assertEquals(wc.size(), STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length()));
        assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
        wc.setCursorPositionWithinWord(2);
@@ -56,15 +55,12 @@ public class WordComposerTests extends AndroidTestCase {
        // Move the cursor to after the 'f'
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
        assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
        // Check the previous word is still there
        assertEquals(PREV_WORDS_INFO, wc.getPrevWordsInfoForSuggestion());
        // Move the cursor past the end of the word
        assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1));
        assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15));
        // Do what LatinIME does when the cursor is moved outside of the word,
        // and check the behavior is correct.
        wc.reset();
        assertNull(wc.getPrevWordsInfoForSuggestion().mPrevWord);

        // \uD861\uDED7 is 𨛗, a character outside the BMP
        final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh";
@@ -73,8 +69,8 @@ public class WordComposerTests extends AndroidTestCase {
        final int[] COORDINATES_WITH_SUPPLEMENTARY_CHAR =
                CoordinateUtils.newCoordinateArray(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length,
                        Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        assertEquals(wc.size(), CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length);
        assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
        wc.setCursorPositionWithinWord(3);
@@ -83,55 +79,43 @@ public class WordComposerTests extends AndroidTestCase {
        assertTrue(wc.isCursorFrontOrMiddleOfComposingWord());
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
        assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
        assertNull(wc.getPrevWordsInfoForSuggestion().mPrevWord);

        final PrevWordsInfo PREV_WORDS_INFO_STR_WITHIN_BMP = new PrevWordsInfo(STR_WITHIN_BMP);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_STR_WITHIN_BMP);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        wc.setCursorPositionWithinWord(3);
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
        assertEquals(PREV_WORDS_INFO_STR_WITHIN_BMP, wc.getPrevWordsInfoForSuggestion());

        final PrevWordsInfo PREV_WORDS_INFO_STR_WITH_SUPPLEMENTARY_CHAR =
                new PrevWordsInfo(STR_WITH_SUPPLEMENTARY_CHAR);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_STR_WITH_SUPPLEMENTARY_CHAR);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        wc.setCursorPositionWithinWord(3);
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
        assertEquals(PREV_WORDS_INFO_STR_WITH_SUPPLEMENTARY_CHAR,
                wc.getPrevWordsInfoForSuggestion());

        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_STR_WITHIN_BMP);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        wc.setCursorPositionWithinWord(3);
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3));
        assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1));
        assertEquals(PREV_WORDS_INFO_STR_WITHIN_BMP, wc.getPrevWordsInfoForSuggestion());


        final PrevWordsInfo PREV_WORDS_INFO_NULL = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_NULL);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        wc.setCursorPositionWithinWord(3);
        assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9));
        assertNull(wc.getPrevWordsInfoForSuggestion().mPrevWord);

        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_STR_WITH_SUPPLEMENTARY_CHAR);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10));
        assertEquals(PREV_WORDS_INFO_STR_WITH_SUPPLEMENTARY_CHAR,
                wc.getPrevWordsInfoForSuggestion());

        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_NULL);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-11));

        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_NULL);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0));

        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR,
                PREV_WORDS_INFO_NULL);
        wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
                COORDINATES_WITH_SUPPLEMENTARY_CHAR);
        wc.setCursorPositionWithinWord(2);
        assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0));
    }