am 914078fd: Fix a bug where recorrection would stop on connectors

* commit '914078fd9198aeb3d7ffa034562321d688d588f7':
  Fix a bug where recorrection would stop on connectors
main
Jean Chalard 2014-09-08 12:59:23 +00:00 committed by Android Git Automerger
commit 0eaa25e0b3
4 changed files with 54 additions and 26 deletions

View File

@ -623,14 +623,24 @@ public final class RichInputConnection {
return Arrays.binarySearch(sortedSeparators, code) >= 0; return Arrays.binarySearch(sortedSeparators, code) >= 0;
} }
private static boolean isPartOfCompositionForScript(final int codePoint,
final SpacingAndPunctuations spacingAndPunctuations, final int scriptId) {
// We always consider word connectors part of compositions.
return spacingAndPunctuations.isWordConnector(codePoint)
// Otherwise, it's part of composition if it's part of script and not a separator.
|| (!spacingAndPunctuations.isWordSeparator(codePoint)
&& ScriptUtils.isLetterPartOfScript(codePoint, scriptId));
}
/** /**
* Returns the text surrounding the cursor. * Returns the text surrounding the cursor.
* *
* @param sortedSeparators a sorted array of code points that split words. * @param spacingAndPunctuations the rules for spacing and punctuation
* @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_* * @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_*
* @return a range containing the text surrounding the cursor * @return a range containing the text surrounding the cursor
*/ */
public TextRange getWordRangeAtCursor(final int[] sortedSeparators, final int scriptId) { public TextRange getWordRangeAtCursor(final SpacingAndPunctuations spacingAndPunctuations,
final int scriptId) {
mIC = mParent.getCurrentInputConnection(); mIC = mParent.getCurrentInputConnection();
if (mIC == null) { if (mIC == null) {
return null; return null;
@ -647,8 +657,7 @@ public final class RichInputConnection {
int startIndexInBefore = before.length(); int startIndexInBefore = before.length();
while (startIndexInBefore > 0) { while (startIndexInBefore > 0) {
final int codePoint = Character.codePointBefore(before, startIndexInBefore); final int codePoint = Character.codePointBefore(before, startIndexInBefore);
if (isSeparator(codePoint, sortedSeparators) if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
|| !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) {
break; break;
} }
--startIndexInBefore; --startIndexInBefore;
@ -661,8 +670,7 @@ public final class RichInputConnection {
int endIndexInAfter = -1; int endIndexInAfter = -1;
while (++endIndexInAfter < after.length()) { while (++endIndexInAfter < after.length()) {
final int codePoint = Character.codePointAt(after, endIndexInAfter); final int codePoint = Character.codePointAt(after, endIndexInAfter);
if (isSeparator(codePoint, sortedSeparators) if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
|| !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) {
break; break;
} }
if (Character.isSupplementaryCodePoint(codePoint)) { if (Character.isSupplementaryCodePoint(codePoint)) {

View File

@ -1478,8 +1478,7 @@ public final class InputLogic {
return; return;
} }
final TextRange range = mConnection.getWordRangeAtCursor( final TextRange range = mConnection.getWordRangeAtCursor(
settingsValues.mSpacingAndPunctuations.mSortedWordSeparators, settingsValues.mSpacingAndPunctuations, currentKeyboardScriptId);
currentKeyboardScriptId);
if (null == range) return; // Happens if we don't have an input connection at all if (null == range) return; // Happens if we don't have an input connection at all
if (range.length() <= 0) { if (range.length() <= 0) {
// Race condition, or touching a word in a non-supported script. // Race condition, or touching a word in a non-supported script.

View File

@ -18,6 +18,7 @@ package com.android.inputmethod.latin.settings;
import android.content.res.Resources; import android.content.res.Resources;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.keyboard.internal.MoreKeySpec;
import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.PunctuationSuggestions; import com.android.inputmethod.latin.PunctuationSuggestions;
@ -68,6 +69,22 @@ public final class SpacingAndPunctuations {
mSuggestPuncList = PunctuationSuggestions.newPunctuationSuggestions(suggestPuncsSpec); mSuggestPuncList = PunctuationSuggestions.newPunctuationSuggestions(suggestPuncsSpec);
} }
@UsedForTesting
public SpacingAndPunctuations(final SpacingAndPunctuations model,
final int[] overrideSortedWordSeparators) {
mSortedSymbolsPrecededBySpace = model.mSortedSymbolsPrecededBySpace;
mSortedSymbolsFollowedBySpace = model.mSortedSymbolsFollowedBySpace;
mSortedSymbolsClusteringTogether = model.mSortedSymbolsClusteringTogether;
mSortedWordConnectors = model.mSortedWordConnectors;
mSortedWordSeparators = overrideSortedWordSeparators;
mSuggestPuncList = model.mSuggestPuncList;
mSentenceSeparator = model.mSentenceSeparator;
mSentenceSeparatorAndSpace = model.mSentenceSeparatorAndSpace;
mCurrentLanguageHasSpaces = model.mCurrentLanguageHasSpaces;
mUsesAmericanTypography = model.mUsesAmericanTypography;
mUsesGermanRules = model.mUsesGermanRules;
}
public boolean isWordSeparator(final int code) { public boolean isWordSeparator(final int code) {
return Arrays.binarySearch(mSortedWordSeparators, code) >= 0; return Arrays.binarySearch(mSortedWordSeparators, code) >= 0;
} }

View File

@ -215,18 +215,23 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
"abc 'def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); "abc 'def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
} }
/**
* Test logic in getting the word range at the cursor.
*/
private static final int[] SPACE = { Constants.CODE_SPACE };
static final int[] TAB = { Constants.CODE_TAB };
private static final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t");
// A character that needs surrogate pair to represent its code point (U+2008A).
private static final String SUPPLEMENTARY_CHAR = "\uD840\uDC8A";
private static final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // あいうえお
private static final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // και
public void testGetWordRangeAtCursor() { public void testGetWordRangeAtCursor() {
/**
* Test logic in getting the word range at the cursor.
*/
final SpacingAndPunctuations SPACE = new SpacingAndPunctuations(
mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE });
final SpacingAndPunctuations TAB = new SpacingAndPunctuations(
mSpacingAndPunctuations, new int[] { Constants.CODE_TAB });
final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t");
// A character that needs surrogate pair to represent its code point (U+2008A).
final String SUPPLEMENTARY_CHAR_STRING = "\uD840\uDC8A";
final SpacingAndPunctuations SUPPLEMENTARY_CHAR = new SpacingAndPunctuations(
mSpacingAndPunctuations, StringUtils.toSortedCodePointArray(
SUPPLEMENTARY_CHAR_STRING));
final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // あいうえお
final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // και
ExtractedText et = new ExtractedText(); ExtractedText et = new ExtractedText();
final MockInputMethodService mockInputMethodService = new MockInputMethodService(); final MockInputMethodService mockInputMethodService = new MockInputMethodService();
final RichInputConnection ic = new RichInputConnection(mockInputMethodService); final RichInputConnection ic = new RichInputConnection(mockInputMethodService);
@ -249,10 +254,9 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
// splitting on supplementary character // splitting on supplementary character
mockInputMethodService.setInputConnection( mockInputMethodService.setInputConnection(
new MockConnection("one word" + SUPPLEMENTARY_CHAR + "wo", "rd", et)); new MockConnection("one word" + SUPPLEMENTARY_CHAR_STRING + "wo", "rd", et));
ic.beginBatchEdit(); ic.beginBatchEdit();
r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR), r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN);
ScriptUtils.SCRIPT_LATIN);
ic.endBatchEdit(); ic.endBatchEdit();
assertTrue(TextUtils.equals("word", r.mWord)); assertTrue(TextUtils.equals("word", r.mWord));
@ -260,8 +264,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
mockInputMethodService.setInputConnection( mockInputMethodService.setInputConnection(
new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et)); new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et));
ic.beginBatchEdit(); ic.beginBatchEdit();
r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR), r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN);
ScriptUtils.SCRIPT_LATIN);
ic.endBatchEdit(); ic.endBatchEdit();
assertTrue(TextUtils.equals("word", r.mWord)); assertTrue(TextUtils.equals("word", r.mWord));
@ -269,8 +272,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
mockInputMethodService.setInputConnection( mockInputMethodService.setInputConnection(
new MockConnection("text" + GREEK_WORD, "text", et)); new MockConnection("text" + GREEK_WORD, "text", et));
ic.beginBatchEdit(); ic.beginBatchEdit();
r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR), r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_GREEK);
ScriptUtils.SCRIPT_GREEK);
ic.endBatchEdit(); ic.endBatchEdit();
assertTrue(TextUtils.equals(GREEK_WORD, r.mWord)); assertTrue(TextUtils.equals(GREEK_WORD, r.mWord));
} }
@ -286,6 +288,8 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
} }
private void helpTestGetSuggestionSpansAtWord(final int cursorPos) { private void helpTestGetSuggestionSpansAtWord(final int cursorPos) {
final SpacingAndPunctuations SPACE = new SpacingAndPunctuations(
mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE });
final MockInputMethodService mockInputMethodService = new MockInputMethodService(); final MockInputMethodService mockInputMethodService = new MockInputMethodService();
final RichInputConnection ic = new RichInputConnection(mockInputMethodService); final RichInputConnection ic = new RichInputConnection(mockInputMethodService);