feat(:term): make previous command output read-only

This makes the output of previous commands and prompts immutable (by
default), so users can't accidentally alter them, which can leave the
buffer in a half-broken state (requiring the user flush out the garbled
input with a couple RETs).

This targets comint shells (shell, ielm, etc), eshell, (ansi-)term, and
any derivatives thereof.

Fix: #8411
This commit is contained in:
Henrik Lissner
2025-06-30 20:51:30 +02:00
parent 5b5b170f79
commit 1b125ddf7b
3 changed files with 72 additions and 2 deletions

View File

@ -348,8 +348,35 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original
(after! comint
(setq comint-prompt-read-only t
comint-buffer-maximum-size 2048)) ; double the default
(setq-default comint-buffer-maximum-size 2048) ; double the default
;; Protect prompts from accidental modifications.
(setq-default comint-prompt-read-only t)
;; UX: Prior output in shell and comint shells (like ielm) should be
;; read-only. Otherwise, it's trivial to make edits in visual modes (like
;; evil's or term's term-line-mode) and leave the buffer in a half-broken
;; state (which you have to flush out with a couple RETs, which may execute
;; the broken text in the buffer),
(defadvice! doom--comint-protect-output-in-visual-modes-a (process _string)
:after #'comint-output-filter
;; Adapted from https://github.com/michalrus/dotfiles/blob/c4421e361400c4184ea90a021254766372a1f301/.emacs.d/init.d/040-terminal.el.symlink#L33-L49
(let* ((start-marker comint-last-output-start)
(end-marker (or (if process (process-mark process))
(point-max-marker))))
(when (< start-marker end-marker) ;; Account for some of the IELMs wilderness.
(let ((inhibit-read-only t))
;; Make all past output read-only (disallow buffer modifications)
(add-text-properties comint-last-input-start (1- end-marker) '(read-only t))
;; Disallow interleaving.
(remove-text-properties start-marker (1- end-marker) '(rear-nonsticky))
;; Make sure that at `max-point' you can always append. Important for
;; bad REPLs that keep writing after giving us prompt (e.g. sbt).
(add-text-properties (1- end-marker) end-marker '(rear-nonsticky t))
;; Protect fence (newline of input, just before output).
(when (eq (char-before start-marker) ?\n)
(remove-text-properties (1- start-marker) start-marker '(rear-nonsticky))
(add-text-properties (1- start-marker) start-marker '(read-only t))))))))
(after! compile