aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorJuri Linkov2018-01-21 23:45:43 +0200
committerJuri Linkov2018-01-21 23:45:43 +0200
commitafba4ccb8b8c6347a44efd0b9f4d6fb85756f85b (patch)
tree42b97dca576f2020dc18a33ccabcc7ff1ddf2c93 /lisp
parent9ae0e4aa1aee3d7ff2546e34aa83536f72f8c06a (diff)
downloademacs-afba4ccb8b8c6347a44efd0b9f4d6fb85756f85b.tar.gz
emacs-afba4ccb8b8c6347a44efd0b9f4d6fb85756f85b.zip
New function read-answer (bug#30073)
* lisp/emacs-lisp/map-ynp.el (read-answer): New function. (read-answer-short): New defcustom. * lisp/dired.el (dired-delete-file): Use read-answer. (dired--yes-no-all-quit-help): Remove function. (dired-delete-help): Remove defconst. * lisp/subr.el (assoc-delete-all): New function.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/dired.el41
-rw-r--r--lisp/emacs-lisp/map-ynp.el122
-rw-r--r--lisp/subr.el15
3 files changed, 145 insertions, 33 deletions
diff --git a/lisp/dired.el b/lisp/dired.el
index b853d64c563..eebf8362cfc 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -2997,37 +2997,6 @@ Any other value means to ask for each directory."
2997;; Match anything but `.' and `..'. 2997;; Match anything but `.' and `..'.
2998(defvar dired-re-no-dot "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*") 2998(defvar dired-re-no-dot "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")
2999 2999
3000(defconst dired-delete-help
3001 "Type:
3002`yes' to delete recursively the current directory,
3003`no' to skip to next,
3004`all' to delete all remaining directories with no more questions,
3005`quit' to exit,
3006`help' to show this help message.")
3007
3008(defun dired--yes-no-all-quit-help (prompt &optional help-msg)
3009 "Ask a question with valid answers: yes, no, all, quit, help.
3010PROMPT must end with '? ', for instance, 'Delete it? '.
3011If optional arg HELP-MSG is non-nil, then is a message to show when
3012the user answers 'help'. Otherwise, default to `dired-delete-help'."
3013 (let ((valid-answers (list "yes" "no" "all" "quit"))
3014 (answer "")
3015 (input-fn (lambda ()
3016 (read-string
3017 (format "%s [yes, no, all, quit, help] " prompt)))))
3018 (setq answer (funcall input-fn))
3019 (when (string= answer "help")
3020 (with-help-window "*Help*"
3021 (with-current-buffer "*Help*"
3022 (insert (or help-msg dired-delete-help)))))
3023 (while (not (member answer valid-answers))
3024 (unless (string= answer "help")
3025 (beep)
3026 (message "Please answer `yes' or `no' or `all' or `quit'")
3027 (sleep-for 2))
3028 (setq answer (funcall input-fn)))
3029 answer))
3030
3031;; Delete file, possibly delete a directory and all its files. 3000;; Delete file, possibly delete a directory and all its files.
3032;; This function is useful outside of dired. One could change its name 3001;; This function is useful outside of dired. One could change its name
3033;; to e.g. recursive-delete-file and put it somewhere else. 3002;; to e.g. recursive-delete-file and put it somewhere else.
@@ -3057,11 +3026,17 @@ TRASH non-nil means to trash the file instead of deleting, provided
3057 "trash" 3026 "trash"
3058 "delete") 3027 "delete")
3059 (dired-make-relative file)))) 3028 (dired-make-relative file))))
3060 (pcase (dired--yes-no-all-quit-help prompt) ; Prompt user. 3029 (pcase (read-answer
3030 prompt
3031 '(("yes" ?y "delete recursively the current directory")
3032 ("no" ?n "skip to next")
3033 ("all" ?! "delete all remaining directories with no more questions")
3034 ("quit" ?q "exit")))
3061 ('"all" (setq recursive 'always dired-recursive-deletes recursive)) 3035 ('"all" (setq recursive 'always dired-recursive-deletes recursive))
3062 ('"yes" (if (eq recursive 'top) (setq recursive 'always))) 3036 ('"yes" (if (eq recursive 'top) (setq recursive 'always)))
3063 ('"no" (setq recursive nil)) 3037 ('"no" (setq recursive nil))
3064 ('"quit" (keyboard-quit))))) 3038 ('"quit" (keyboard-quit))
3039 (_ (keyboard-quit))))) ; catch all unknown answers
3065 (setq recursive nil)) ; Empty dir or recursive is nil. 3040 (setq recursive nil)) ; Empty dir or recursive is nil.
3066 (delete-directory file recursive trash)))) 3041 (delete-directory file recursive trash))))
3067 3042
diff --git a/lisp/emacs-lisp/map-ynp.el b/lisp/emacs-lisp/map-ynp.el
index dd80524a152..61c04ff7b3e 100644
--- a/lisp/emacs-lisp/map-ynp.el
+++ b/lisp/emacs-lisp/map-ynp.el
@@ -252,4 +252,126 @@ C-g to quit (cancel the whole command);
252 ;; Return the number of actions that were taken. 252 ;; Return the number of actions that were taken.
253 actions)) 253 actions))
254 254
255
256;; read-answer is a general-purpose question-asker that supports
257;; either long or short answers.
258
259;; For backward compatibility check if short y/n answers are preferred.
260(defcustom read-answer-short (eq (symbol-function 'yes-or-no-p) 'y-or-n-p)
261 "If non-nil, accept short answers to the question."
262 :type 'boolean
263 :version "27.1"
264 :group 'minibuffer)
265
266(defconst read-answer-map--memoize (make-hash-table :weakness 'key :test 'equal))
267
268(defun read-answer (question answers)
269 "Read an answer either as a complete word or its character abbreviation.
270Ask user a question and accept an answer from the list of possible answers.
271
272QUESTION should end in a space; this function adds a list of answers to it.
273
274ANSWERS is an alist with elements in the following format:
275 (LONG-ANSWER SHORT-ANSWER HELP-MESSAGE)
276where
277 LONG-ANSWER is a complete answer,
278 SHORT-ANSWER is an abbreviated one-character answer,
279 HELP-MESSAGE is a string describing the meaning of the answer.
280
281Example:
282 \\='((\"yes\" ?y \"perform the action\")
283 (\"no\" ?n \"skip to the next\")
284 (\"all\" ?! \"accept all remaining without more questions\")
285 (\"help\" ?h \"show help\")
286 (\"quit\" ?q \"exit\"))
287
288When `read-answer-short' is non-nil, accept short answers.
289
290Return a long answer even in case of accepting short ones.
291
292When `use-dialog-box' is t, pop up a dialog window to get user input."
293 (custom-reevaluate-setting 'read-answer-short)
294 (let* ((short read-answer-short)
295 (answers-with-help
296 (if (assoc "help" answers)
297 answers
298 (append answers '(("help" ?? "show this help message")))))
299 (answers-without-help
300 (assoc-delete-all "help" (copy-alist answers-with-help)))
301 (prompt
302 (format "%s(%s) " question
303 (mapconcat (lambda (a)
304 (if short
305 (format "%c" (nth 1 a))
306 (nth 0 a)))
307 answers-with-help ", ")))
308 (message
309 (format "Please answer %s."
310 (mapconcat (lambda (a)
311 (format "`%s'" (if short
312 (string (nth 1 a))
313 (nth 0 a))))
314 answers-with-help " or ")))
315 (short-answer-map
316 (when short
317 (or (gethash answers read-answer-map--memoize)
318 (puthash answers
319 (let ((map (make-sparse-keymap)))
320 (set-keymap-parent map minibuffer-local-map)
321 (dolist (a answers-with-help)
322 (define-key map (vector (nth 1 a))
323 (lambda ()
324 (interactive)
325 (delete-minibuffer-contents)
326 (insert (nth 0 a))
327 (exit-minibuffer))))
328 (define-key map [remap self-insert-command]
329 (lambda ()
330 (interactive)
331 (delete-minibuffer-contents)
332 (beep)
333 (message message)
334 (sleep-for 2)))
335 map)
336 read-answer-map--memoize))))
337 answer)
338 (while (not (assoc (setq answer (downcase
339 (cond
340 ((and (display-popup-menus-p)
341 last-input-event ; not during startup
342 (listp last-nonmenu-event)
343 use-dialog-box)
344 (x-popup-dialog
345 t
346 (cons question
347 (mapcar (lambda (a)
348 (cons (capitalize (nth 0 a))
349 (nth 0 a)))
350 answers-with-help))))
351 (short
352 (read-from-minibuffer
353 prompt nil short-answer-map nil
354 'yes-or-no-p-history))
355 (t
356 (read-from-minibuffer
357 prompt nil nil nil
358 'yes-or-no-p-history)))))
359 answers-without-help))
360 (if (string= answer "help")
361 (with-help-window "*Help*"
362 (with-current-buffer "*Help*"
363 (insert "Type:\n"
364 (mapconcat
365 (lambda (a)
366 (format "`%s'%s to %s"
367 (if short (string (nth 1 a)) (nth 0 a))
368 (if short (format " (%s)" (nth 0 a)) "")
369 (nth 2 a)))
370 answers-with-help ",\n")
371 ".\n")))
372 (beep)
373 (message message)
374 (sleep-for 2)))
375 answer))
376
255;;; map-ynp.el ends here 377;;; map-ynp.el ends here
diff --git a/lisp/subr.el b/lisp/subr.el
index 46cf5a34ccc..092850a44d9 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -705,6 +705,21 @@ Non-strings in LIST are ignored."
705 (setq list (cdr list))) 705 (setq list (cdr list)))
706 list) 706 list)
707 707
708(defun assoc-delete-all (key alist)
709 "Delete from ALIST all elements whose car is `equal' to KEY.
710Return the modified alist.
711Elements of ALIST that are not conses are ignored."
712 (while (and (consp (car alist))
713 (equal (car (car alist)) key))
714 (setq alist (cdr alist)))
715 (let ((tail alist) tail-cdr)
716 (while (setq tail-cdr (cdr tail))
717 (if (and (consp (car tail-cdr))
718 (equal (car (car tail-cdr)) key))
719 (setcdr tail (cdr tail-cdr))
720 (setq tail tail-cdr))))
721 alist)
722
708(defun assq-delete-all (key alist) 723(defun assq-delete-all (key alist)
709 "Delete from ALIST all elements whose car is `eq' to KEY. 724 "Delete from ALIST all elements whose car is `eq' to KEY.
710Return the modified alist. 725Return the modified alist.