fix(macos/ime): skip key-equivalent priority path while IME is composing (#9709)#9711
fix(macos/ime): skip key-equivalent priority path while IME is composing (#9709)#9711maxmilian wants to merge 1 commit intowarpdotdev:masterfrom
Conversation
Navigation keys with the function-key modifier flag (e.g. arrow keys, modifierFlags=0xa00100) qualify as key equivalents on macOS, so AppKit fires performKeyEquivalent: before keyDown:. The previous implementation called keyDownImpl from performKeyEquivalent:, which invokes interpretKeyEvents and forwards to Rust. During IME composition, Rust suppresses the keystroke and keyDownImpl returns NO, so we fall through to [super performKeyEquivalent:]. AppKit then dispatches keyDown: to the host view, and interpretKeyEvents runs a second time. Most macOS IMEs render their candidate UI through IMKCandidates (a separate NSPanel), so they only observe the first delivery. 超注音 / Yahoo Bopomofo, however, draws its candidate panel itself and listens on the input context directly, so it receives both deliveries and advances candidate selection by 2 per arrow press. Skip the priority path entirely while hasMarkedText is YES so AppKit routes the event through keyDown: exactly once. Confirmed via NSLog instrumentation: arrow press during composition now produces a single keyDownImpl / interpretKeyEvents pair instead of two. Fixes warpdotdev#9709 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have the users @maxmilian on file. In order for us to review and merge your code, each contributor must visit https://cla.warp.dev to read and agree to our CLA. Once you have done so, please comment |
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I reviewed this pull request and requested human review from: @vorporeal. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR skips Warp's key-equivalent priority path while macOS marked text is active, allowing AppKit to deliver IME composition keys through the normal keyDown: path exactly once.
Concerns
- No blocking correctness or security concerns found in the changed lines.
Verdict
Found: 0 critical, 0 important, 0 suggestions
Approve
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
Description
Fixes #9709 — on macOS, third-party IMEs that listen on the input context (e.g. 超注音 / Yahoo Bopomofo) observed every arrow keystroke twice during candidate selection, advancing the highlight by 2 instead of 1.
Root cause
Arrow keys carry the function-key flag (
modifierFlags=0xa00100), so they qualify as key equivalents and AppKit dispatches them throughperformKeyEquivalent:beforekeyDown:. Incrates/warpui/src/platform/mac/objc/window.m::performKeyEquivalent:, the previous implementation:[self.contentView keyDownImpl:event]— which invokesinterpretKeyEvents:and forwards to Rustis_composing=true), sokeyDownImplreturnedNO[super performKeyEquivalent:event]keyDown:on the host view →keyDownImplagain →interpretKeyEventsran a second timeApple's built-in IME uses
IMKCandidates(a separateNSPanel) for candidate selection, so the second delivery doesn't reach its candidate index. 超注音 draws its candidate panel itself and listens on the input context directly — it observes both deliveries and advances by 2.Fix
Skip the priority path in
performKeyEquivalent:whilehasMarkedTextisYES. AppKit then routes the event through the standardkeyDown:chain exactly once.Testing
NSLog instrumentation (before/after)
I added per-call instrumentation around
keyDownImpl,interpretKeyEvents,setMarkedText:,insertText:,unmarkText,doCommandBySelector:, andperformKeyEquivalent:and reproduced with 超注音 (Yahoo Bopomofo) on macOS 26.4.1.Before the fix — single right-arrow press during candidate selection:
After the fix:
Manual verification (macOS)
hasMarkedTextguard only triggers during active composition, so the priority path still executes for ordinary key equivalents. ✅No automated tests were added: the bug requires running an IME on macOS and observing AppKit's two-stage event delivery (
performKeyEquivalent:→keyDown:), which is impractical to simulate in the existing unit/integration test harnesses. The change is small, well-scoped (performKeyEquivalent:only), and the guard short-circuits to[super performKeyEquivalent:]on the same path AppKit would fall back to anyway.Server API dependencies
None — this is a pure native-input-handling change.
Agent Mode
Changelog Entries for Stable
CHANGELOG-BUG-FIX: macOS: fix third-party IMEs (e.g. 超注音 / Yahoo Bopomofo) advancing candidate selection by 2 per arrow press during composition.