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

@ -95,6 +95,26 @@ You should use `set-eshell-alias!' to change this.")
(setq buffer-undo-list old-undo-list)
(clrhash undo-equiv-table)))
;; UX: Prior output in eshell buffers 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
;; must flush out with a couple RETs, which may execute the broken text in
;; the buffer),
(add-hook! 'eshell-pre-command-hook
(defun +eshell-protect-input-in-visual-modes-h ()
(when (and eshell-last-input-start
eshell-last-input-end)
(add-text-properties eshell-last-input-start
(1- eshell-last-input-end)
'(read-only t)))))
(add-hook! 'eshell-post-command-hook
(defun +eshell-protect-output-in-visual-modes-h ()
(when (and eshell-last-input-end
eshell-last-output-start)
(add-text-properties eshell-last-input-end
eshell-last-output-start
'(read-only t)))))
;; Enable autopairing in eshell
(add-hook 'eshell-mode-hook #'smartparens-mode)

View File

@ -13,3 +13,26 @@
;; Remove hscroll-margin in shells, otherwise you get jumpiness when the cursor
;; comes close to the left/right edges of the window.
(setq-hook! 'term-mode-hook hscroll-margin 0)
;; HACK: Prior output in (ansi-)term shells 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 must
;; flush out with a couple RETs, which may execute the broken text in the
;; buffer), Note that this does not protect the prompt in (ansi-)term buffers
;; unless you set `term-prompt-regexp' buffer-locally! (e.g. with
;; `setq-hook!').
(defadvice! +term--protect-process-output-in-visual-modes-a (&rest _)
:before #'term-line-mode
(when (term-in-char-mode)
(let* ((prompt?)
(prompt-end
(save-excursion
(goto-char (process-mark (get-buffer-process (current-buffer))))
(or (and (not (equal term-prompt-regexp "^"))
(setq prompt? (re-search-backward term-prompt-regexp (line-beginning-position) t))
(match-end 0))
(line-beginning-position)))))
(with-silent-modifications
(when prompt?
(put-text-property (1- prompt-end) prompt-end 'read-only 'fence))
(add-text-properties (point-min) prompt-end '(read-only t))))))