diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-31 12:39:27 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-31 12:39:27 -0500 |
| commit | 0b93863689038a385fd320440ac0a413ce81c4e1 (patch) | |
| tree | 30dbabae88dfe704236544e5ebb6f6ebd18f4814 /tests/test-custom-buffer-file-move-buffer-and-file.el | |
| parent | 445a0be315f3d6fff585f1558262fb5dacd10d12 (diff) | |
| download | dotemacs-0b93863689038a385fd320440ac0a413ce81c4e1.tar.gz dotemacs-0b93863689038a385fd320440ac0a413ce81c4e1.zip | |
I had to kill an Emacs session today over a prompt I could not answer. A
second agent session held the selected window while "Overwrite hiroshi.m3u?
(yes or no)" waited in another frame, so my keystrokes went to a terminal
and the minibuffer stayed empty. Losing unsaved buffers is a far worse
outcome than a mis-keyed confirm.
So cj/confirm-strong becomes cj/confirm-destructive, and instead of binding
use-short-answers to nil for a typed "yes" it reads a single y or n.
Two things I kept:
- No default. Only y and n answer, so a stray RET or space re-prompts
rather than confirming a shutdown.
- Pending input is discarded before the read. Without that the change would
have been a bad trade: read-char-choice reads the input queue, so a key
typed before the prompt painted would confirm instantly, where the old
typed-"yes" absorbed it harmlessly.
Worth being honest about what this does not fix. One keystroke does not
make a prompt reachable when focus is elsewhere, and C-g is still the
escape. It lowers the cost of the situation rather than preventing it.
Six call sites: shutdown and reboot, shred, playlist overwrite and delete,
and the two file overwrites.
Diffstat (limited to 'tests/test-custom-buffer-file-move-buffer-and-file.el')
| -rw-r--r-- | tests/test-custom-buffer-file-move-buffer-and-file.el | 27 |
1 files changed, 18 insertions, 9 deletions
diff --git a/tests/test-custom-buffer-file-move-buffer-and-file.el b/tests/test-custom-buffer-file-move-buffer-and-file.el index 8331db5c..b3d78ccf 100644 --- a/tests/test-custom-buffer-file-move-buffer-and-file.el +++ b/tests/test-custom-buffer-file-move-buffer-and-file.el @@ -884,11 +884,17 @@ (with-temp-file source-file (insert "new")) (find-file source-file) - ;; Mock yes-or-no-p to capture that it was called - (cl-letf (((symbol-function 'yes-or-no-p) - (lambda (prompt) + ;; The overwrite confirm goes through `cj/confirm-destructive', which + ;; reads a single key rather than a typed yes. Mock the key read, not + ;; `yes-or-no-p' -- mocking the latter would pass whether or not the + ;; prompt happened at all. + (cl-letf (((symbol-function 'read-char-choice) + (lambda (&rest _) (setq prompted t) - t)) + ?y)) + ((symbol-function 'yes-or-no-p) + (lambda (&rest _) + (error "overwrite confirm must not demand a typed yes"))) ((symbol-function 'read-directory-name) (lambda (&rest _) target-dir))) (call-interactively #'cj/move-buffer-and-file) @@ -907,11 +913,14 @@ (with-temp-file source-file (insert "new")) (find-file source-file) - ;; Mock yes-or-no-p to capture if it was called - (cl-letf (((symbol-function 'yes-or-no-p) - (lambda (prompt) - (setq prompted t) - t)) + ;; Both prompt paths are mocked, not just the old one. Watching + ;; `yes-or-no-p' alone would make this assertion unfalsifiable now + ;; that the confirm reads a key instead: it would pass whether the + ;; prompt was correctly skipped or merely moved. + (cl-letf (((symbol-function 'read-char-choice) + (lambda (&rest _) (setq prompted t) ?y)) + ((symbol-function 'yes-or-no-p) + (lambda (&rest _) (setq prompted t) t)) ((symbol-function 'read-directory-name) (lambda (&rest _) target-dir))) (call-interactively #'cj/move-buffer-and-file) |
