Fix live-converge word casing: stuck all-caps (#4) + dropped auto-cap (#5)#6
Merged
Merged
Conversation
Casing bugs specific to two-thumb live-converge / multi-part re-recognition, where every extending tap or swipe REPLACES the whole composing word with the recognizer's fresh (lowercase) output: - Dropped auto-cap (#5): "Hello" -> "hellow" on the first extension. - Stuck all-caps (#4): a short ambiguous swipe whose top pick is an all-caps acronym ("CSA") set WordComposer.isAllUpperCase, which forced every later suggestion upper, so the word stuck in caps ("CSA"->"CAN"->"CAME"). - Swipe-extension downcasing: "Was"+swipe -> "wait" (a second swipe re-entered onStartBatchInput and re-captured the now-cleared shift state). Approach: separate a word's casing INTENT from the recognizer's letters. - WordComposer.mCapitalizedMode is the persistent per-word intent: seeded at word start from auto-cap + shift, it survives the setBatchInputWord rebuild and is cleared only at commitWord. Exposed via getCapitalizedMode(). - New InputLogic.applyComposingCase(lemma, capsMode, locale) treats the recognizer output as a casing-NEUTRAL lemma (lowercased first) and re-applies the intent. Lowercasing first dissolves #4 at the source: the composing word is never all-caps, so isAllUpperCase never arms. - onStartBatchInput captures the intent only for a FRESH word (gated on !extendComposingWord), so an extending gesture preserves the first fragment's intent instead of re-capturing the auto-cleared shift state (#5 / "Wait"). - Fresh-word gesture capitalization is unchanged (still mShiftModeAtGestureStart, captured before any state mutates), so plain glide typing is byte-identical and a standalone acronym swipe ("CSA") still stays as-is. Tests: applyComposingCase covered directly (pure, native-free) plus intent lifecycle tests in InputLogicTest. :app:testOfflineDebugUnitTest green except the 3 documented pre-existing failures. Fixes #4, #5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cc21c01 to
b1729bd
Compare
Owner
Author
|
Force-pushed a refined implementation (replaces the earlier prior-word heuristic). Why the change: the prior-word approach mis-fired on a sentence-start acronym ( New approach: separate a word's casing intent from the recognizer's letters.
Unit-tested directly ( |
This was referenced Jun 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes two casing bugs specific to the live-converge (#1.7) merged-trail re-recognition. Both are gated to that path, so a plain single gesture — including an intentional standalone acronym swipe — is untouched.
#4 — word gets stuck in all-caps
A short, ambiguous swipe can make the glide recognizer's top pick an all-caps dictionary acronym (e.g.
CSA), while the keyboard is unshifted. That setsWordComposer.isAllUpperCase, which makesSuggest.getSuggestedWordsForBatchInputforce every later candidate to all-caps; live-converge then keeps replacing the word with the uppercased result, so it stays stuck (CSA → CAN → CAME, suggestions shown in caps) until the word is committed. Confirmed from a debug log — there is nosetShiftLocked=true, so it isn't a caps-lock/Shift-key bug.#5 — extending a word drops the auto-capital
The first-letter capital comes from
mShiftModeAtGestureStart, captured at gesture start and cleared after the first commit. A live-converge extending tap re-recognizes the whole word and re-commits with that value alreadyOFF, soHellobecomeshellow.Fix
In
InputLogic.onUpdateTailBatchInputCompleted, whenusedMergedTrailis set, re-derive the case from the word being re-recognized:CAPITALIZE_FIRST→ re-apply the leading capital (fixes Live-converge: extending a word drops the auto-capitalized first letter #5 — capital survives every re-converge);CAPITALIZE_ALL→ lowercase it unless the keyboard is genuinely shift-locked (fixes Live-converge: word gets stuck in all-caps after recognizer returns an acronym #4 — stops theisAllUpperCaselatch at the source; deliberate caps-lock still works).Trade-off: gliding an acronym and then extending it normalizes to lowercase. A standalone acronym swipe (no extension) is unaffected.
Testing
:app:compileStandardDebugJavaWithJavacclean on this base (offmain).InputLogicTest.extendBase*(live-converge/merged-trail regression) green on the source branch.Fixes #4
Fixes #5