aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Chalard <jchalard@google.com>2014-01-17 10:40:05 +0900
committerJean Chalard <jchalard@google.com>2014-01-27 18:17:33 +0900
commitc7ef305bbc119b820fd619d3ed205198d4f98c3f (patch)
treee219c2485b2da81ac5a9a6f2373dce60408bc765
parent963d97af6d83f62c48a4395caca8ac64972f8a57 (diff)
downloadlatinime-c7ef305bbc119b820fd619d3ed205198d4f98c3f.tar.gz
latinime-c7ef305bbc119b820fd619d3ed205198d4f98c3f.tar.xz
latinime-c7ef305bbc119b820fd619d3ed205198d4f98c3f.zip
Try to figure out whether d.quotes open or close.
Bug: 8911672 Change-Id: I5d5635949530a67f95e5208986907251b7bce903
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java11
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java26
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java44
-rw-r--r--tests/src/com/android/inputmethod/latin/PunctuationTests.java28
4 files changed, 103 insertions, 6 deletions
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 325a0d981..0d0b7a160 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -815,6 +815,17 @@ public final class RichInputConnection {
}
/**
+ * 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
* cursor used to be initially in the editor at the time it first received the focus; this
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 6e9bdc34a..d1b25141c 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -602,8 +602,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) {
@@ -630,14 +643,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
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 6f15b11bf..b154623ae 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -348,7 +348,7 @@ public final class StringUtils {
boolean hasPeriod = false;
int codePoint = 0;
while (i > 0) {
- codePoint = Character.codePointBefore(text, i);
+ codePoint = Character.codePointBefore(text, i);
if (codePoint < Constants.CODE_PERIOD || codePoint > 'z') {
// Handwavy heuristic to see if that's a URL character. Anything between period
// and z. This includes all lower- and upper-case ascii letters, period,
@@ -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) {
diff --git a/tests/src/com/android/inputmethod/latin/PunctuationTests.java b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
index d5c06e223..556af0906 100644
--- a/tests/src/com/android/inputmethod/latin/PunctuationTests.java
+++ b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
@@ -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());
+ }
}