macos: send all insertText commits as key events

Previously, insertText commits without marked text were delivered via
sendText, which applies paste semantics and wraps the text in bracketed
paste when the program enables it. macOS dictation and other input
methods often commit without marked text, so programs treated dictated
text as a paste and applied paste-specific handling.

insertText is only invoked by input methods (IME, dictation, emoji
picker, character viewer); real paste operations use a separate path.
Send every non-empty commit through the key event path so programs
interpret input method text as typed input. Typing is unaffected (the
accumulator path returns earlier) and Cmd+V pastes are unaffected.

The helper is renamed from committedPreeditTextAction to
committedTextAction since it no longer only handles preedit commits.
This commit is contained in:
Samuel Huang
2026-08-14 11:29:38 -04:00
parent 43fe699071
commit ecbeb60ca5

View File

@@ -1212,7 +1212,7 @@ extension Ghostty {
continue
}
_ = committedPreeditTextAction(action, text: text)
_ = committedTextAction(action, text: text)
}
if shouldReplayCommittedPreeditKey(translationEvent) {
@@ -1512,7 +1512,7 @@ extension Ghostty {
}
}
private func committedPreeditTextAction(
private func committedTextAction(
_ action: ghostty_input_action_e,
text: String
) -> Bool {
@@ -2063,7 +2063,6 @@ extension Ghostty.SurfaceView: NSTextInputClient {
func insertText(_ string: Any, replacementRange: NSRange) {
// We must have an associated event
guard NSApp.currentEvent != nil else { return }
guard let surfaceModel else { return }
// We want the string view of the any value
var chars = ""
@@ -2089,8 +2088,6 @@ extension Ghostty.SurfaceView: NSTextInputClient {
return
}
let hadMarkedText = hasMarkedText()
// If insertText is called, our preedit must be over.
unmarkText()
@@ -2102,14 +2099,11 @@ extension Ghostty.SurfaceView: NSTextInputClient {
return
}
if hadMarkedText, !chars.isEmpty {
// Send preedit commits as key events instead of raw text for
// keybind interpretation by programs.
_ = committedPreeditTextAction(GHOSTTY_ACTION_PRESS, text: chars)
return
// All committed text (IME, dictation, etc.) must be sent as key
// events so programs treat it as typed input, never as a paste.
if !chars.isEmpty {
_ = committedTextAction(GHOSTTY_ACTION_PRESS, text: chars)
}
surfaceModel.sendText(chars)
}
/// This function needs to exist for two reasons: