GNU bug report logs - #38076
Using minibuffer for y-or-n-p

Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.

Package: emacs; Reported by: Juri Linkov <juri@HIDDEN>; Keywords: fixed patch; Done: Juri Linkov <juri@HIDDEN>; Maintainer for emacs is bug-gnu-emacs@HIDDEN.
bug marked as fixed in version 27.0.50, send any further explanations to 38076 <at> debbugs.gnu.org and Juri Linkov <juri@HIDDEN> Request was from Juri Linkov <juri@HIDDEN> to control <at> debbugs.gnu.org. Full text available.
Added tag(s) fixed. Request was from Juri Linkov <juri@HIDDEN> to control <at> debbugs.gnu.org. Full text available.

Message received at 38076 <at> debbugs.gnu.org:


Received: (at 38076) by debbugs.gnu.org; 6 Nov 2019 22:31:39 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Wed Nov 06 17:31:39 2019
Received: from localhost ([127.0.0.1]:41367 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1iSTpy-0000Mr-Tc
	for submit <at> debbugs.gnu.org; Wed, 06 Nov 2019 17:31:39 -0500
Received: from aye.elm.relay.mailchannels.net ([23.83.212.6]:37945)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <juri@HIDDEN>) id 1iSTpv-0000Mh-S4
 for 38076 <at> debbugs.gnu.org; Wed, 06 Nov 2019 17:31:36 -0500
X-Sender-Id: dreamhost|x-authsender|jurta@HIDDEN
Received: from relay.mailchannels.net (localhost [127.0.0.1])
 by relay.mailchannels.net (Postfix) with ESMTP id BB1698C3602;
 Wed,  6 Nov 2019 22:31:34 +0000 (UTC)
Received: from pdx1-sub0-mail-a79.g.dreamhost.com
 (100-96-92-150.trex.outbound.svc.cluster.local [100.96.92.150])
 (Authenticated sender: dreamhost)
 by relay.mailchannels.net (Postfix) with ESMTPA id 0BA238C35F8;
 Wed,  6 Nov 2019 22:31:34 +0000 (UTC)
X-Sender-Id: dreamhost|x-authsender|jurta@HIDDEN
Received: from pdx1-sub0-mail-a79.g.dreamhost.com ([TEMPUNAVAIL].
 [64.90.62.162]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384)
 by 0.0.0.0:2500 (trex/5.18.5); Wed, 06 Nov 2019 22:31:34 +0000
X-MC-Relay: Neutral
X-MailChannels-SenderId: dreamhost|x-authsender|jurta@HIDDEN
X-MailChannels-Auth-Id: dreamhost
X-Abortive-Troubled: 207427bf377574d3_1573079494464_20289825
X-MC-Loop-Signature: 1573079494464:2453096950
X-MC-Ingress-Time: 1573079494463
Received: from pdx1-sub0-mail-a79.g.dreamhost.com (localhost [127.0.0.1])
 by pdx1-sub0-mail-a79.g.dreamhost.com (Postfix) with ESMTP id A4C89A33B4;
 Wed,  6 Nov 2019 14:31:28 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=linkov.net; h=from:to:cc
 :subject:references:date:in-reply-to:message-id:mime-version
 :content-type; s=linkov.net; bh=DuZpKIkWtTcCiUXKSJOnEWnaNiE=; b=
 wau45JDJvg2Xwr2omX4EhhZaQWYCfF9axgFw4gi48y0E174GdublulUHmlzM8fdF
 GzwJkTaTGx+Jhv2s3SfXE+wA62Hqz7ETmmlBkdvEDvlxOp8hFTagDJaIHSjVQXo2
 t7eSmavF+tPTfKtB4NG5UmdxZMXIZh/r2//SsVFlBfs=
Received: from mail.jurta.org (m91-129-102-1.cust.tele2.ee [91.129.102.1])
 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
 (No client certificate requested)
 (Authenticated sender: jurta@HIDDEN)
 by pdx1-sub0-mail-a79.g.dreamhost.com (Postfix) with ESMTPSA id 840D1A3358;
 Wed,  6 Nov 2019 14:31:25 -0800 (PST)
X-DH-BACKEND: pdx1-sub0-mail-a79
From: Juri Linkov <juri@HIDDEN>
To: 38076 <at> debbugs.gnu.org
Subject: Re: bug#38076: Using minibuffer for y-or-n-p
Organization: LINKOV.NET
References: <20191026101407.GA17424@ACM>
 <jwvmudnd6rk.fsf-monnier+emacs@HIDDEN> <87o8y3d4ur.fsf@HIDDEN>
 <87mudl3l83.fsf@HIDDEN> <87v9s9b12x.fsf@HIDDEN>
 <87ftjctsx5.fsf@HIDDEN> <87k18n233r.fsf@HIDDEN>
 <87pnifcdzd.fsf@HIDDEN> <87d0efxfxg.fsf@HIDDEN>
 <875zk57sqk.fsf@HIDDEN>
 <jwvd0edwuaw.fsf-monnier+emacs@HIDDEN>
 <87r22m53yq.fsf_-_@HIDDEN>
Date: Thu, 07 Nov 2019 00:25:41 +0200
In-Reply-To: <87r22m53yq.fsf_-_@HIDDEN> (Juri Linkov's message of
 "Wed, 06 Nov 2019 00:54:37 +0200")
Message-ID: <87h83g7ica.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (x86_64-pc-linux-gnu)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 38076
Cc: Stefan Monnier <monnier@HIDDEN>
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

--=-=-=
Content-Type: text/plain

The previous patch fixes the problem with y-or-n-p reported in bug#17272.

But to fix the problem reported by Alan in
https://lists.gnu.org/archive/html/emacs-devel/2019-10/msg01020.html
for the prompt asking about local variables:

  Please type y, n, or !, or C-v to scroll:

requires using read-char-from-minibuffer instead of read-char-choice.
A new arg CHARS was added to read-char-from-minibuffer to make it
a drop-in replacement for read-char-choice.

Here is a complete tested patch that solves all reported problems
in bug#17272 and bug#19064.  More specific patches will be posted
now to these bug reports as well.


--=-=-=
Content-Type: text/x-diff; charset=iso-8859-1
Content-Disposition: inline;
 filename=y-or-n-p-minibuffer-read-char-from-minibuffer.patch
Content-Transfer-Encoding: quoted-printable

diff --git a/lisp/files.el b/lisp/files.el
index 7690357058..8f7c7c2d04 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2120,7 +2120,7 @@ files--ask-user-about-large-file
                                       ("Yes" . ?y)
                                       ("No" . ?n)
                                       ("Open literally" . ?l)))
-                (read-char-choice
+                (read-char-from-minibuffer
                  (concat prompt " (y)es or (n)o or (l)iterally ")
                  '(?y ?Y ?n ?N ?l ?L)))))
         (cond ((memq choice '(?y ?Y)) nil)
@@ -3503,24 +3503,17 @@ hack-local-variables-confirm
       ;; Display the buffer and read a choice.
       (save-window-excursion
 	(pop-to-buffer buf '(display-buffer--maybe-at-bottom))
-	(let* ((exit-chars '(?y ?n ?\s ?\C-g ?\C-v))
+	(let* ((exit-chars '(?y ?n ?\s))
 	       (prompt (format "Please type %s%s: "
 			       (if offer-save "y, n, or !" "y or n")
 			       (if (< (line-number-at-pos (point-max))
 				      (window-body-height))
 				   ""
-				 (push ?\C-v exit-chars)
-				 ", or C-v to scroll")))
+				 ", or C-v/M-v to scroll")))
 	       char)
 	  (if offer-save (push ?! exit-chars))
 	  (while (null char)
-	    (setq char (read-char-choice prompt exit-chars t))
-	    (when (eq char ?\C-v)
-	      (condition-case nil
-		  (scroll-up)
-		(error (goto-char (point-min))
-		       (recenter 1)))
-	      (setq char nil)))
+	    (setq char (read-char-from-minibuffer prompt exit-chars)))
 	  (when (and offer-save (=3D char ?!) unsafe-vars)
 	    (customize-push-and-save 'safe-local-variable-values unsafe-vars))
 	  (prog1 (memq char '(?! ?\s ?y))
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 43dd277a2e..741fab4f89 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -715,6 +715,11 @@ minibuffer-message
           (message "%s" message))
         (prog1 (sit-for (or minibuffer-message-timeout 1000000))
           (message nil)))
+    ;; Record message in the *Messages* buffer
+    (let ((inhibit-message t))
+      (if args
+          (apply #'message message args)
+        (message "%s" message)))
     ;; Clear out any old echo-area message to make way for our new thing=
.
     (message nil)
     (setq message (if (and (null args)
@@ -2236,6 +2241,13 @@ completion-help-at-point
 (let ((map minibuffer-local-map))
   (define-key map "\C-g" 'abort-recursive-edit)
   (define-key map "\M-<" 'minibuffer-beginning-of-buffer)
+
+  (define-key map [remap recenter-top-bottom] 'minibuffer-recenter-top-b=
ottom)
+  (define-key map [remap scroll-up-command] 'minibuffer-scroll-up-comman=
d)
+  (define-key map [remap scroll-down-command] 'minibuffer-scroll-down-co=
mmand)
+  (define-key map [remap scroll-other-window] 'minibuffer-scroll-other-w=
indow)
+  (define-key map [remap scroll-other-window-down] 'minibuffer-scroll-ot=
her-window-down)
+
   (define-key map "\r" 'exit-minibuffer)
   (define-key map "\n" 'exit-minibuffer))
=20
@@ -3670,6 +3682,46 @@ minibuffer-beginning-of-buffer
   (when (and arg (not (consp arg)))
     (forward-line 1)))
=20
+(defmacro with-minibuffer-selected-window (&rest body)
+  "Execute the forms in BODY from the minibuffer in its original window.
+When used in a minibuffer window, select the window selected just before
+minibuffer window was selected, and execute the forms."
+  (declare (indent 0) (debug t))
+  `(let ((window (minibuffer-selected-window)))
+     (when window
+       (with-selected-window window
+         ,@body))))
+
+(defun minibuffer-recenter-top-bottom (&optional arg)
+  "Run `recenter-top-bottom' from minibuffer in original window."
+  (interactive "P")
+  (with-minibuffer-selected-window
+    (recenter-top-bottom arg)))
+
+(defun minibuffer-scroll-up-command (&optional arg)
+  "Run `scroll-up-command' from minibuffer in original window."
+  (interactive "^P")
+  (with-minibuffer-selected-window
+    (scroll-up-command arg)))
+
+(defun minibuffer-scroll-down-command (&optional arg)
+  "Run `scroll-down-command' from minibuffer in original window."
+  (interactive "^P")
+  (with-minibuffer-selected-window
+    (scroll-down-command arg)))
+
+(defun minibuffer-scroll-other-window (&optional arg)
+  "Run `scroll-other-window' from minibuffer in original window."
+  (interactive "P")
+  (with-minibuffer-selected-window
+    (scroll-other-window arg)))
+
+(defun minibuffer-scroll-other-window-down (&optional arg)
+  "Run `scroll-other-window-down' from minibuffer in original window."
+  (interactive "^P")
+  (with-minibuffer-selected-window
+    (scroll-other-window-down arg)))
+
 (provide 'minibuffer)
=20
 ;;; minibuffer.el ends here
diff --git a/lisp/simple.el b/lisp/simple.el
index 10aecd651f..7ea6c73d3e 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -5171,46 +5171,84 @@ backward-delete-char-untabify
     ;; Avoid warning about delete-backward-char
     (with-no-warnings (delete-backward-char n killp))))
=20
-(defvar read-char-from-minibuffer-history nil
+(defvar read-char-history nil
   "The default history for the `read-char-from-minibuffer' function.")
=20
 (defvar read-char-from-minibuffer-map
   (let ((map (make-sparse-keymap)))
     (set-keymap-parent map minibuffer-local-map)
     (define-key map [remap self-insert-command]
-      'read-char-from-minibuffer-self-insert)
+      'read-char-from-minibuffer-insert-char)
     map)
   "Keymap for the `read-char-from-minibuffer' function.")
=20
-(defun read-char-from-minibuffer-self-insert ()
-  "Insert the character you type in the minibuffer."
+(defconst read-char-from-minibuffer-map-hash
+  (make-hash-table :weakness 'key :test 'equal))
+
+(defun read-char-from-minibuffer-insert-char ()
+  "Insert the character you type in the minibuffer and exit.
+Discard all input in a minibuffer before inserting and exiting the minib=
uffer."
   (interactive)
   (delete-minibuffer-contents)
-  (insert (event-basic-type last-command-event))
+  (insert last-command-event)
   (exit-minibuffer))
=20
-(defun read-char-from-minibuffer (prompt)
-  "Read a character from the minibuffer, prompting with string PROMPT.
-Like `read-char', but allows navigating in a history.  The navigation
-commands are `M-p' and `M-n', with `RET' to select a character from
-history."
-  (let ((result
-         (read-from-minibuffer prompt nil
-                               read-char-from-minibuffer-map nil
-                               'read-char-from-minibuffer-history)))
-    (if (> (length result) 0)
-        ;; We have a string (with one character), so return the first on=
e.
-        (elt result 0)
-      ;; The default value is RET.
-      (push "\r" read-char-from-minibuffer-history)
-      ?\r)))
+(defun read-char-from-minibuffer-insert-other ()
+  "Insert the character you type in the minibuffer and exit.
+Discard all input in a minibuffer before inserting and exiting the minib=
uffer."
+  (interactive)
+  (delete-minibuffer-contents)
+  (beep)
+  (minibuffer-message "Wrong answer")
+  (sit-for 2))
+
+(defvar empty-history)
+
+(defun read-char-from-minibuffer (prompt &optional chars history)
+  "Read a character from the minibuffer, prompting for PROMPT.
+Like `read-char', but uses the minibuffer to read and return a character=
.
+When CHARS is non-nil, any input that is not one of CHARS is ignored.
+When HISTORY is a symbol, then allows navigating in a history.
+The navigation commands are `M-p' and `M-n', with `RET' to select
+a character from history."
+  (discard-input)
+  (let* ((empty-history '())
+         (map (if (consp chars)
+                  (or (gethash chars read-char-from-minibuffer-map-hash)
+                      (puthash chars
+                               (let ((map (make-sparse-keymap)))
+                                 (set-keymap-parent map read-char-from-m=
inibuffer-map)
+                                 (dolist (char chars)
+                                   (define-key map (vector char)
+                                     'read-char-from-minibuffer-insert-c=
har))
+                                 (define-key map [remap self-insert-comm=
and]
+                                   'read-char-from-minibuffer-insert-oth=
er)
+                                 ;; (define-key map [remap t]
+                                 ;;   'read-char-from-minibuffer-insert-=
other)
+                                 map)
+                               read-char-from-minibuffer-map-hash))
+                read-char-from-minibuffer-map))
+         (result
+          (read-from-minibuffer prompt nil map nil
+                                (or history 'empty-history)))
+         (char
+          (if (> (length result) 0)
+              ;; We have a string (with one character), so return the fi=
rst one.
+              (elt result 0)
+            ;; The default value is RET.
+            (push "\r" read-char-history)
+            ?\r)))
+    ;; Display the question with the answer.
+    (message "%s%s" prompt (char-to-string char))
+    char))
=20
 (defun zap-to-char (arg char)
   "Kill up to and including ARGth occurrence of CHAR.
 Case is ignored if `case-fold-search' is non-nil in the current buffer.
 Goes backward if ARG is negative; error if CHAR not found."
   (interactive (list (prefix-numeric-value current-prefix-arg)
-		     (read-char-from-minibuffer "Zap to char: ")))
+		     (read-char-from-minibuffer "Zap to char: "
+                                                nil 'read-char-history))=
)
   ;; Avoid "obsolete" warnings for translation-table-for-input.
   (with-no-warnings
     (if (char-table-p translation-table-for-input)
diff --git a/lisp/subr.el b/lisp/subr.el
index 03cf3da278..33464d6032 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2668,6 +2668,70 @@ sit-for
 ;; Behind display-popup-menus-p test.
 (declare-function x-popup-dialog "menu.c" (position contents &optional h=
eader))
=20
+(defvar y-or-n-p-history-variable nil
+  "History list symbol to add `y-or-n-p' answers to.")
+
+(defvar y-or-n-p-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map minibuffer-local-map)
+
+    ;; (define-key map [t] 'y-or-n-p-insert-other)
+
+    (define-key map [remap act] 'y-or-n-p-insert-y)
+    (define-key map [remap act-and-show] 'y-or-n-p-insert-y)
+    (define-key map [remap act-and-exit] 'y-or-n-p-insert-y)
+    (define-key map [remap automatic] 'y-or-n-p-insert-y)
+
+    (define-key map [remap skip] 'y-or-n-p-insert-n)
+
+    (define-key map [remap help] 'y-or-n-p-insert-other)
+    (define-key map [remap backup] 'y-or-n-p-insert-other)
+    (define-key map [remap undo] 'y-or-n-p-insert-other)
+    (define-key map [remap undo-all] 'y-or-n-p-insert-other)
+    (define-key map [remap edit] 'y-or-n-p-insert-other)
+    (define-key map [remap edit-replacement] 'y-or-n-p-insert-other)
+    (define-key map [remap delete-and-edit] 'y-or-n-p-insert-other)
+    (define-key map [remap ignore] 'y-or-n-p-insert-other)
+    (define-key map [remap self-insert-command] 'y-or-n-p-insert-other)
+
+    (define-key map [remap recenter] 'minibuffer-recenter-top-bottom)
+
+    (define-key map [remap quit] 'abort-recursive-edit)
+    (define-key map [remap exit] 'abort-recursive-edit)
+    (define-key map [remap exit-prefix] 'abort-recursive-edit)
+    (define-key map [escape] 'abort-recursive-edit)
+
+    ;; (define-key map [remap t] 'y-or-n-p-insert-other)
+
+    map)
+  "Keymap that defines additional bindings for `y-or-n-p' answers.")
+
+(defun y-or-n-p-insert-y ()
+  "Insert the answer \"y\" and exit the minibuffer of `y-or-n-p'.
+Discard all input in a minibuffer before inserting."
+  (interactive)
+  (delete-minibuffer-contents)
+  (insert "y")
+  (exit-minibuffer))
+
+(defun y-or-n-p-insert-n ()
+  "Insert the answer \"n\" and exit the minibuffer of `y-or-n-p'.
+Discard all input in a minibuffer before inserting."
+  (interactive)
+  (delete-minibuffer-contents)
+  (insert "n")
+  (exit-minibuffer))
+
+(defun y-or-n-p-insert-other ()
+  "Handle inserting of other answers in the minibuffer of `y-or-n-p'."
+  (interactive)
+  (delete-minibuffer-contents)
+  (ding)
+  (minibuffer-message "Please answer y or n")
+  (sit-for 2))
+
+(defvar empty-history)
+
 (defun y-or-n-p (prompt)
   "Ask user a \"y or n\" question.
 Return t if answer is \"y\" and nil if it is \"n\".
@@ -2683,16 +2747,13 @@ y-or-n-p
 case, the useful bindings are `act', `skip', `recenter',
 `scroll-up', `scroll-down', and `quit'.
 An `act' response means yes, and a `skip' response means no.
-A `quit' response means to invoke `keyboard-quit'.
+A `quit' response means to invoke `abort-recursive-edit'.
 If the user enters `recenter', `scroll-up', or `scroll-down'
 responses, perform the requested window recentering or scrolling
 and ask again.
=20
 Under a windowing system a dialog box will be used if `last-nonmenu-even=
t'
 is nil and `use-dialog-box' is non-nil."
-  ;; =A1Beware! 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)))
@@ -2718,36 +2779,14 @@ y-or-n-p
 	    answer (x-popup-dialog t `(,prompt ("Yes" . act) ("No" . skip)))))
      (t
       (setq prompt (funcall padded prompt))
-      (while
-          (let* ((scroll-actions '(recenter scroll-up scroll-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-action=
s)
-                                              prompt
-                                            (concat "Please answer y or =
n.  "
-                                                    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)
-	     ((eq answer 'scroll-up)
-	      (ignore-errors (scroll-up-command)) t)
-	     ((eq answer 'scroll-down)
-	      (ignore-errors (scroll-down-command)) t)
-	     ((eq answer 'scroll-other-window)
-	      (ignore-errors (scroll-other-window)) t)
-	     ((eq answer 'scroll-other-window-down)
-	      (ignore-errors (scroll-other-window-down)) t)
-	     ((or (memq answer '(exit-prefix quit)) (eq key ?\e))
-	      (signal 'quit nil) t)
-	     (t t)))
-        (ding)
-        (discard-input))))
+      (discard-input)
+      (let* ((empty-history '())
+             (str (read-from-minibuffer
+                   prompt nil
+                   (make-composed-keymap y-or-n-p-map query-replace-map)
+                   nil
+                   (or y-or-n-p-history-variable 'empty-history))))
+        (setq answer (if (member str '("y" "Y")) 'act 'skip)))))
     (let ((ret (eq answer 'act)))
       (unless noninteractive
         (message "%s%c" prompt (if ret ?y ?n)))
diff --git a/lisp/userlock.el b/lisp/userlock.el
index 209768620c..1d3ac84584 100644
--- a/lisp/userlock.el
+++ b/lisp/userlock.el
@@ -152,7 +152,7 @@ ask-user-about-supersession-threat
 	(message "%s" prompt)
 	(error "Cannot resolve conflict in batch mode"))
       (while (null answer)
-	(setq answer (read-char-choice prompt choices))
+	(setq answer (read-char-from-minibuffer prompt choices))
 	(cond ((memq answer '(?? ?\C-h))
 	       (ask-user-about-supersession-help)
 	       (setq answer nil))
diff --git a/src/window.c b/src/window.c
index 0fa0bdf7b9..c01f5c4aa3 100644
--- a/src/window.c
+++ b/src/window.c
@@ -6253,12 +6253,12 @@ DEFUN ("other-window-for-scrolling", Fother_windo=
w_for_scrolling, Sother_window_
     {
       /* Nothing specified; look for a neighboring window on the same
 	 frame.  */
-      window =3D Fnext_window (selected_window, Qnil, Qnil);
+      window =3D Fnext_window (selected_window, Qlambda, Qnil);
=20
       if (EQ (window, selected_window))
 	/* That didn't get us anywhere; look for a window on another
            visible frame on the current terminal.  */
-        window =3D Fnext_window (window, Qnil, Qvisible);
+        window =3D Fnext_window (window, Qlambda, Qvisible);
     }
=20
   CHECK_LIVE_WINDOW (window);

--=-=-=--




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#38076; Package emacs. Full text available.

Message received at submit <at> debbugs.gnu.org:


Received: (at submit) by debbugs.gnu.org; 5 Nov 2019 22:58:46 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Tue Nov 05 17:58:45 2019
Received: from localhost ([127.0.0.1]:39185 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1iS7mf-0004xA-Ab
	for submit <at> debbugs.gnu.org; Tue, 05 Nov 2019 17:58:45 -0500
Received: from lists.gnu.org ([209.51.188.17]:51392)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <juri@HIDDEN>) id 1iS7mc-0004x2-TQ
 for submit <at> debbugs.gnu.org; Tue, 05 Nov 2019 17:58:43 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:33115)
 by lists.gnu.org with esmtp (Exim 4.90_1)
 (envelope-from <juri@HIDDEN>) id 1iS7ma-00076K-RT
 for bug-gnu-emacs@HIDDEN; Tue, 05 Nov 2019 17:58:42 -0500
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org
X-Spam-Level: 
X-Spam-Status: No, score=0.8 required=5.0 tests=BAYES_50,RCVD_IN_DNSWL_NONE,
 URIBL_BLOCKED autolearn=disabled version=3.3.2
Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71)
 (envelope-from <juri@HIDDEN>) id 1iS7mY-0001iH-Nn
 for bug-gnu-emacs@HIDDEN; Tue, 05 Nov 2019 17:58:40 -0500
Received: from crocodile.birch.relay.mailchannels.net ([23.83.209.45]:31566)
 by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32)
 (Exim 4.71) (envelope-from <juri@HIDDEN>) id 1iS7mY-0001fu-A4
 for bug-gnu-emacs@HIDDEN; Tue, 05 Nov 2019 17:58:38 -0500
X-Sender-Id: dreamhost|x-authsender|jurta@HIDDEN
Received: from relay.mailchannels.net (localhost [127.0.0.1])
 by relay.mailchannels.net (Postfix) with ESMTP id 8CE1C342180
 for <bug-gnu-emacs@HIDDEN>; Tue,  5 Nov 2019 22:58:36 +0000 (UTC)
Received: from pdx1-sub0-mail-a64.g.dreamhost.com
 (100-96-14-250.trex.outbound.svc.cluster.local [100.96.14.250])
 (Authenticated sender: dreamhost)
 by relay.mailchannels.net (Postfix) with ESMTPA id 08B6A3420BC
 for <bug-gnu-emacs@HIDDEN>; Tue,  5 Nov 2019 22:58:36 +0000 (UTC)
X-Sender-Id: dreamhost|x-authsender|jurta@HIDDEN
Received: from pdx1-sub0-mail-a64.g.dreamhost.com ([TEMPUNAVAIL].
 [64.90.62.162]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384)
 by 0.0.0.0:2500 (trex/5.18.5); Tue, 05 Nov 2019 22:58:36 +0000
X-MC-Relay: Neutral
X-MailChannels-SenderId: dreamhost|x-authsender|jurta@HIDDEN
X-MailChannels-Auth-Id: dreamhost
X-Oafish-White: 030f564c403fd0fb_1572994716434_2387425792
X-MC-Loop-Signature: 1572994716434:1881170825
X-MC-Ingress-Time: 1572994716434
Received: from pdx1-sub0-mail-a64.g.dreamhost.com (localhost [127.0.0.1])
 by pdx1-sub0-mail-a64.g.dreamhost.com (Postfix) with ESMTP id 363447F512
 for <bug-gnu-emacs@HIDDEN>; Tue,  5 Nov 2019 14:58:34 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=linkov.net; h=from:to
 :subject:references:date:in-reply-to:message-id:mime-version
 :content-type; s=linkov.net; bh=u63KV0nGDqubDuwBVG63gy2pqNw=; b=
 vcuEpde4+JhkJ2sHSGvdDP0OSxro+8tp0Z9KdmxgU4gKhrJlJgpXcDlc0/u29DC1
 MJKOYcUMvZpWKuBoCD+0oUXMX2JYfatZWkWKKSv/EQKW5BvKhX0yrhwr8v+WwMEa
 77zD6+yR6kwLC9oWVsPsXDout8gj5Q7i3fdfJFwRbUc=
Received: from mail.jurta.org (m91-129-101-77.cust.tele2.ee [91.129.101.77])
 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
 (No client certificate requested)
 (Authenticated sender: jurta@HIDDEN)
 by pdx1-sub0-mail-a64.g.dreamhost.com (Postfix) with ESMTPSA id D05767F51D
 for <bug-gnu-emacs@HIDDEN>; Tue,  5 Nov 2019 14:58:32 -0800 (PST)
X-DH-BACKEND: pdx1-sub0-mail-a64
From: Juri Linkov <juri@HIDDEN>
To: bug-gnu-emacs@HIDDEN
Subject: Using minibuffer for y-or-n-p
Organization: LINKOV.NET
References: <20191026101407.GA17424@ACM>
 <jwvmudnd6rk.fsf-monnier+emacs@HIDDEN> <87o8y3d4ur.fsf@HIDDEN>
 <87mudl3l83.fsf@HIDDEN> <87v9s9b12x.fsf@HIDDEN>
 <87ftjctsx5.fsf@HIDDEN> <87k18n233r.fsf@HIDDEN>
 <87pnifcdzd.fsf@HIDDEN> <87d0efxfxg.fsf@HIDDEN>
 <875zk57sqk.fsf@HIDDEN>
 <jwvd0edwuaw.fsf-monnier+emacs@HIDDEN>
Date: Wed, 06 Nov 2019 00:54:37 +0200
In-Reply-To: <jwvd0edwuaw.fsf-monnier+emacs@HIDDEN> (Stefan Monnier's message
 of "Wed, 30 Oct 2019 22:00:40 -0400")
Message-ID: <87r22m53yq.fsf_-_@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (x86_64-pc-linux-gnu)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic]
 [fuzzy]
X-Received-From: 23.83.209.45
X-Spam-Score: -1.4 (-)
X-Debbugs-Envelope-To: submit
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -2.4 (--)

--=-=-=
Content-Type: text/plain

X-Debbugs-CC: Stefan Monnier <monnier@HIDDEN>
Tags: patch

>> query-replace-map needs to be translated to another keymap
>> where the same characters from 'query-replace-map'
>> run real commands, not intermediate symbols.
>
> E.g.
>
>     (defvar foo-remapping-map
>       (let ((map (make-sparse-keymap)))
>         (define-key map [remap ask] '...)
>         ...
>         map))
>
> and then
>
>       ... (make-composed-keymap query-replace-map foo-remapping-map) ..

This also required adding the same feature that supported recentering/scrolling
in y-or-n-p to the minibuffer as well.  A large part of old implementation
of y-or-n-p handled recentering/scrolling.  Now the minibuffer supports
the same commands by using the new macro 'with-minibuffer-selected-window'.

window.c was changed to use the 'lambda' value for MINIBUF arg of 'next-window',
so minibuffer-scroll-other-window/minibuffer-scroll-other-window-down
doesn't try to scroll the minibuffer window.

A new history variable 'y-or-n-p-history-variable' is nil by default,
so no history is used in 'y-or-n-p' minibuffer.

This patch was tested with various commands that use 'y-or-n-p'
and seems to work fine:


--=-=-=
Content-Type: text/x-diff; charset=iso-8859-1
Content-Disposition: inline; filename=y-or-n-p-minibuffer.patch
Content-Transfer-Encoding: quoted-printable

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 43dd277a2e..0c55954b02 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2236,6 +2236,13 @@ completion-help-at-point
 (let ((map minibuffer-local-map))
   (define-key map "\C-g" 'abort-recursive-edit)
   (define-key map "\M-<" 'minibuffer-beginning-of-buffer)
+
+  (define-key map [remap recenter-top-bottom] 'minibuffer-recenter-top-b=
ottom)
+  (define-key map [remap scroll-up-command] 'minibuffer-scroll-up-comman=
d)
+  (define-key map [remap scroll-down-command] 'minibuffer-scroll-down-co=
mmand)
+  (define-key map [remap scroll-other-window] 'minibuffer-scroll-other-w=
indow)
+  (define-key map [remap scroll-other-window-down] 'minibuffer-scroll-ot=
her-window-down)
+
   (define-key map "\r" 'exit-minibuffer)
   (define-key map "\n" 'exit-minibuffer))
=20
@@ -3670,6 +3677,46 @@ minibuffer-beginning-of-buffer
   (when (and arg (not (consp arg)))
     (forward-line 1)))
=20
+(defmacro with-minibuffer-selected-window (&rest body)
+  "Execute the forms in BODY from the minibuffer in its original window.
+When used in a minibuffer window, select the window selected just before
+minibuffer window was selected, and execute the forms."
+  (declare (indent 0) (debug t))
+  `(let ((window (minibuffer-selected-window)))
+     (when window
+       (with-selected-window window
+         ,@body))))
+
+(defun minibuffer-recenter-top-bottom (&optional arg)
+  "Run `recenter-top-bottom' from minibuffer in original window."
+  (interactive "P")
+  (with-minibuffer-selected-window
+    (recenter-top-bottom arg)))
+
+(defun minibuffer-scroll-up-command (&optional arg)
+  "Run `scroll-up-command' from minibuffer in original window."
+  (interactive "^P")
+  (with-minibuffer-selected-window
+    (scroll-up-command arg)))
+
+(defun minibuffer-scroll-down-command (&optional arg)
+  "Run `scroll-down-command' from minibuffer in original window."
+  (interactive "^P")
+  (with-minibuffer-selected-window
+    (scroll-down-command arg)))
+
+(defun minibuffer-scroll-other-window (&optional arg)
+  "Run `scroll-other-window' from minibuffer in original window."
+  (interactive "P")
+  (with-minibuffer-selected-window
+    (scroll-other-window arg)))
+
+(defun minibuffer-scroll-other-window-down (&optional arg)
+  "Run `scroll-other-window-down' from minibuffer in original window."
+  (interactive "^P")
+  (with-minibuffer-selected-window
+    (scroll-other-window-down arg)))
+
 (provide 'minibuffer)
=20
 ;;; minibuffer.el ends here
diff --git a/lisp/subr.el b/lisp/subr.el
index 03cf3da278..0a8a505b70 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2668,6 +2668,66 @@ sit-for
 ;; Behind display-popup-menus-p test.
 (declare-function x-popup-dialog "menu.c" (position contents &optional h=
eader))
=20
+(defvar y-or-n-p-history-variable nil
+  "History list symbol to add `y-or-n-p' answers to.")
+
+(defvar y-or-n-p-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map minibuffer-local-map)
+
+    (define-key map [remap act] 'y-or-n-p-insert-y)
+    (define-key map [remap act-and-show] 'y-or-n-p-insert-y)
+    (define-key map [remap act-and-exit] 'y-or-n-p-insert-y)
+    (define-key map [remap automatic] 'y-or-n-p-insert-y)
+
+    (define-key map [remap skip] 'y-or-n-p-insert-n)
+
+    (define-key map [remap help] 'y-or-n-p-insert-other)
+    (define-key map [remap backup] 'y-or-n-p-insert-other)
+    (define-key map [remap undo] 'y-or-n-p-insert-other)
+    (define-key map [remap undo-all] 'y-or-n-p-insert-other)
+    (define-key map [remap edit] 'y-or-n-p-insert-other)
+    (define-key map [remap edit-replacement] 'y-or-n-p-insert-other)
+    (define-key map [remap delete-and-edit] 'y-or-n-p-insert-other)
+    (define-key map [remap ignore] 'y-or-n-p-insert-other)
+    (define-key map [remap self-insert-command] 'y-or-n-p-insert-other)
+
+    (define-key map [remap recenter] 'minibuffer-recenter-top-bottom)
+
+    (define-key map [remap quit] 'abort-recursive-edit)
+    (define-key map [remap exit] 'abort-recursive-edit)
+    (define-key map [remap exit-prefix] 'abort-recursive-edit)
+    (define-key map [escape] 'abort-recursive-edit)
+
+    map)
+  "Keymap that defines additional bindings for `y-or-n-p' answers.")
+
+(defun y-or-n-p-insert-y ()
+  "Insert the answer \"y\" and exit the minibuffer of `y-or-n-p'.
+Discard all input in a minibuffer before inserting."
+  (interactive)
+  (delete-minibuffer-contents)
+  (insert "y")
+  (exit-minibuffer))
+
+(defun y-or-n-p-insert-n ()
+  "Insert the answer \"n\" and exit the minibuffer of `y-or-n-p'.
+Discard all input in a minibuffer before inserting."
+  (interactive)
+  (delete-minibuffer-contents)
+  (insert "n")
+  (exit-minibuffer))
+
+(defun y-or-n-p-insert-other ()
+  "Handle inserting of other answers in the minibuffer of `y-or-n-p'."
+  (interactive)
+  (delete-minibuffer-contents)
+  (ding)
+  (minibuffer-message "Please answer y or n.")
+  (sit-for 2))
+
+(defvar empty-history)
+
 (defun y-or-n-p (prompt)
   "Ask user a \"y or n\" question.
 Return t if answer is \"y\" and nil if it is \"n\".
@@ -2683,16 +2743,13 @@ y-or-n-p
 case, the useful bindings are `act', `skip', `recenter',
 `scroll-up', `scroll-down', and `quit'.
 An `act' response means yes, and a `skip' response means no.
-A `quit' response means to invoke `keyboard-quit'.
+A `quit' response means to invoke `abort-recursive-edit'.
 If the user enters `recenter', `scroll-up', or `scroll-down'
 responses, perform the requested window recentering or scrolling
 and ask again.
=20
 Under a windowing system a dialog box will be used if `last-nonmenu-even=
t'
 is nil and `use-dialog-box' is non-nil."
-  ;; =A1Beware! 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)))
@@ -2718,36 +2775,13 @@ y-or-n-p
 	    answer (x-popup-dialog t `(,prompt ("Yes" . act) ("No" . skip)))))
      (t
       (setq prompt (funcall padded prompt))
-      (while
-          (let* ((scroll-actions '(recenter scroll-up scroll-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-action=
s)
-                                              prompt
-                                            (concat "Please answer y or =
n.  "
-                                                    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)
-	     ((eq answer 'scroll-up)
-	      (ignore-errors (scroll-up-command)) t)
-	     ((eq answer 'scroll-down)
-	      (ignore-errors (scroll-down-command)) t)
-	     ((eq answer 'scroll-other-window)
-	      (ignore-errors (scroll-other-window)) t)
-	     ((eq answer 'scroll-other-window-down)
-	      (ignore-errors (scroll-other-window-down)) t)
-	     ((or (memq answer '(exit-prefix quit)) (eq key ?\e))
-	      (signal 'quit nil) t)
-	     (t t)))
-        (ding)
-        (discard-input))))
+      (let* ((empty-history '())
+             (str (read-from-minibuffer
+                   prompt nil
+                   (make-composed-keymap y-or-n-p-map query-replace-map)
+                   nil
+                   (or y-or-n-p-history-variable 'empty-history))))
+        (setq answer (if (member str '("y" "Y")) 'act 'skip)))))
     (let ((ret (eq answer 'act)))
       (unless noninteractive
         (message "%s%c" prompt (if ret ?y ?n)))
diff --git a/src/window.c b/src/window.c
index 0fa0bdf7b9..c01f5c4aa3 100644
--- a/src/window.c
+++ b/src/window.c
@@ -6253,12 +6253,12 @@ DEFUN ("other-window-for-scrolling", Fother_windo=
w_for_scrolling, Sother_window_
     {
       /* Nothing specified; look for a neighboring window on the same
 	 frame.  */
-      window =3D Fnext_window (selected_window, Qnil, Qnil);
+      window =3D Fnext_window (selected_window, Qlambda, Qnil);
=20
       if (EQ (window, selected_window))
 	/* That didn't get us anywhere; look for a window on another
            visible frame on the current terminal.  */
-        window =3D Fnext_window (window, Qnil, Qvisible);
+        window =3D Fnext_window (window, Qlambda, Qvisible);
     }
=20
   CHECK_LIVE_WINDOW (window);

--=-=-=--




Acknowledgement sent to Juri Linkov <juri@HIDDEN>:
New bug report received and forwarded. Copy sent to monnier@HIDDEN, bug-gnu-emacs@HIDDEN. Full text available.
Report forwarded to monnier@HIDDEN, bug-gnu-emacs@HIDDEN:
bug#38076; Package emacs. Full text available.
Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.
Last modified: Tue, 12 Nov 2019 21:15:01 UTC

GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997 nCipher Corporation Ltd, 1994-97 Ian Jackson.