unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Tino Calancha <tino.calancha@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 28525@debbugs.gnu.org, npostavs@users.sourceforge.net
Subject: bug#28525: 26.0.60; dired-delete-file: Accept y/n if yes-or-no-p is aliased to y-or-n-p
Date: Sun, 01 Oct 2017 13:06:32 +0900	[thread overview]
Message-ID: <87poa7tstz.fsf@gmail.com> (raw)
In-Reply-To: <87tvzktk75.fsf@gmail.com> (Tino Calancha's message of "Sat, 30 Sep 2017 22:00:46 +0900")

Tino Calancha <tino.calancha@gmail.com> writes:

> In my previous patch i checked if `yes-or-no-p' is aliased to
> `y-or-n-p'.
> A more general way is to add new functions `yes-or-no-or-else-p',
> `y-or-n-or-else-p': they ask the question and
> accept additional answers according with an optional argument.
Indeed, we don't need new functions just add an optional argument makes
the thing (see below patch).
In some cases it might be useful to allow more answers than just 'yes'
or 'no'.

(yes-or-no-p "Do it? ") ; Accepts 'yes' or 'no'

(yes-or-no-p "Do it? " '((l . later) (m . maybe)))
;; Also accepts 'later' and 'maybe'.

(y-or-n-p "Do it? " '((l . later) (m . maybe)))
;; Accepts: 'y', 'n', 'l' and 'm'.

This has the advantage that is backward compatible for users doing:
(fset 'yes-or-no-p 'y-or-n-p)

Then, these users can use the new feature answering '!', if
we write something like:
(yes-or-no-p "Recursively delete dir? " '((! . all) (q . quit) (h . help)))

--8<-----------------------------cut here---------------start------------->8---
commit 17b1d3715b3d07c487b5b9cc757fc2ce5e0a89a9
Author: Tino Calancha <tino.calancha@gmail.com>
Date:   Sun Oct 1 12:44:41 2017 +0900

    Accept more answers in yes-or-no-or-else-p and y-or-n-or-else-p
    
    * src/fns.c (yes-or-no-p)
    * lisp/subr.el (y-or-n-or-p):
    Add optional argument OTHERS.

diff --git a/lisp/subr.el b/lisp/subr.el
index cf15ec287f..1b11e84dd0 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2484,8 +2484,13 @@ sit-for
 ;; Behind display-popup-menus-p test.
 (declare-function x-popup-dialog "menu.c" (position contents &optional header))
 
-(defun y-or-n-p (prompt)
-  "Ask user a \"y or n\" question.
+(defun y-or-n-p (prompt &optional others)
+  "Ask user a \"y or n r ...\" question.
+
+OTHERS is a list (INPUT . ACTION), with INPUT the user
+input, and ACTION determines how to proceed; both are symbols.
+For instance, SYMBOL might be '!' and ACTION 'automatic'.
+
 Return t if answer is \"y\" and nil if it is \"n\".
 PROMPT is the string to display to ask the question.  It should
 end in a space; `y-or-n-p' adds \"(y or n) \" to it.
@@ -2509,48 +2514,58 @@ y-or-n-p
   ;; ¡Beware! when I tried to edebug this code, Emacs got into a weird state
   ;; where all the keys were unbound (i.e. it somehow got triggered
   ;; within read-key, apparently).  I had to kill it.
-  (let ((answer 'recenter)
-	(padded (lambda (prompt &optional dialog)
-		  (let ((l (length prompt)))
-		    (concat prompt
-			    (if (or (zerop l) (eq ?\s (aref prompt (1- l))))
-				"" " ")
-			    (if dialog "" "(y or n) "))))))
+  (let* ((answer 'recenter)
+         (options (mapcar #'car others))
+         (options-str (mapconcat #'identity
+                                 (append (list "y" "n")
+                                         (mapcar (lambda (x) (symbol-name (car x))) others)) " or "))
+         (actions (append '(skip act) (mapcar #'cdr others)))
+	 (padded (lambda (prompt &optional dialog)
+		   (let ((l (length prompt)))
+		     (concat prompt
+			     (if (or (zerop l) (eq ?\s (aref prompt (1- l))))
+				 "" " ")
+			     (if dialog "" (concat "(" options-str ")")))))))
     (cond
      (noninteractive
       (setq prompt (funcall padded prompt))
       (let ((temp-prompt prompt))
-	(while (not (memq answer '(act skip)))
+	(while (not (memq answer actions))
 	  (let ((str (read-string temp-prompt)))
 	    (cond ((member str '("y" "Y")) (setq answer 'act))
+                  ((assoc (intern str) others) (setq answer (cdr (assoc (intern str) others))))
 		  ((member str '("n" "N")) (setq answer 'skip))
-		  (t (setq temp-prompt (concat "Please answer y or n.  "
+		  (t (setq temp-prompt (concat "Please answer " options-str ". "
 					       prompt))))))))
      ((and (display-popup-menus-p)
            last-input-event             ; not during startup
 	   (listp last-nonmenu-event)
 	   use-dialog-box)
       (setq prompt (funcall padded prompt t)
-	    answer (x-popup-dialog t `(,prompt ("Yes" . act) ("No" . skip)))))
+	    answer
+            (x-popup-dialog
+             t
+             `(,prompt ("Yes" . act) ("No" . skip)
+                       (mapcar (lambda (x) (cons (symbol-name (car x)) (cdr x))) others)))))
      (t
       (setq prompt (funcall padded prompt))
       (while
           (let* ((scroll-actions '(recenter scroll-up scroll-down
-				   scroll-other-window scroll-other-window-down))
+				            scroll-other-window scroll-other-window-down))
 		 (key
                   (let ((cursor-in-echo-area t))
                     (when minibuffer-auto-raise
                       (raise-frame (window-frame (minibuffer-window))))
                     (read-key (propertize (if (memq answer scroll-actions)
                                               prompt
-                                            (concat "Please answer y or n.  "
-                                                    prompt))
+                                            (concat "Please answer " options-str ". " prompt))
                                           'face 'minibuffer-prompt)))))
             (setq answer (lookup-key query-replace-map (vector key) t))
             (cond
-	     ((memq answer '(skip act)) nil)
-	     ((eq answer 'recenter)
-	      (recenter) t)
+	     ((memq answer actions) nil)
+	     ((member (intern (char-to-string key)) options)
+              (setq answer (cdr (assoc (intern (char-to-string key)) others))) nil)
+	     ((eq answer 'recenter) (recenter) t)
 	     ((eq answer 'scroll-up)
 	      (ignore-errors (scroll-up-command)) t)
 	     ((eq answer 'scroll-down)
@@ -2564,9 +2579,14 @@ y-or-n-p
 	     (t t)))
         (ding)
         (discard-input))))
-    (let ((ret (eq answer 'act)))
+    (let ((ret (cond ((eq answer 'act))
+                     ((eq answer 'skip) nil)
+                     ((memq answer actions) answer))))
       (unless noninteractive
-        (message "%s%c" prompt (if ret ?y ?n)))
+        (message "%s%c" prompt (cond ((eq ret t) ?y)
+                                     ((null ret) ?n)
+                                     ((memq ret actions)
+                                      (string-to-char (symbol-name (car (rassoc ret others))))))))
       ret)))
 
 \f
diff --git a/src/fns.c b/src/fns.c
index 4524ff9b26..a9e1a864cd 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -2582,20 +2582,27 @@ do_yes_or_no_p (Lisp_Object prompt)
   return call1 (intern ("yes-or-no-p"), prompt);
 }
 
-DEFUN ("yes-or-no-p", Fyes_or_no_p, Syes_or_no_p, 1, 1, 0,
-       doc: /* Ask user a yes-or-no question.
-Return t if answer is yes, and nil if the answer is no.
+DEFUN ("yes-or-no-p", Fyes_or_no_p, Syes_or_no_p, 1, 2, 0,
+       doc: /* Ask user a yes or no or ... question.
+OTHERS is a list (INPUT . ACTION), with INPUT the user
+input, and ACTION determines how to proceed; both are symbols.
+For instance, SYMBOL might be '!' and ACTION 'automatic'.
+
+Return t if answer is yes, nil if the answer is no or ACTION if the answer
+is ACTION.
 PROMPT is the string to display to ask the question.  It should end in
-a space; `yes-or-no-p' adds \"(yes or no) \" to it.
+a space; `yes-or-no-or-else-p' adds \"(yes or no or ACTION1 or ACTION2 ...) \" to it.
 
 The user must confirm the answer with RET, and can edit it until it
 has been confirmed.
 
 If dialog boxes are supported, a dialog box will be used
-if `last-nonmenu-event' is nil, and `use-dialog-box' is non-nil.  */)
-  (Lisp_Object prompt)
+if `last-nonmenu-event' is nil, and `use-dialog-box' is non-nil.
+
+This function is like `yes-or-no-p' with the additional answers in OTHERS.  */)
+  (Lisp_Object prompt, Lisp_Object others)
 {
-  Lisp_Object ans;
+  Lisp_Object ans, yes_or_no, actions, str;
 
   CHECK_STRING (prompt);
 
@@ -2611,9 +2618,21 @@ if `last-nonmenu-event' is nil, and `use-dialog-box' is non-nil.  */)
       return obj;
     }
 
-  AUTO_STRING (yes_or_no, "(yes or no) ");
-  prompt = CALLN (Fconcat, prompt, yes_or_no);
-
+  if (!NILP (others)) {
+    actions = Fmapcar (intern ("symbol-name"), Fmapcar (Qcdr, others));
+    yes_or_no = Fmapconcat (Qidentity,
+                            CALLN (Fappend, list2 (build_string ("yes"),
+                                                   build_string ("no")),
+                                   actions),
+                            build_string (" or "));
+    yes_or_no = CALLN (Fconcat, build_string ("("), yes_or_no, build_string (")"));
+  }
+  else {
+        actions = Qnil;
+        yes_or_no = build_string ("(yes or no)");
+  }
+  prompt = CALLN (Fconcat, prompt, yes_or_no, build_string (" "));
+  str = CALLN (Fconcat, build_string ("Please answer "), yes_or_no, build_string ("."));
   while (1)
     {
       ans = Fdowncase (Fread_from_minibuffer (prompt, Qnil, Qnil, Qnil,
@@ -2623,13 +2642,16 @@ if `last-nonmenu-event' is nil, and `use-dialog-box' is non-nil.  */)
 	return Qt;
       if (SCHARS (ans) == 2 && !strcmp (SSDATA (ans), "no"))
 	return Qnil;
+      if (!NILP (Fmember (ans, actions)))
+        return Fcdr (Frassoc (intern (SSDATA (ans)), others));
 
       Fding (Qnil);
       Fdiscard_input ();
-      message1 ("Please answer yes or no.");
+      message1 (SSDATA (str));
       Fsleep_for (make_number (2), Qnil);
     }
 }
+
 \f
 DEFUN ("load-average", Fload_average, Sload_average, 0, 1, 0,
        doc: /* Return list of 1 minute, 5 minute and 15 minute load averages.

--8<-----------------------------cut here---------------end--------------->8---
 In GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.11)
 of 2017-09-30
 Repository revision: 20a09de953f437109a098fa8c4d380663d921481





  reply	other threads:[~2017-10-01  4:06 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-09-20  9:51 bug#28525: 26.0.60; dired-delete-file: Accept y/n if yes-or-no-p is aliased to y-or-n-p Tino Calancha
2017-09-21  8:15 ` Eli Zaretskii
2017-09-30 13:00   ` Tino Calancha
2017-10-01  4:06     ` Tino Calancha [this message]
2017-10-01 23:15       ` Drew Adams
2017-10-02  5:40         ` Tino Calancha
2017-10-02 13:33           ` Drew Adams
2017-10-03  8:02             ` Tino Calancha
2017-10-02 17:41       ` Eli Zaretskii
2017-10-03  8:00         ` Tino Calancha
2017-10-03 11:10           ` Michael Heerdegen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87poa7tstz.fsf@gmail.com \
    --to=tino.calancha@gmail.com \
    --cc=28525@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    --cc=npostavs@users.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).