am 6d1c8b2c: Merge "Try to figure out whether d.quotes open or close."
* commit '6d1c8b2c3f78969866db7f718e7e8d2be240f674': Try to figure out whether d.quotes open or close.main
commit
7f1a5c4d9b
|
@ -814,6 +814,17 @@ public final class RichInputConnection {
|
|||
return StringUtils.lastPartLooksLikeURL(mCommittedTextBeforeComposingText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks at the text just before the cursor to find out if we are inside a double quote.
|
||||
*
|
||||
* As with #textBeforeCursorLooksLikeURL, this is dependent on how much text we have cached.
|
||||
* However this won't be a concrete problem in most situations, as the cache is almost always
|
||||
* long enough for this use.
|
||||
*/
|
||||
public boolean isInsideDoubleQuoteOrAfterDigit() {
|
||||
return StringUtils.isInsideDoubleQuoteOrAfterDigit(mCommittedTextBeforeComposingText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get the text from the editor to expose lies the framework may have been
|
||||
* telling us. Concretely, when the device rotates, the frameworks tells us about where the
|
||||
|
|
|
@ -670,8 +670,21 @@ public final class InputLogic {
|
|||
final boolean swapWeakSpace = maybeStripSpace(settingsValues, codePoint, spaceState,
|
||||
isFromSuggestionStrip);
|
||||
|
||||
if (SpaceState.PHANTOM == spaceState &&
|
||||
settingsValues.isUsuallyPrecededBySpace(codePoint)) {
|
||||
final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint
|
||||
&& mConnection.isInsideDoubleQuoteOrAfterDigit();
|
||||
|
||||
final boolean needsPrecedingSpace;
|
||||
if (SpaceState.PHANTOM != spaceState) {
|
||||
needsPrecedingSpace = false;
|
||||
} else if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
|
||||
// Double quotes behave like they are usually preceded by space iff we are
|
||||
// not inside a double quote or after a digit.
|
||||
needsPrecedingSpace = !isInsideDoubleQuoteOrAfterDigit;
|
||||
} else {
|
||||
needsPrecedingSpace = settingsValues.isUsuallyPrecededBySpace(codePoint);
|
||||
}
|
||||
|
||||
if (needsPrecedingSpace) {
|
||||
promotePhantomSpace(settingsValues);
|
||||
}
|
||||
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
|
||||
|
@ -698,14 +711,17 @@ public final class InputLogic {
|
|||
if (swapWeakSpace) {
|
||||
swapSwapperAndSpace(keyboardSwitcher);
|
||||
mSpaceState = SpaceState.SWAP_PUNCTUATION;
|
||||
} else if (SpaceState.PHANTOM == spaceState
|
||||
&& settingsValues.isUsuallyFollowedBySpace(codePoint)) {
|
||||
} else if ((SpaceState.PHANTOM == spaceState
|
||||
&& settingsValues.isUsuallyFollowedBySpace(codePoint))
|
||||
|| (Constants.CODE_DOUBLE_QUOTE == codePoint
|
||||
&& isInsideDoubleQuoteOrAfterDigit)) {
|
||||
// If we are in phantom space state, and the user presses a separator, we want to
|
||||
// stay in phantom space state so that the next keypress has a chance to add the
|
||||
// space. For example, if I type "Good dat", pick "day" from the suggestion strip
|
||||
// then insert a comma and go on to typing the next word, I want the space to be
|
||||
// inserted automatically before the next word, the same way it is when I don't
|
||||
// input the comma.
|
||||
// input the comma. A double quote behaves like it's usually followed by space if
|
||||
// we're inside a double quote.
|
||||
// The case is a little different if the separator is a space stripper. Such a
|
||||
// separator does not normally need a space on the right (that's the difference
|
||||
// between swappers and strippers), so we should not stay in phantom space state if
|
||||
|
|
|
@ -387,6 +387,48 @@ public final class StringUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the string and returns whether we're inside a double quote.
|
||||
*
|
||||
* This is used to decide whether we should put an automatic space before or after a double
|
||||
* quote character. If we're inside a quotation, then we want to close it, so we want a space
|
||||
* after and not before. Otherwise, we want to open the quotation, so we want a space before
|
||||
* and not after. Exception: after a digit, we never want a space because the "inch" or
|
||||
* "minutes" use cases is dominant after digits.
|
||||
* In the practice, we determine whether we are in a quotation or not by finding the previous
|
||||
* double quote character, and looking at whether it's followed by whitespace. If so, that
|
||||
* was a closing quotation mark, so we're not inside a double quote. If it's not followed
|
||||
* by whitespace, then it was an opening quotation mark, and we're inside a quotation.
|
||||
*
|
||||
* @param text the text to examine.
|
||||
* @return whether we're inside a double quote.
|
||||
*/
|
||||
public static boolean isInsideDoubleQuoteOrAfterDigit(final CharSequence text) {
|
||||
int i = text.length();
|
||||
if (0 == i) return false;
|
||||
int codePoint = Character.codePointBefore(text, i);
|
||||
if (Character.isDigit(codePoint)) return true;
|
||||
int prevCodePoint = 0;
|
||||
while (i > 0) {
|
||||
codePoint = Character.codePointBefore(text, i);
|
||||
if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
|
||||
// If we see a double quote followed by whitespace, then that
|
||||
// was a closing quote.
|
||||
if (Character.isWhitespace(prevCodePoint)) return false;
|
||||
}
|
||||
if (Character.isWhitespace(codePoint) && Constants.CODE_DOUBLE_QUOTE == prevCodePoint) {
|
||||
// If we see a double quote preceded by whitespace, then that
|
||||
// was an opening quote. No need to continue seeking.
|
||||
return true;
|
||||
}
|
||||
i -= Character.charCount(codePoint);
|
||||
prevCodePoint = codePoint;
|
||||
}
|
||||
// We reached the start of text. If the first char is a double quote, then we're inside
|
||||
// a double quote. Otherwise we're not.
|
||||
return Constants.CODE_DOUBLE_QUOTE == codePoint;
|
||||
}
|
||||
|
||||
public static boolean isEmptyStringOrWhiteSpaces(final String s) {
|
||||
final int N = codePointCount(s);
|
||||
for (int i = 0; i < N; ++i) {
|
||||
|
|
|
@ -169,4 +169,32 @@ public class PunctuationTests extends InputTestsBase {
|
|||
+ " ; Suggestions = " + mLatinIME.getSuggestedWords(),
|
||||
EXPECTED_RESULT, mEditText.getText().toString());
|
||||
}
|
||||
|
||||
public void testAutoSpaceWithDoubleQuotes() {
|
||||
final String STRING_TO_TYPE = "He said\"hello\"to me. I replied,\"hi\"."
|
||||
+ "Then, 5\"passed. He said\"bye\"and left.";
|
||||
final String EXPECTED_RESULT = "He said \"hello\" to me. I replied, \"hi\". "
|
||||
+ "Then, 5\" passed. He said \"bye\" and left. \"";
|
||||
// Split by double quote, so that we can type the double quotes individually.
|
||||
for (final String partToType : STRING_TO_TYPE.split("\"")) {
|
||||
// Split at word boundaries. This regexp means "anywhere that is preceded
|
||||
// by a word character but not followed by a word character, OR that is not
|
||||
// preceded by a word character but followed by a word character".
|
||||
// We need to input word by word because auto-spaces are only active when
|
||||
// manually picking or gesturing (which we can't simulate yet), but only words
|
||||
// can be picked.
|
||||
final String[] wordsToType = partToType.split("(?<=\\w)(?!\\w)|(?<!\\w)(?=\\w)");
|
||||
for (final String wordToType : wordsToType) {
|
||||
type(wordToType);
|
||||
if (wordToType.matches("^\\w+$")) {
|
||||
// Only pick selection if that was a word, because if that was not a word,
|
||||
// then we don't have a composition.
|
||||
pickSuggestionManually(0, wordToType);
|
||||
}
|
||||
}
|
||||
type("\"");
|
||||
}
|
||||
assertEquals("auto-space with double quotes",
|
||||
EXPECTED_RESULT, mEditText.getText().toString());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue