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 /modules/system-commands.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 'modules/system-commands.el')
| -rw-r--r-- | modules/system-commands.el | 17 |
1 files changed, 10 insertions, 7 deletions
diff --git a/modules/system-commands.el b/modules/system-commands.el index edc6339d..08a1be5d 100644 --- a/modules/system-commands.el +++ b/modules/system-commands.el @@ -39,7 +39,7 @@ ;; require keeps the module loadable on its own (tests, byte-compile) rather ;; than relying on init.el's load order. (require 'host-environment) -;; `system-lib' provides `cj/confirm-strong', used at runtime by the `strong' +;; `system-lib' provides `cj/confirm-destructive', used at runtime by the `strong' ;; confirm branch of `cj/system-cmd' for irreversible actions (shutdown/reboot). (require 'system-lib) (eval-when-compile (require 'subr-x)) @@ -76,10 +76,11 @@ If CMD is deemed dangerous, ask for confirmation." (label (nth 2 resolved))) (let ((confirm (and sym (get sym 'cj/system-confirm)))) (cond - ;; Strong confirm for irreversible actions (shutdown, reboot): - ;; require an explicit "yes", so a stray RET/space can't trigger them. + ;; Strong confirm for irreversible actions (shutdown, reboot): one + ;; keystroke, but with no default, so a stray RET/space can't trigger + ;; them and type-ahead is discarded before the read. ((eq confirm 'strong) - (unless (cj/confirm-strong (format "Really run %s (%s)? " label cmdstr)) + (unless (cj/confirm-destructive (format "Really run %s (%s)? " label cmdstr)) (user-error "Aborted"))) ;; Quick (Y/n) confirm for recoverable actions (logout, suspend). (confirm @@ -96,9 +97,11 @@ If CMD is deemed dangerous, ask for confirmation." (defmacro cj/defsystem-command (name var cmdstr &optional confirm) "Define VAR with CMDSTR and interactive command NAME to run it. -CONFIRM controls the confirmation prompt: t for a quick (Y/n) prompt, -the symbol `strong' for an explicit yes-or-no-p (used for irreversible -actions like shutdown and reboot), nil for no confirmation." +CONFIRM controls the confirmation prompt: t for a quick (Y/n) prompt where +RET and space mean yes, the symbol `strong' for `cj/confirm-destructive' +\(used for irreversible actions like shutdown and reboot), nil for no +confirmation. Both are one keystroke; the difference is that `strong' has +no default, so RET and space re-prompt rather than confirming." (declare (indent defun)) `(progn (defvar ,var ,cmdstr) |
