Skip to content

fix: prevent IME double-resync from stripping list bullet after Enter#722

Open
tfkci wants to merge 1 commit into
MohamedRejeb:mainfrom
tfkci:fix/upstream-ime-resync-guard
Open

fix: prevent IME double-resync from stripping list bullet after Enter#722
tfkci wants to merge 1 commit into
MohamedRejeb:mainfrom
tfkci:fix/upstream-ime-resync-guard

Conversation

@tfkci
Copy link
Copy Markdown

@tfkci tfkci commented May 7, 2026

Problem

When the user presses Enter at the end of a list item, checkForParagraphs creates a new paragraph and inserts its startText (e.g. "• " or "1. ") programmatically into tempTextFieldValue. Some IMEs — in particular Samsung Keyboard on Galaxy devices — respond to this insertion by sending one or more removal callbacks that remove exactly those startText characters from the new, still-empty paragraph.

These callbacks go through TextFieldKeyInput (dispatchKeyEvent) rather than the standard IME composition protocol, so they arrive as normal onValueChange calls. Without a guard, each one is treated as a user Backspace, demoting the paragraph type to DefaultParagraph and losing the bullet/number formatting.

The issue is especially visible with Samsung's word-prediction keyboard, which tends to send the resync twice (double-resync). A single-shot boolean guard (armed once, cleared on first resync) would leave the second resync unprotected.

Fix

justInsertedListParagraph flag — armed by checkForParagraphs immediately after it inserts a non-empty startText for a new list paragraph.

isComposeResync positional check in handleRemovingCharacters — fires when ALL of the following are true:

  • justInsertedListParagraph is set
  • the paragraph is a list type
  • the paragraph body is empty (only startText exists, nothing typed yet)
  • the removal count equals exactly the startText length
  • the removal starts exactly at the startText position

On resync detection: restore the startText into tempTextFieldValue (so updateAnnotatedString sees no net change) and do NOT clear the flag — every subsequent identical resync is handled the same way. The flag is cleared on the first real user action (non-resync removal, or next character typed).

Tests

Three new regression tests in RichTextStateIMETest:

  • testSingleResyncAfterEnter_unorderedList
  • testDoubleResyncAfterEnter_unorderedList
  • testDoubleResyncAfterEnter_orderedList

All existing tests continue to pass.

Affected devices

Samsung Galaxy A-series, M-series, and other Samsung devices with Samsung Keyboard word suggestions enabled. Potentially any IME that routes certain operations through dispatchKeyEvent.

When the user presses Enter at the end of a list item, checkForParagraphs
creates a new paragraph and inserts its startText (e.g. "• " or "1. ")
into tempTextFieldValue. Some IMEs (Samsung keyboard and others) respond
to this programmatic insertion by sending one or more removal callbacks
that remove exactly those startText characters from the new, still-empty
paragraph. These are NOT user Backspaces — they are the IME reconciling
its internal buffer.

The previous code had no guard for this. Each resync was treated as a
real deletion, demoting the paragraph type to DefaultParagraph and losing
the bullet/number formatting.

Fix:

- Add `justInsertedListParagraph` flag, armed by checkForParagraphs
  whenever it inserts a non-empty startText for a new list paragraph.

- In handleRemovingCharacters, compute `isComposeResync` by checking
  all of: the flag is set, the paragraph is a list type, the paragraph
  body is still empty, the removal count equals exactly the startText
  length, and the removal starts exactly at the startText position.

- On resync detection: restore the startText into tempTextFieldValue
  (so updateAnnotatedString sees no net change) and do NOT clear the
  flag — repeated resyncs from persistent IMEs are handled identically.

- The flag is cleared on the first real user action (any non-resync
  removal, or the next character addition).

Adds three regression tests: single resync, double resync on unordered
list, and double resync on ordered list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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