mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-24 08:01:45 +00:00
macos: send all insertText commits as key events (#13817)
Partially addresses #13796. Extends #13222. 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: opencode collapsed it into a `"[Pasted ~N lines]"` chip and Neovim applied paste-mode handling. `insertText` is only invoked by input methods (IME, dictation, emoji picker, character viewer); real paste operations use a separate path. Every non-empty commit is now sent as a key event — the same path already used for preedit commits since #13222 — so input method text always arrives as typed input. Typing is unaffected (the accumulator path returns earlier) and Cmd+V pastes are unaffected. `committedPreeditTextAction` is renamed to `committedTextAction` since it no longer only handles preedit commits. Testing: - 311 macOS unit tests pass. - Manually verified on macOS 26: dictation into Opencode and Neovim arrives inline with no paste handling; emoji picker inserts inline; Chinese IME composition unchanged; dictation in Neovim normal mode now behaves as keystrokes, matching Terminal.app. Notes: - Dictated "new line" now matches Terminal.app behavior (no newline with typed-text semantics). The previous behavior came from the paste path preserving the newline; a follow-up could deliver it as an Enter keypress if desired. AI usage: drafted with OMO + OpenCode + DeepSeek V4 Pro assistance; reviewed, edited, and manually tested by the author.
This commit is contained in:
@@ -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 {
|
||||
@@ -2074,7 +2074,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 = ""
|
||||
@@ -2100,8 +2099,6 @@ extension Ghostty.SurfaceView: NSTextInputClient {
|
||||
return
|
||||
}
|
||||
|
||||
let hadMarkedText = hasMarkedText()
|
||||
|
||||
// If insertText is called, our preedit must be over.
|
||||
unmarkText()
|
||||
|
||||
@@ -2113,14 +2110,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:
|
||||
|
||||
Reference in New Issue
Block a user