fix: prevent Japanese IME confirmation Enter from executing command (#2075)

* fix: prevent Japanese IME confirmation Enter from executing command

Korean IME commits a syllable and executes on a single Enter, but
Japanese/Chinese IME use Enter only to confirm conversion — a second
Enter is needed to execute. Restrict the extra Return forwarding in
shouldSendCommittedIMEConfirmKey to Korean input sources only.

* refactor: use case-insensitive check for Korean input source ID
This commit is contained in:
Hiroki Kajiwara 2026-03-25 14:24:13 +09:00 committed by GitHub
parent 22448197de
commit a395e8c343
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 17 additions and 1 deletions

View file

@ -5928,7 +5928,12 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
private func shouldSendCommittedIMEConfirmKey(event: NSEvent, markedTextBefore: Bool) -> Bool {
guard markedTextBefore, markedText.length == 0 else { return false }
return event.keyCode == 36 || event.keyCode == 76
guard event.keyCode == 36 || event.keyCode == 76 else { return false }
// Korean IME: Enter commits the syllable AND executes the command (single step).
// Japanese/Chinese IME: Enter only confirms the conversion; a second Enter executes.
// Only send the extra Return key for Korean input sources.
guard let sourceId = KeyboardLayout.id else { return false }
return sourceId.range(of: "korean", options: .caseInsensitive) != nil
}
private func ghosttyKeyEvent(for event: NSEvent, surface: ghostty_surface_t) -> ghostty_input_key_s {

View file

@ -2,8 +2,16 @@ import AppKit
import Carbon
class KeyboardLayout {
/// Test-only override for the current input source ID.
#if DEBUG
static var debugInputSourceIdOverride: String?
#endif
/// Return a string ID of the current keyboard input source.
static var id: String? {
#if DEBUG
if let override = debugInputSourceIdOverride { return override }
#endif
if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(),
let sourceIdPointer = TISGetInputSourceProperty(source, kTISPropertyInputSourceID) {
let sourceId = Unmanaged<CFString>.fromOpaque(sourceIdPointer).takeUnretainedValue()

View file

@ -1038,6 +1038,8 @@ final class KoreanIMEReturnCommitRegressionTests: XCTestCase {
view.setMarkedText("", selectedRange: NSRange(location: 0, length: 1), replacementRange: NSRange(location: NSNotFound, length: 0))
// Simulate Korean input source so shouldSendCommittedIMEConfirmKey fires
KeyboardLayout.debugInputSourceIdOverride = "com.apple.inputmethod.Korean.2SetKorean"
installCJKIMEInterpretKeyEventsSwizzle()
cjkIMEInterpretKeyEventsHook = { candidateView, _ in
guard candidateView === view else { return false }
@ -1045,6 +1047,7 @@ final class KoreanIMEReturnCommitRegressionTests: XCTestCase {
return true
}
defer {
KeyboardLayout.debugInputSourceIdOverride = nil
cjkIMEInterpretKeyEventsHook = nil
}