Disclaimer
I'm not deeply familiar with the internals of either ghostel or emacs-rime, and I'm genuinely unsure whether this belongs here or in the emacs-rime tracker. I'm filing it here because ghostel already owns an IME-integration layer (ghostel-ime-mode) that deliberately handles Lisp IMEs which commit by inserting into the buffer — this looks like a second commit path that layer doesn't yet cover. If you feel this is really an emacs-rime problem, please redirect me and I'll refile.
Summary
With emacs-rime active in a ghostel buffer, pressing RET to commit fails with:
rime--return: Buffer is read-only: #<buffer *claude:...*>
Per-character input composes fine (thanks to ghostel-ime-mode — character input is forwarded to the PTY correctly). Only the commit-on-RET path breaks.
Repro
- Emacs 31.0.90 (emacs-plus@31), macOS
- ghostel
28b39a6 (2026-07-02), ghostel-ime-mode enabled via (add-hook 'ghostel-mode-hook #'ghostel-ime-mode)
- emacs-rime
f927d26
- Open a ghostel buffer, activate emacs-rime (
toggle-input-method).
- Type
nihao — the rime preedit/candidates show correctly.
- Press
RET.
- →
rime--return: Buffer is read-only.
Root cause (best understanding)
This is adjacent to #343 and #510. #510 fixed the read-only interaction for the input-method-function path by binding buffer-read-only/inhibit-read-only around the input method inside ghostel-ime--wrap-input-method.
But emacs-rime commits via its own keymap command, not through input-method-function. When a composition is active, rime pushes rime-active-mode-map onto overriding-terminal-local-map, binding RET → rime--return → rime--commit:
(defun rime--commit (value)
"Insert VALUE, then clear state."
(when (and value (rime--rime-lib-module-ready-p))
(rime--clear-overlay)
(insert value) ; <-- hits the read-only barrier; also never reaches the PTY
(rime-lib-clear-composition)
...))
Because this bypasses input-method-function, the ghostel-ime--wrap-input-method wrapper (and #510's read-only lifting) never runs. The (insert value) both trips buffer-read-only and, even if it didn't, would land in the buffer instead of being forwarded to the PTY.
So: same class of problem #343/#510 addressed (a Lisp IME inserting committed text into a protected buffer), but via a second path — a keymap command the IME installs itself — that the current wrapper doesn't intercept.
Workaround I'm using
An :around advice on rime--commit that, in ghostel buffers, forwards the committed text to the PTY instead of inserting:
(with-eval-after-load 'rime
(defun my/rime-commit-to-pty (orig-fn value)
(if (and (derived-mode-p 'ghostel-mode)
(fboundp 'ghostel--send-string)
value)
(progn
(when (fboundp 'rime--clear-overlay) (rime--clear-overlay))
(ghostel--send-string (encode-coding-string value 'utf-8))
(rime-lib-clear-composition)
(rime--redisplay)
(rime--refresh-mode-state))
(funcall orig-fn value)))
(advice-add 'rime--commit :around #'my/rime-commit-to-pty))
This works, but it hardcodes a private rime function (rime--commit), so it's clearly not a general fix. A robust solution probably belongs in ghostel — e.g. intercepting inserts into the protected region at the read-only-barrier level so that any IME committing through its own command path is forwarded to the PTY, not just those going through input-method-function. I'll defer to your judgment on the right layer.
Happy to test patches. Thanks for ghostel!
Disclaimer
I'm not deeply familiar with the internals of either ghostel or emacs-rime, and I'm genuinely unsure whether this belongs here or in the emacs-rime tracker. I'm filing it here because ghostel already owns an IME-integration layer (
ghostel-ime-mode) that deliberately handles Lisp IMEs which commit by inserting into the buffer — this looks like a second commit path that layer doesn't yet cover. If you feel this is really an emacs-rime problem, please redirect me and I'll refile.Summary
With emacs-rime active in a ghostel buffer, pressing
RETto commit fails with:Per-character input composes fine (thanks to
ghostel-ime-mode— character input is forwarded to the PTY correctly). Only the commit-on-RET path breaks.Repro
28b39a6(2026-07-02),ghostel-ime-modeenabled via(add-hook 'ghostel-mode-hook #'ghostel-ime-mode)f927d26toggle-input-method).nihao— the rime preedit/candidates show correctly.RET.rime--return: Buffer is read-only.Root cause (best understanding)
This is adjacent to #343 and #510. #510 fixed the read-only interaction for the
input-method-functionpath by bindingbuffer-read-only/inhibit-read-onlyaround the input method insideghostel-ime--wrap-input-method.But emacs-rime commits via its own keymap command, not through
input-method-function. When a composition is active, rime pushesrime-active-mode-mapontooverriding-terminal-local-map, bindingRET→rime--return→rime--commit:Because this bypasses
input-method-function, theghostel-ime--wrap-input-methodwrapper (and #510's read-only lifting) never runs. The(insert value)both tripsbuffer-read-onlyand, even if it didn't, would land in the buffer instead of being forwarded to the PTY.So: same class of problem #343/#510 addressed (a Lisp IME inserting committed text into a protected buffer), but via a second path — a keymap command the IME installs itself — that the current wrapper doesn't intercept.
Workaround I'm using
An
:aroundadvice onrime--committhat, in ghostel buffers, forwards the committed text to the PTY instead of inserting:This works, but it hardcodes a private rime function (
rime--commit), so it's clearly not a general fix. A robust solution probably belongs in ghostel — e.g. intercepting inserts into the protected region at the read-only-barrier level so that any IME committing through its own command path is forwarded to the PTY, not just those going throughinput-method-function. I'll defer to your judgment on the right layer.Happy to test patches. Thanks for ghostel!