Skip to content

Batch commands together when editing text in LegacyPlatformTextInputServiceAdapter#2722

Merged
m-sasha merged 1 commit intojb-mainfrom
m-sasha/fix-wubi-input
Feb 1, 2026
Merged

Batch commands together when editing text in LegacyPlatformTextInputServiceAdapter#2722
m-sasha merged 1 commit intojb-mainfrom
m-sasha/fix-wubi-input

Conversation

@m-sasha
Copy link
Copy Markdown

@m-sasha m-sasha commented Jan 27, 2026

The skiko implementation of LegacyPlatformTextInputServiceAdapter currently runs each EditCommand immediately as it is requested.

This is problematic because:

  • It differs from the implementation for BasicTextField(TextFieldState) (in TextInputSession.skiko.kt) which buffers the commands in TransformedTextFieldState.editUntransformedTextAsUser.
  • In the case of BasicTextField(TextFieldValue) it causes calls to onValueChange with intermediate states. Combined with the if (value != it) check in BasicTextField(TextFieldValue) before calling the upstream onValueChange this makes the final state (even more) dependent on the order and timing of recompositions. See the scenario detailed below for an example.

This PR changes the behavior of LegacyPlatformTextInputServiceAdapter to instead buffer commands and commit them together once the editing block returns.

This change also semi-accidentally fixes CMP-9380 Wubi input method; first letter vanishes on 2nd keystroke. The issue there is that for Wubi input, the JVM sends each composing InputMethodEvent twice. Each such an event tells us the committed text is empty, and adds a character to the composing text. For example, when the user presses 'q', the event sent is InputMethodEvent[committed="", composing="q"]. For each such event DesktopTextInputService2 performs two editing commands:

  • Commit the committed text.
  • Set the composing text.

When two such events are sent, with a recomposition between them, the state changes as follows:

  1. First InputMethodEvent[committed="", composing="q"] is received by DesktopTextInputService2, and it:
    1. Commits "", which doesn't change the text state, and so isn't delivered to onValueChange.
    2. Sets the composing text to "q", which is delivered to onValueChange. The hoisted value is now TextFieldValue(text='q', selection=TextRange(1, 1), composition=TextRange(0, 1))
  2. A recomposition occurs, which calls BasicTextField with the new hoisted value.
  3. Second InputMethodEvent[committed="", composing="q"] is received by DesktopTextInputService2, and it:
    1. Commits "", which is delivered to onValueChange, changing the hoisted value to TextFieldValue(text='', selection=TextRange(0, 0), composition=null)
    2. Sets the composing text to "q", which attempts to deliver the new TextFieldValue(text='q', selection=TextRange(1, 1), composition=TextRange(0, 1)) to onValueChange, but actually fails to do so because this value is identical to the last recomposed value in BasicTextField which only calls the "upstream" onValueChange if the value is actually changed:
CoreTextField(
    value = value,
    onValueChange = {
        if (value != it) {
            onValueChange(it)
        }
    },
    ...
)

So the final state ends up being the value delivered in 3.1, which is just the empty text value.

When instead each pair of changes (commit + setComposition) are grouped:

  • The state after 1.2 is the same (correct state)
  • onValueChange is not called in step 3.1. This is the crucial difference.
  • onValueChange is attempted to be called in step 3.2, but isn't because the new value is identical to the recomposed one.

Altogether, the final state is the correct TextFieldValue(text='q', selection=TextRange(1, 1), composition=TextRange(0, 1))

Fixes https://youtrack.jetbrains.com/issue/CMP-9380

Testing

Tested manually and added unit tests.

Release Notes

Fixes - Desktop

  • [macOS] Fix Wubi input for (Basic)TextField(TextFieldValue).

@m-sasha m-sasha requested review from ASalavei and igordmn January 27, 2026 10:06
@m-sasha m-sasha changed the title Run all text commands together when editing text in LegacyPlatformTextInputServiceAdapter Batch commands together when editing text in LegacyPlatformTextInputServiceAdapter Jan 27, 2026
@m-sasha m-sasha merged commit b632098 into jb-main Feb 1, 2026
22 checks passed
@m-sasha m-sasha deleted the m-sasha/fix-wubi-input branch February 1, 2026 10:44
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.

3 participants