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);
|
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
|
* 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
|
* 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,
|
final boolean swapWeakSpace = maybeStripSpace(settingsValues, codePoint, spaceState,
|
||||||
isFromSuggestionStrip);
|
isFromSuggestionStrip);
|
||||||
|
|
||||||
if (SpaceState.PHANTOM == spaceState &&
|
final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint
|
||||||
settingsValues.isUsuallyPrecededBySpace(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);
|
promotePhantomSpace(settingsValues);
|
||||||
}
|
}
|
||||||
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
|
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
|
||||||
|
@ -698,14 +711,17 @@ public final class InputLogic {
|
||||||
if (swapWeakSpace) {
|
if (swapWeakSpace) {
|
||||||
swapSwapperAndSpace(keyboardSwitcher);
|
swapSwapperAndSpace(keyboardSwitcher);
|
||||||
mSpaceState = SpaceState.SWAP_PUNCTUATION;
|
mSpaceState = SpaceState.SWAP_PUNCTUATION;
|
||||||
} else if (SpaceState.PHANTOM == spaceState
|
} else if ((SpaceState.PHANTOM == spaceState
|
||||||
&& settingsValues.isUsuallyFollowedBySpace(codePoint)) {
|
&& 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// between swappers and strippers), so we should not stay in phantom space state if
|
||||||
|
|
|
@ -387,6 +387,48 @@ public final class StringUtils {
|
||||||
return false;
|
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) {
|
public static boolean isEmptyStringOrWhiteSpaces(final String s) {
|
||||||
final int N = codePointCount(s);
|
final int N = codePointCount(s);
|
||||||
for (int i = 0; i < N; ++i) {
|
for (int i = 0; i < N; ++i) {
|
||||||
|
|
|
@ -169,4 +169,32 @@ public class PunctuationTests extends InputTestsBase {
|
||||||
+ " ; Suggestions = " + mLatinIME.getSuggestedWords(),
|
+ " ; Suggestions = " + mLatinIME.getSuggestedWords(),
|
||||||
EXPECTED_RESULT, mEditText.getText().toString());
|
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