Add beginning of sentence information in PrevWordsInfo.

Bug: 14119293
Bug: 14425059
Change-Id: I65320920e840082b0b697bb621676716d0933e0c
This commit is contained in:
Keisuke Kuroyanagi 2014-05-21 15:40:08 +09:00
parent c18b1c42f3
commit 17f326b745
6 changed files with 102 additions and 67 deletions

View file

@ -1435,12 +1435,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We're checking the previous word in the text field against the memorized previous // We're checking the previous word in the text field against the memorized previous
// word. If we are composing a word we should have the second word before the cursor // word. If we are composing a word we should have the second word before the cursor
// memorized, otherwise we should have the first. // memorized, otherwise we should have the first.
final CharSequence rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion( final PrevWordsInfo rereadPrevWordsInfo =
mInputLogic.getPrevWordsInfoFromNthPreviousWordForSuggestion(
currentSettings.mSpacingAndPunctuations, currentSettings.mSpacingAndPunctuations,
mInputLogic.mWordComposer.isComposingWord() ? 2 : 1); mInputLogic.mWordComposer.isComposingWord() ? 2 : 1);
if (!TextUtils.equals(prevWordsInfo.mPrevWord, rereadPrevWord)) { if (!TextUtils.equals(prevWordsInfo.mPrevWord, rereadPrevWordsInfo.mPrevWord)) {
throw new RuntimeException("Unexpected previous word: " throw new RuntimeException("Unexpected previous word: "
+ prevWordsInfo.mPrevWord + " <> " + rereadPrevWord); + prevWordsInfo.mPrevWord + " <> " + rereadPrevWordsInfo.mPrevWord);
} }
} }
} }

View file

@ -16,6 +16,9 @@
package com.android.inputmethod.latin; package com.android.inputmethod.latin;
import android.util.Log;
// TODO: Support multiple previous words for n-gram.
public class PrevWordsInfo { public class PrevWordsInfo {
// The previous word. May be null after resetting and before starting a new composing word, or // The previous word. May be null after resetting and before starting a new composing word, or
// when there is no context like at the start of text for example. It can also be set to null // when there is no context like at the start of text for example. It can also be set to null
@ -23,7 +26,18 @@ public class PrevWordsInfo {
// or a comma. // or a comma.
public final String mPrevWord; public final String mPrevWord;
// TODO: Have sentence separator.
// Whether the current context is beginning of sentence or not.
public final boolean mIsBeginningOfSentence;
// Beginning of sentence.
public PrevWordsInfo() {
mPrevWord = null;
mIsBeginningOfSentence = true;
}
public PrevWordsInfo(final String prevWord) { public PrevWordsInfo(final String prevWord) {
mPrevWord = prevWord; mPrevWord = prevWord;
mIsBeginningOfSentence = false;
} }
} }

View file

@ -538,10 +538,12 @@ public final class RichInputConnection {
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public String getNthPreviousWord(final SpacingAndPunctuations spacingAndPunctuations, public PrevWordsInfo getPrevWordsInfoFromNthPreviousWord(
final int n) { final SpacingAndPunctuations spacingAndPunctuations, final int n) {
mIC = mParent.getCurrentInputConnection(); mIC = mParent.getCurrentInputConnection();
if (null == mIC) return null; if (null == mIC) {
return new PrevWordsInfo(null);
}
final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
if (DEBUG_PREVIOUS_TEXT && null != prev) { if (DEBUG_PREVIOUS_TEXT && null != prev) {
final int checkLength = LOOKBACK_CHARACTER_NUM - 1; final int checkLength = LOOKBACK_CHARACTER_NUM - 1;
@ -561,46 +563,57 @@ public final class RichInputConnection {
} }
} }
} }
return getNthPreviousWord(prev, spacingAndPunctuations, n); return getPrevWordsInfoFromNthPreviousWord(prev, spacingAndPunctuations, n);
} }
private static boolean isSeparator(final int code, final int[] sortedSeparators) { private static boolean isSeparator(final int code, final int[] sortedSeparators) {
return Arrays.binarySearch(sortedSeparators, code) >= 0; return Arrays.binarySearch(sortedSeparators, code) >= 0;
} }
// Get the nth word before cursor. n = 1 retrieves the word immediately before the cursor, // Get information of the nth word before cursor. n = 1 retrieves the word immediately before
// n = 2 retrieves the word before that, and so on. This splits on whitespace only. // the cursor, n = 2 retrieves the word before that, and so on. This splits on whitespace only.
// Also, it won't return words that end in a separator (if the nth word before the cursor // Also, it won't return words that end in a separator (if the nth word before the cursor
// ends in a separator, it returns null). // ends in a separator, it returns information represents beginning-of-sentence).
// Example : // Example :
// (n = 1) "abc def|" -> def // (n = 1) "abc def|" -> def
// (n = 1) "abc def |" -> def // (n = 1) "abc def |" -> def
// (n = 1) "abc def. |" -> null // (n = 1) "abc def. |" -> beginning-of-sentence
// (n = 1) "abc def . |" -> null // (n = 1) "abc def . |" -> beginning-of-sentence
// (n = 2) "abc def|" -> abc // (n = 2) "abc def|" -> abc
// (n = 2) "abc def |" -> abc // (n = 2) "abc def |" -> abc
// (n = 2) "abc def. |" -> abc // (n = 2) "abc def. |" -> abc
// (n = 2) "abc def . |" -> def // (n = 2) "abc def . |" -> def
// (n = 2) "abc|" -> null // (n = 2) "abc|" -> beginning-of-sentence
// (n = 2) "abc |" -> null // (n = 2) "abc |" -> beginning-of-sentence
// (n = 2) "abc. def|" -> null // (n = 2) "abc. def|" -> beginning-of-sentence
public static String getNthPreviousWord(final CharSequence prev, public static PrevWordsInfo getPrevWordsInfoFromNthPreviousWord(final CharSequence prev,
final SpacingAndPunctuations spacingAndPunctuations, final int n) { final SpacingAndPunctuations spacingAndPunctuations, final int n) {
if (prev == null) return null; if (prev == null) return new PrevWordsInfo(null);
final String[] w = spaceRegex.split(prev); final String[] w = spaceRegex.split(prev);
// If we can't find n words, or we found an empty word, return null. // If we can't find n words, or we found an empty word, the context is
if (w.length < n) return null; // beginning-of-sentence.
if (w.length < n) {
return new PrevWordsInfo();
}
final String nthPrevWord = w[w.length - n]; final String nthPrevWord = w[w.length - n];
final int length = nthPrevWord.length(); final int length = nthPrevWord.length();
if (length <= 0) return null; if (length <= 0) {
return new PrevWordsInfo();
}
// If ends in a separator, return null // If ends in a sentence separator, the context is beginning-of-sentence.
final char lastChar = nthPrevWord.charAt(length - 1); final char lastChar = nthPrevWord.charAt(length - 1);
if (spacingAndPunctuations.isSentenceSeparator(lastChar)) {
new PrevWordsInfo();
}
// If ends in a word separator or connector, the context is unclear.
// TODO: Return meaningful context for this case.
if (spacingAndPunctuations.isWordSeparator(lastChar) if (spacingAndPunctuations.isWordSeparator(lastChar)
|| spacingAndPunctuations.isWordConnector(lastChar)) return null; || spacingAndPunctuations.isWordConnector(lastChar)) {
return new PrevWordsInfo(null);
return nthPrevWord; }
return new PrevWordsInfo(nthPrevWord);
} }
/** /**

View file

@ -371,12 +371,12 @@ public final class WordComposer {
* Also, batch input needs to know about the current caps mode to display correctly * Also, batch input needs to know about the current caps mode to display correctly
* capitalized suggestions. * capitalized suggestions.
* @param mode the mode at the time of start * @param mode the mode at the time of start
* @param previousWord the previous word as context for suggestions. May be null if none. * @param prevWordsInfo the information of previous words
*/ */
public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode, public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
final CharSequence previousWord) { final PrevWordsInfo prevWordsInfo) {
mCapitalizedMode = mode; mCapitalizedMode = mode;
mPrevWordsInfo = new PrevWordsInfo(null == previousWord ? null : previousWord.toString()); mPrevWordsInfo = prevWordsInfo;
} }
/** /**

View file

@ -575,7 +575,7 @@ public final class InputLogic {
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()), getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()),
// Prev word is 1st word before cursor // Prev word is 1st word before cursor
getNthPreviousWordForSuggestion( getPrevWordsInfoFromNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
} }
@ -614,7 +614,8 @@ public final class InputLogic {
getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState()); getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState());
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
getActualCapsMode(settingsValues, getActualCapsMode(settingsValues,
keyboardSwitcher.getKeyboardShiftMode()), commitParts[0]); keyboardSwitcher.getKeyboardShiftMode()),
new PrevWordsInfo(commitParts[0]));
++mAutoCommitSequenceNumber; ++mAutoCommitSequenceNumber;
} }
} }
@ -765,7 +766,8 @@ public final class InputLogic {
// We pass 1 to getPreviousWordForSuggestion because we were not composing a word // We pass 1 to getPreviousWordForSuggestion because we were not composing a word
// yet, so the word we want is the 1st word before the cursor. // yet, so the word we want is the 1st word before the cursor.
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
inputTransaction.mShiftState, getNthPreviousWordForSuggestion( inputTransaction.mShiftState,
getPrevWordsInfoFromNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
} }
mConnection.setComposingText(getTextWithUnderline( mConnection.setComposingText(getTextWithUnderline(
@ -1326,7 +1328,8 @@ public final class InputLogic {
// Show predictions. // Show predictions.
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
WordComposer.CAPS_MODE_OFF, WordComposer.CAPS_MODE_OFF,
getNthPreviousWordForSuggestion(settingsValues.mSpacingAndPunctuations, 1)); getPrevWordsInfoFromNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations, 1));
mLatinIME.mHandler.postUpdateSuggestionStrip(); mLatinIME.mHandler.postUpdateSuggestionStrip();
return; return;
} }
@ -1374,11 +1377,9 @@ public final class InputLogic {
// We want the previous word for suggestion. If we have chars in the word // We want the previous word for suggestion. If we have chars in the word
// before the cursor, then we want the word before that, hence 2; otherwise, // before the cursor, then we want the word before that, hence 2; otherwise,
// we want the word immediately before the cursor, hence 1. // we want the word immediately before the cursor, hence 1.
final CharSequence prevWord = getNthPreviousWordForSuggestion( final PrevWordsInfo prevWordsInfo = getPrevWordsInfoFromNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations, settingsValues.mSpacingAndPunctuations,
0 == numberOfCharsInWordBeforeCursor ? 1 : 2); 0 == numberOfCharsInWordBeforeCursor ? 1 : 2);
final PrevWordsInfo prevWordsInfo =
new PrevWordsInfo(prevWord != null ? prevWord.toString() : null);
mWordComposer.setComposingWord(codePoints, mWordComposer.setComposingWord(codePoints,
mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo); mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
mWordComposer.setCursorPositionWithinWord( mWordComposer.setCursorPositionWithinWord(
@ -1590,21 +1591,23 @@ public final class InputLogic {
} }
/** /**
* Get the nth previous word before the cursor as context for the suggestion process. * Get information fo previous words from the nth previous word before the cursor as context
* for the suggestion process.
* @param spacingAndPunctuations the current spacing and punctuations settings. * @param spacingAndPunctuations the current spacing and punctuations settings.
* @param nthPreviousWord reverse index of the word to get (1-indexed) * @param nthPreviousWord reverse index of the word to get (1-indexed)
* @return the nth previous word before the cursor. * @return the information of previous words
*/ */
// TODO: Make this private // TODO: Make this private
public CharSequence getNthPreviousWordForSuggestion( public PrevWordsInfo getPrevWordsInfoFromNthPreviousWordForSuggestion(
final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) { final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) {
if (spacingAndPunctuations.mCurrentLanguageHasSpaces) { if (spacingAndPunctuations.mCurrentLanguageHasSpaces) {
// If we are typing in a language with spaces we can just look up the previous // If we are typing in a language with spaces we can just look up the previous
// word from textview. // word information from textview.
return mConnection.getNthPreviousWord(spacingAndPunctuations, nthPreviousWord); return mConnection.getPrevWordsInfoFromNthPreviousWord(
spacingAndPunctuations, nthPreviousWord);
} else { } else {
return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? new PrevWordsInfo()
: mLastComposedWord.mCommittedWord; : new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString());
} }
} }
@ -1972,8 +1975,8 @@ public final class InputLogic {
suggestedWords); suggestedWords);
// Use the 2nd previous word as the previous word because the 1st previous word is the word // Use the 2nd previous word as the previous word because the 1st previous word is the word
// to be committed. // to be committed.
final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(mConnection.getNthPreviousWord( final PrevWordsInfo prevWordsInfo = mConnection.getPrevWordsInfoFromNthPreviousWord(
settingsValues.mSpacingAndPunctuations, 2)); settingsValues.mSpacingAndPunctuations, 2);
mConnection.commitText(chosenWordWithSuggestions, 1); mConnection.commitText(chosenWordWithSuggestions, 1);
// Add the word to the user history dictionary // Add the word to the user history dictionary
performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWordsInfo); performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWordsInfo);

View file

@ -155,13 +155,17 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
*/ */
public void testGetPreviousWord() { public void testGetPreviousWord() {
// If one of the following cases breaks, the bigram suggestions won't work. // If one of the following cases breaks, the bigram suggestions won't work.
assertEquals(RichInputConnection.getNthPreviousWord( assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def", mSpacingAndPunctuations, 2), "abc"); "abc def", mSpacingAndPunctuations, 2).mPrevWord, "abc");
assertNull(RichInputConnection.getNthPreviousWord( assertNull(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc", mSpacingAndPunctuations, 2)); "abc", mSpacingAndPunctuations, 2).mPrevWord);
assertNull(RichInputConnection.getNthPreviousWord( assertNull(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc. def", mSpacingAndPunctuations, 2)); "abc. def", mSpacingAndPunctuations, 2).mPrevWord);
assertFalse(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def", mSpacingAndPunctuations, 2).mIsBeginningOfSentence);
assertTrue(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc", mSpacingAndPunctuations, 2).mIsBeginningOfSentence);
// The following tests reflect the current behavior of the function // The following tests reflect the current behavior of the function
// RichInputConnection#getNthPreviousWord. // RichInputConnection#getNthPreviousWord.
// TODO: However at this time, the code does never go // TODO: However at this time, the code does never go
@ -169,23 +173,23 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
// this function if needed - especially since it does not seem very // this function if needed - especially since it does not seem very
// logical. These tests are just there to catch any unintentional // logical. These tests are just there to catch any unintentional
// changes in the behavior of the RichInputConnection#getPreviousWord method. // changes in the behavior of the RichInputConnection#getPreviousWord method.
assertEquals(RichInputConnection.getNthPreviousWord( assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def ", mSpacingAndPunctuations, 2), "abc"); "abc def ", mSpacingAndPunctuations, 2).mPrevWord, "abc");
assertEquals(RichInputConnection.getNthPreviousWord( assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def.", mSpacingAndPunctuations, 2), "abc"); "abc def.", mSpacingAndPunctuations, 2).mPrevWord, "abc");
assertEquals(RichInputConnection.getNthPreviousWord( assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def .", mSpacingAndPunctuations, 2), "def"); "abc def .", mSpacingAndPunctuations, 2).mPrevWord, "def");
assertNull(RichInputConnection.getNthPreviousWord( assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc ", mSpacingAndPunctuations, 2)); "abc ", mSpacingAndPunctuations, 2).mPrevWord, null);
assertEquals(RichInputConnection.getNthPreviousWord( assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def", mSpacingAndPunctuations, 1), "def"); "abc def", mSpacingAndPunctuations, 1).mPrevWord, "def");
assertEquals(RichInputConnection.getNthPreviousWord( assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def ", mSpacingAndPunctuations, 1), "def"); "abc def ", mSpacingAndPunctuations, 1).mPrevWord, "def");
assertNull(RichInputConnection.getNthPreviousWord( assertNull(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def.", mSpacingAndPunctuations, 1)); "abc def.", mSpacingAndPunctuations, 1).mPrevWord);
assertNull(RichInputConnection.getNthPreviousWord( assertNull(RichInputConnection.getPrevWordsInfoFromNthPreviousWord(
"abc def .", mSpacingAndPunctuations, 1)); "abc def .", mSpacingAndPunctuations, 1).mPrevWord);
} }
/** /**