Skip to content

feat(toolbar): UNDO_WORD key — revert last commit to alternatives (#35)#66

Merged
AsafMah merged 5 commits into
devfrom
feat/a10-undo-word
Jun 7, 2026
Merged

feat(toolbar): UNDO_WORD key — revert last commit to alternatives (#35)#66
AsafMah merged 5 commits into
devfrom
feat/a10-undo-word

Conversation

@AsafMah

@AsafMah AsafMah commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Implements the toolbar-key half of #35 (A10).

What

An Undo-word toolbar key that reverts the last committed (gesture) word straight back to its alternatives in the suggestion strip — modeled end-to-end on the existing JOIN_NEXT toolbar key.

  • KeyCode.UNDO_WORD = -251 (next free toolbar code after JOIN_NEXT/-249, FORCE_NEXT_SPACE/-250) + added to the working-codes list.
  • ToolbarKey.UNDO_WORD + getCodeForToolbarKey mapping; ic_undo / ic_undo_rounded icons; talkback description.
  • InputLogic.handleUndoWord: guarded by mLastComposedWord.canRevertCommit(), then reuses the backspace revert sequence (revertCommit + restartSuggestionsOnWordTouchedByCursor). revertCommit re-commits the original word with its SuggestionSpans; the restart reads those spans and repopulates the strip with the alternatives. For a gesture commit didCommitTypedWord() is false, so canRevertCommit() is true.

Scope

Toolbar key only. The swipe-up/down assignable target (also named in #35) is deferred — it depends on the shortcut-assignment plumbing (shared with #37) that doesn't exist yet.

Verification

  • Compiles (offline flavor, Java+Kotlin); :app:testOfflineRunTestsUnitTest = 228 tests, 4 failed (all the Windows-only ParserTest set; pass on Linux CI) — zero new failures.
  • Needs on-device check: that gesture-committed words restore their original alternatives (gesture words should carry a SpannableString with SuggestionSpans; if a committed word is a plain String, the strip would show dictionary-fresh suggestions instead). Also untested for no-spaces scripts (takes the same revertCommit branch as backspace).

Implemented by a subagent; coordinator fixed a private-field access (mActivecanRevertCommit()), compiled, and ran the suite. Base dev, leaving for review.

AsafMah added 2 commits June 7, 2026 09:45
…#35)

Adds an Undo-word toolbar key that reverts the last committed (gesture) word back
to its alternatives in the suggestion strip, modeled on the JOIN_NEXT toolbar key.

- New KeyCode.UNDO_WORD (-251, next free toolbar code); ToolbarKey.UNDO_WORD +
  getCodeForToolbarKey mapping; ic_undo / ic_undo_rounded icons; talkback string.
- InputLogic.handleUndoWord: guarded by mLastComposedWord.canRevertCommit(), reuses
  the backspace revert sequence (revertCommit + restartSuggestionsOnWordTouchedByCursor)
  so the committed word's SuggestionSpans repopulate the strip with its alternatives.

Scope: toolbar key only. The swipe-up/down assignable target (also in #35) is deferred
until the shortcut-assignment plumbing (shared with #37) exists.

Needs on-device verification that gesture-committed words restore their alternatives
(vs dictionary-fresh suggestions) — see PR notes.
On-device testing found the revertCommit reuse was wrong: it deletes
committedWord.length()+separator before the CURRENT cursor, but after a gesture
the trailing space is a pending PHANTOM space (not in the text), so it over-deleted
into the previous word; on stale/empty state it deleted the wrong span -> garbage.

Drop revertCommit entirely. handleUndoWord now just calls
restartSuggestionsOnWordTouchedByCursor: after a gesture the cursor sits right
after the committed word (phantom space not in text), so that word is re-opened
for re-selection with its alternatives shown — exactly like tapping a word to
re-edit it. Non-destructive (no delete -> no garbage, no over-delete), and its
existing guards no-op safely when the cursor isn't on a word / has selection /
no-spaces language / suggestions disabled.

Also: distinct icon — UNDO_WORD now uses ic_edit (UNDO already uses ic_undo).
@AsafMah

AsafMah commented Jun 7, 2026

Copy link
Copy Markdown
Owner Author

Reworked after your on-device testing. Root cause of the bugs: it reused backspace's revertCommit, which deletes committedWord+separator before the current cursor — but after a gesture the trailing space is a phantom space (not in the text), so it over-deleted by one (your #3), and on stale/empty state deleted the wrong span (your #2 garbage). Fix: dropped revertCommit; it now re-opens the word at the cursor via the existing non-destructive recorrection path (restartSuggestionsOnWordTouchedByCursor) — no deletion at all, so no garbage/over-delete, and it feels native (same as tapping a word to re-edit). Icon: now ic_edit (distinct from regular UNDO's ic_undo). Rebuilding the test build for you to retest.

AsafMah added 3 commits June 7, 2026 10:16
…sh lookup (#35)

restartSuggestionsOnWordTouchedByCursor reads suggestion spans from the EDITOR
text, but gesture commits don't leave them there, so it fell into a fresh
typing-style lookup -> nonsense suggestions.

Read the alternatives from mLastComposedWord.mCommittedWord's in-memory
SuggestionSpan instead (the actual gesture/autocorrect candidates the original
revertCommit used). Still non-destructive: verify the committed word is exactly
before the cursor, set the composing region over it (no delete), and show those
alternatives; no-op if the word moved/changed or has no real alternatives.
…space (#35)

The real value is AFTER you've pressed space (the strip already shows alternatives
right after a word). mLastComposedWord.deactivate() runs on the space keypress, but
the object + its in-memory SuggestionSpan alternatives survive. So handleUndoWord now
finds the committed word before the cursor allowing a trailing whitespace separator,
moves the cursor back into it (setSelection), re-composes it, and shows its original
alternatives. Non-destructive (no deletion). No-ops if the last word before the cursor
isn't the committed one or it has no alternatives.
…#35)

Root cause of the no-ops: commitChosenWord stores the PLAIN chosenWord as
mLastComposedWord.mCommittedWord (the spans-carrying string only goes to the
editor at commitText), so reading SuggestionSpans off mCommittedWord always found
none -> size<=1 -> no-op.

The candidates actually live in the editor text's SuggestionSpans. So: move the
cursor to the end of the last word before the cursor (skip a trailing space), then
postResumeSuggestions(delay) -> MSG_RESUME_SUGGESTIONS -> restartSuggestionsOnWordTouchedByCursor,
which reads the word's editor spans after the cursor settles. Non-destructive,
race-free (uses the standard resume mechanism).
@AsafMah

AsafMah commented Jun 7, 2026

Copy link
Copy Markdown
Owner Author

On-device verified (S24+): after a word + space, Undo word jumps back to the word and shows its real alternatives. (Root cause was that the spans live only in the editor text, not mLastComposedWord.) ✅

@AsafMah
AsafMah merged commit efcf1d5 into dev Jun 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant