From 0b93863689038a385fd320440ac0a413ce81c4e1 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Fri, 31 Jul 2026 12:39:27 -0500 Subject: fix(prompts): make destructive confirms one keystroke 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. --- tests/test-system-lib-confirm-strong.el | 37 --------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 tests/test-system-lib-confirm-strong.el (limited to 'tests/test-system-lib-confirm-strong.el') diff --git a/tests/test-system-lib-confirm-strong.el b/tests/test-system-lib-confirm-strong.el deleted file mode 100644 index 26c00822..00000000 --- a/tests/test-system-lib-confirm-strong.el +++ /dev/null @@ -1,37 +0,0 @@ -;;; test-system-lib-confirm-strong.el --- Tests for cj/confirm-strong -*- lexical-binding: t; -*- - -;;; Commentary: -;; ERT tests for `cj/confirm-strong', the typed-"yes" confirmation used for -;; irreversible actions. The behavior under test is the long-form guarantee: -;; the prompt demands a typed yes/no even when the global single-key default -;; (`use-short-answers') is in effect. - -;;; Code: - -(require 'ert) -(require 'cl-lib) -(require 'system-lib) - -(ert-deftest test-system-lib-confirm-strong-returns-t-on-yes () - "Normal: passes a t answer through from `yes-or-no-p'." - (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest _) t))) - (should (eq (cj/confirm-strong "Really? ") t)))) - -(ert-deftest test-system-lib-confirm-strong-returns-nil-on-no () - "Normal: passes a nil answer through from `yes-or-no-p'." - (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest _) nil))) - (should (eq (cj/confirm-strong "Really? ") nil)))) - -(ert-deftest test-system-lib-confirm-strong-forces-long-form () - "Boundary: binds `use-short-answers' to nil for the call even when it is -globally t, so the irreversible prompt requires a typed yes/no regardless of -the single-key default." - (let ((use-short-answers t) - (seen 'unset)) - (cl-letf (((symbol-function 'yes-or-no-p) - (lambda (&rest _) (setq seen use-short-answers) t))) - (cj/confirm-strong "Really? ") - (should (eq seen nil))))) - -(provide 'test-system-lib-confirm-strong) -;;; test-system-lib-confirm-strong.el ends here -- cgit v1.2.3