all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
@ 2019-10-26 10:14 Alan Mackenzie
  2019-10-26 12:41 ` Stefan Monnier
  2019-10-27 21:51 ` Juri Linkov
  0 siblings, 2 replies; 26+ messages in thread
From: Alan Mackenzie @ 2019-10-26 10:14 UTC (permalink / raw)
  To: emacs-devel

Hello, Emacs.

In do-after-load-evaluation, we have the following, near the end:

          (run-with-timer 0 nil
                          (lambda (msg)
                            (message "%s" msg))
                          msg)

.  run-with-timer is being used to run message once, immediately.  Why
not just call message?

The reason I ask is that whilst loading my desktop, the prompt

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

, asking me whether I want dangerous local variables to be loaded, is
getting obscured by the less important

        Package cl is deprecated

, and I have to know that I'm expected to respond to this obscured
prompt.  This has been happening to me only for a short time, at least a
week, but probably less than a month.

This last message about cl being deprecated is being output by the
strange run-with-timer.  If I replace the run-with-timer form with a
straight message call, I see the prompt from hack-local-variables.

What is going on, here?  Is the run-with-timer mechanism being used
deliberately to make the "deprecated" message prevail over other
messages?  If so, perhaps it shouldn't be.

<A bit later>

I've searched the git log, and found that cl was moved into lisp/obsolete
on 2019-08-06.  At the same time, some change was made to output the
"deprecated" message in do-after-load-evaluation.

The current situation seems unsatisfactory; the prompt from
hack-local-variables is more important than the deprecation message, and
shouldn't be obscured by it.

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 10:14 Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation Alan Mackenzie
@ 2019-10-26 12:41 ` Stefan Monnier
  2019-10-26 13:16   ` Lars Ingebrigtsen
                     ` (2 more replies)
  2019-10-27 21:51 ` Juri Linkov
  1 sibling, 3 replies; 26+ messages in thread
From: Stefan Monnier @ 2019-10-26 12:41 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

>           (run-with-timer 0 nil
>                           (lambda (msg)
>                             (message "%s" msg))
>                           msg)
>
> run-with-timer is being used to run message once, immediately.
> Why not just call message?

Good question.  It's been that way since

    commit 5766c380eec20a19844253cbb511922b6c70fc0b
    Author: Stefan Monnier <monnier@iro.umontreal.ca>
    Date:   Sat Sep 12 03:35:40 2009 +0000
    
        * lread.c (Fload): Don't output a message after loading an obsolete
        package any more (done in Lisp now).
        * subr.el (do-after-load-evaluation): Warn the user after loading an
        obsolete package.

but I can't see any trace of an explanation nor can I find it in my memory.

> What is going on, here?  Is the run-with-timer mechanism being used
> deliberately to make the "deprecated" message prevail over other
> messages?

That's my best guess, yes.

> The current situation seems unsatisfactory; the prompt from
> hack-local-variables is more important than the deprecation message,
> and shouldn't be obscured by it.

It's a kind of general problem with messages, indeed.


        Stefan




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 12:41 ` Stefan Monnier
@ 2019-10-26 13:16   ` Lars Ingebrigtsen
  2019-10-26 16:38     ` Stefan Monnier
  2019-10-27 21:57     ` Juri Linkov
  2019-10-26 14:18   ` Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation Alan Mackenzie
  2019-10-26 15:27   ` Juanma Barranquero
  2 siblings, 2 replies; 26+ messages in thread
From: Lars Ingebrigtsen @ 2019-10-26 13:16 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Alan Mackenzie, emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> The current situation seems unsatisfactory; the prompt from
>> hack-local-variables is more important than the deprecation message,
>> and shouldn't be obscured by it.
>
> It's a kind of general problem with messages, indeed.

Yup.  There are several bug reports on this (general) issue of
asynchronous messages hiding queries.  One suggestion has been to extend
the echo area in these instances, showing the message (perhaps) below
the prompt.  Another is to have the prompt re-assert itself after a
timeout, making the message disappear.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 12:41 ` Stefan Monnier
  2019-10-26 13:16   ` Lars Ingebrigtsen
@ 2019-10-26 14:18   ` Alan Mackenzie
  2019-10-26 15:27   ` Juanma Barranquero
  2 siblings, 0 replies; 26+ messages in thread
From: Alan Mackenzie @ 2019-10-26 14:18 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello, Stefan.

On Sat, Oct 26, 2019 at 08:41:27 -0400, Stefan Monnier wrote:
> >           (run-with-timer 0 nil
> >                           (lambda (msg)
> >                             (message "%s" msg))
> >                           msg)

> > run-with-timer is being used to run message once, immediately.
> > Why not just call message?

> Good question.  It's been that way since

>     commit 5766c380eec20a19844253cbb511922b6c70fc0b
>     Author: Stefan Monnier <monnier@iro.umontreal.ca>
>     Date:   Sat Sep 12 03:35:40 2009 +0000
    
>         * lread.c (Fload): Don't output a message after loading an obsolete
>         package any more (done in Lisp now).
>         * subr.el (do-after-load-evaluation): Warn the user after loading an
>         obsolete package.

> but I can't see any trace of an explanation nor can I find it in my memory.

> > What is going on, here?  Is the run-with-timer mechanism being used
> > deliberately to make the "deprecated" message prevail over other
> > messages?

> That's my best guess, yes.

I think I can see what's happening, now.  The (run-with-timer 0 nil ...)
mechanism doesn't trigger its function until Emacs is waiting for input.
This will be in the minibuffer routines, just after the y-or-n-p has
displayed its prompt string and tries to read a character.  So the
(run-with-timer ...) message will overwrite any prompt string.

I have counted 11 occurrences of run-with-timer or run-at-time with a
zero delay in them.

So, the (run-with-timer ...) seems indeed to be being used to raise the
"priority" of its message.  Maybe a simple message call should be used
here instead.

> > The current situation seems unsatisfactory; the prompt from
> > hack-local-variables is more important than the deprecation message,
> > and shouldn't be obscured by it.

> It's a kind of general problem with messages, indeed.

Yes.

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 12:41 ` Stefan Monnier
  2019-10-26 13:16   ` Lars Ingebrigtsen
  2019-10-26 14:18   ` Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation Alan Mackenzie
@ 2019-10-26 15:27   ` Juanma Barranquero
  2 siblings, 0 replies; 26+ messages in thread
From: Juanma Barranquero @ 2019-10-26 15:27 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Alan Mackenzie, Emacs developers

[-- Attachment #1: Type: text/plain, Size: 634 bytes --]

On Sat, Oct 26, 2019 at 2:42 PM Stefan Monnier <monnier@iro.umontreal.ca>
wrote:
> > What is going on, here?  Is the run-with-timer mechanism being used
> > deliberately to make the "deprecated" message prevail over other
> > messages?
>
> That's my best guess, yes.

Wouldn't that kind of message be a perfect candidate for a warning instead?
Warnings are underused in Emacs now, but they have advantages, like the
ability to filter them by level/priority.

The warnings are shown in its own buffer (and window), but by using
fit-to-buffer and the new window machinery we could perhaps make them less
jarring and still hard to miss.

[-- Attachment #2: Type: text/html, Size: 794 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 13:16   ` Lars Ingebrigtsen
@ 2019-10-26 16:38     ` Stefan Monnier
  2019-10-27  1:01       ` HaiJun Zhang
  2019-10-27 21:57     ` Juri Linkov
  1 sibling, 1 reply; 26+ messages in thread
From: Stefan Monnier @ 2019-10-26 16:38 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Alan Mackenzie, emacs-devel

>> It's a kind of general problem with messages, indeed.
> Yup.  There are several bug reports on this (general) issue of
> asynchronous messages hiding queries.

It goes further than that: it's also messages hiding each other, whether
asynchronous or not.


        Stefan




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 16:38     ` Stefan Monnier
@ 2019-10-27  1:01       ` HaiJun Zhang
  0 siblings, 0 replies; 26+ messages in thread
From: HaiJun Zhang @ 2019-10-27  1:01 UTC (permalink / raw)
  To: Lars Ingebrigtsen, Stefan Monnier; +Cc: Alan Mackenzie, emacs-devel@gnu.org

[-- Attachment #1: Type: text/plain, Size: 239 bytes --]

There is a variable `minibuffer-message-timeout’ which seems to be related with this issue. From my understanding, if it is set to 2, the message will disappears after 2s and the prompt restores. But it doesn’t work. See bug#34614.


[-- Attachment #2: Type: text/html, Size: 532 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 10:14 Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation Alan Mackenzie
  2019-10-26 12:41 ` Stefan Monnier
@ 2019-10-27 21:51 ` Juri Linkov
  2019-10-28  3:34   ` Eli Zaretskii
  1 sibling, 1 reply; 26+ messages in thread
From: Juri Linkov @ 2019-10-27 21:51 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

> The reason I ask is that whilst loading my desktop, the prompt
>
>         Please type y, n, or !, or C-v to scroll:
>
> , asking me whether I want dangerous local variables to be loaded, is
> getting obscured by the less important
>
>         Package cl is deprecated

For two months I'm seeing the same problem with the message
"Package cl is deprecated" obscuring the y-or-n-p prompt:

  Warning: desktop file appears to be in use.  Use it anyway?

then I type 'y' blindly without seeing the obscured prompt only because
I expect this prompt to appear, but this can't be a general solution.

Maybe it's possible to delay warnings until desktop is loaded?



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-26 13:16   ` Lars Ingebrigtsen
  2019-10-26 16:38     ` Stefan Monnier
@ 2019-10-27 21:57     ` Juri Linkov
  2019-10-27 22:29       ` Juri Linkov
                         ` (2 more replies)
  1 sibling, 3 replies; 26+ messages in thread
From: Juri Linkov @ 2019-10-27 21:57 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

>>> The current situation seems unsatisfactory; the prompt from
>>> hack-local-variables is more important than the deprecation message,
>>> and shouldn't be obscured by it.
>>
>> It's a kind of general problem with messages, indeed.
>
> Yup.  There are several bug reports on this (general) issue of
> asynchronous messages hiding queries.  One suggestion has been to extend
> the echo area in these instances, showing the message (perhaps) below
> the prompt.  Another is to have the prompt re-assert itself after a
> timeout, making the message disappear.

These bug reports are fixed now when messages are displayed in the
minibuffer, but the problem still exists for the y-or-n-p prompt.

Why y-or-n-p doesn't use normal minibuffer functions?

It can finely use read-from-minibuffer with a keymap where
'y' and 'n' keys are bound to minibuffer-exiting commands.

Then history commands will be available for free, so you don't need
to re-implement minibuffer history commands with `read-char-with-history'.



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-27 21:57     ` Juri Linkov
@ 2019-10-27 22:29       ` Juri Linkov
  2019-10-28  9:41         ` martin rudalics
  2019-10-28  2:13       ` Stefan Monnier
  2019-10-28 10:45       ` Lars Ingebrigtsen
  2 siblings, 1 reply; 26+ messages in thread
From: Juri Linkov @ 2019-10-27 22:29 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

> Why y-or-n-p doesn't use normal minibuffer functions?
>
> It can finely use read-from-minibuffer with a keymap where
> 'y' and 'n' keys are bound to minibuffer-exiting commands.
>
> Then history commands will be available for free, so you don't need
> to re-implement minibuffer history commands with `read-char-with-history'.

I meant something like

(defvar y-n-p-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map minibuffer-local-map)
    (define-key map "y" (lambda ()
                          (interactive)
                          (delete-minibuffer-contents)
                          (insert "y")
                          (exit-minibuffer)))
    (define-key map "n" (lambda ()
                          (interactive)
                          (delete-minibuffer-contents)
                          (insert "n")
                          (exit-minibuffer)))
    map))

(defun y-n-p (prompt)
  (read-from-minibuffer prompt nil y-n-p-map nil 'y-n-p-history))

(y-n-p "Please type y or n: ")

Then all history commands are available, e.g. 'M-p' or the up arrow.



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-27 21:57     ` Juri Linkov
  2019-10-27 22:29       ` Juri Linkov
@ 2019-10-28  2:13       ` Stefan Monnier
  2019-10-28 10:45       ` Lars Ingebrigtsen
  2 siblings, 0 replies; 26+ messages in thread
From: Stefan Monnier @ 2019-10-28  2:13 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Alan Mackenzie, Lars Ingebrigtsen, emacs-devel

> Why y-or-n-p doesn't use normal minibuffer functions?

Fine by me.


        Stefan




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-27 21:51 ` Juri Linkov
@ 2019-10-28  3:34   ` Eli Zaretskii
  0 siblings, 0 replies; 26+ messages in thread
From: Eli Zaretskii @ 2019-10-28  3:34 UTC (permalink / raw)
  To: Juri Linkov; +Cc: acm, emacs-devel

> From: Juri Linkov <juri@linkov.net>
> Date: Sun, 27 Oct 2019 23:51:51 +0200
> Cc: emacs-devel@gnu.org
> 
> Maybe it's possible to delay warnings until desktop is loaded?

We already do, perhaps something there is not working?



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-27 22:29       ` Juri Linkov
@ 2019-10-28  9:41         ` martin rudalics
  0 siblings, 0 replies; 26+ messages in thread
From: martin rudalics @ 2019-10-28  9:41 UTC (permalink / raw)
  To: Juri Linkov, Lars Ingebrigtsen
  Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

 > I meant something like

It would certainly remove the present confusion wrt usage of echo area
and minibuffer window when asking questions.  Hence I'm all for such a
change.

martin



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-27 21:57     ` Juri Linkov
  2019-10-27 22:29       ` Juri Linkov
  2019-10-28  2:13       ` Stefan Monnier
@ 2019-10-28 10:45       ` Lars Ingebrigtsen
  2019-10-28 22:19         ` Juri Linkov
  2 siblings, 1 reply; 26+ messages in thread
From: Lars Ingebrigtsen @ 2019-10-28 10:45 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> Yup.  There are several bug reports on this (general) issue of
>> asynchronous messages hiding queries.  One suggestion has been to extend
>> the echo area in these instances, showing the message (perhaps) below
>> the prompt.  Another is to have the prompt re-assert itself after a
>> timeout, making the message disappear.
>
> These bug reports are fixed now when messages are displayed in the
> minibuffer, but the problem still exists for the y-or-n-p prompt.

So read-from-minibuffer reasserts the query if an asynchronous message
hides it?  Then that sounds like a good idea.  On the other hand,
y-or-n-p could just call read-char/event with a timeout and do the same
thing. 

> Why y-or-n-p doesn't use normal minibuffer functions?
>
> It can finely use read-from-minibuffer with a keymap where
> 'y' and 'n' keys are bound to minibuffer-exiting commands.
>
> Then history commands will be available for free, so you don't need
> to re-implement minibuffer history commands with `read-char-with-history'.

I think having a history for y-or-n-p doesn't sound very useful?
Hitting `M-p' doesn't to get to the previous answer just sounds
confusing to me.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-28 10:45       ` Lars Ingebrigtsen
@ 2019-10-28 22:19         ` Juri Linkov
  2019-10-28 23:20           ` Stefan Kangas
  2019-10-29 11:38           ` Lars Ingebrigtsen
  0 siblings, 2 replies; 26+ messages in thread
From: Juri Linkov @ 2019-10-28 22:19 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

>> These bug reports are fixed now when messages are displayed in the
>> minibuffer, but the problem still exists for the y-or-n-p prompt.
>
> So read-from-minibuffer reasserts the query if an asynchronous message
> hides it?

An asynchronous message doesn't hide the minibuffer.  It's displayed
at the end of the minibuffer text.

>> Why y-or-n-p doesn't use normal minibuffer functions?
>>
>> It can finely use read-from-minibuffer with a keymap where
>> 'y' and 'n' keys are bound to minibuffer-exiting commands.
>>
>> Then history commands will be available for free, so you don't need
>> to re-implement minibuffer history commands with `read-char-with-history'.
>
> I think having a history for y-or-n-p doesn't sound very useful?
> Hitting `M-p' doesn't to get to the previous answer just sounds
> confusing to me.

Please try the example I sent earlier.  It feels quite naturally
typing 'M-p RET' to repeat a previous y/n answer.



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-28 22:19         ` Juri Linkov
@ 2019-10-28 23:20           ` Stefan Kangas
  2019-10-29 23:39             ` Juri Linkov
  2019-10-29 11:38           ` Lars Ingebrigtsen
  1 sibling, 1 reply; 26+ messages in thread
From: Stefan Kangas @ 2019-10-28 23:20 UTC (permalink / raw)
  To: Juri Linkov
  Cc: Alan Mackenzie, Lars Ingebrigtsen, Stefan Monnier,
	Emacs developers

Juri Linkov <juri@linkov.net> writes:

> > I think having a history for y-or-n-p doesn't sound very useful?
> > Hitting `M-p' doesn't to get to the previous answer just sounds
> > confusing to me.
>
> Please try the example I sent earlier.  It feels quite naturally
> typing 'M-p RET' to repeat a previous y/n answer.

I think this is a misfeature.  Imagine a user answering "Do you want
to save important file X before closing?" and is used to always having
"y" in her history.  This time, "n" was the first item in history
because of some previous but now forgotten invocation the night
before.  But the user hits "M-p RET" by habit, expecting that to mean
"y", and ends up discarding important work.

Using this puts a cognitive load on the user if she doesn't want to
make mistakes.

Best regards,
Stefan Kangas



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-28 22:19         ` Juri Linkov
  2019-10-28 23:20           ` Stefan Kangas
@ 2019-10-29 11:38           ` Lars Ingebrigtsen
  2019-10-29 23:45             ` Juri Linkov
  1 sibling, 1 reply; 26+ messages in thread
From: Lars Ingebrigtsen @ 2019-10-29 11:38 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> So read-from-minibuffer reasserts the query if an asynchronous message
>> hides it?
>
> An asynchronous message doesn't hide the minibuffer.  It's displayed
> at the end of the minibuffer text.

The original bug report was about:

(progn
  (run-at-time 2 nil (lambda () (message "foo")))
  (y-or-n-p "Yes? "))

But this seems to have the same problem?  

(progn
  (run-at-time 2 nil (lambda () (message "foo")))
  (read-from-minibuffer "Yes? "))

When I eval that, "foo" completely hides the prompt -- it's not appended
or reasserted.

>> I think having a history for y-or-n-p doesn't sound very useful?
>> Hitting `M-p' doesn't to get to the previous answer just sounds
>> confusing to me.
>
> Please try the example I sent earlier.  It feels quite naturally
> typing 'M-p RET' to repeat a previous y/n answer.

I played with it a bit, and I'm not very enthusiastic about that.  Like
Stefan K says, it seems error-prone, and I think it would just create
frustration.  And it's a bigger change in the interface than it first
sounds like -- people may be used to hitting <up> as a way to reassert
the prompt, for instance.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-28 23:20           ` Stefan Kangas
@ 2019-10-29 23:39             ` Juri Linkov
  0 siblings, 0 replies; 26+ messages in thread
From: Juri Linkov @ 2019-10-29 23:39 UTC (permalink / raw)
  To: Stefan Kangas
  Cc: Alan Mackenzie, Lars Ingebrigtsen, Stefan Monnier,
	Emacs developers

>> Please try the example I sent earlier.  It feels quite naturally
>> typing 'M-p RET' to repeat a previous y/n answer.
>
> I think this is a misfeature.  Imagine a user answering "Do you want
> to save important file X before closing?" and is used to always having
> "y" in her history.  This time, "n" was the first item in history
> because of some previous but now forgotten invocation the night
> before.  But the user hits "M-p RET" by habit, expecting that to mean
> "y", and ends up discarding important work.
>
> Using this puts a cognitive load on the user if she doesn't want to
> make mistakes.

There are many things in Emacs that might go wrong when used carelessly.

Sometimes I run such dangerous shell commands:

  M-! rm * RET

then later I forget about it in the shell-command history and type:

  M-! M-p RET

The only solution is to be more careful.

With more freedom comes more responsibility.



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-29 11:38           ` Lars Ingebrigtsen
@ 2019-10-29 23:45             ` Juri Linkov
  2019-10-29 23:58               ` Lars Ingebrigtsen
  0 siblings, 1 reply; 26+ messages in thread
From: Juri Linkov @ 2019-10-29 23:45 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

>> An asynchronous message doesn't hide the minibuffer.  It's displayed
>> at the end of the minibuffer text.
>
> The original bug report was about:
>
> (progn
>   (run-at-time 2 nil (lambda () (message "foo")))
>   (y-or-n-p "Yes? "))
>
> But this seems to have the same problem?
>
> (progn
>   (run-at-time 2 nil (lambda () (message "foo")))
>   (read-from-minibuffer "Yes? "))
>
> When I eval that, "foo" completely hides the prompt -- it's not appended
> or reasserted.

'message' can be easily replaced with 'minibuffer-message',
then it doesn't obscure the minibuffer:

(progn
  (run-at-time 2 nil (lambda () (minibuffer-message "foo")))
  (read-from-minibuffer "Yes? "))

Whereas it obscures the prompt in y-or-n-p:

(progn
  (run-at-time 2 nil (lambda () (minibuffer-message "foo")))
  (y-or-n-p "Yes? "))

>>> I think having a history for y-or-n-p doesn't sound very useful?
>>> Hitting `M-p' doesn't to get to the previous answer just sounds
>>> confusing to me.
>>
>> Please try the example I sent earlier.  It feels quite naturally
>> typing 'M-p RET' to repeat a previous y/n answer.
>
> I played with it a bit, and I'm not very enthusiastic about that.  Like
> Stefan K says, it seems error-prone, and I think it would just create
> frustration.  And it's a bigger change in the interface than it first
> sounds like -- people may be used to hitting <up> as a way to reassert
> the prompt, for instance.

In case of doubt, we could add a new function read-y-or-n-p,
not to replace the implementation of the existing y-or-n-p.

Then like many users already put in init files:

  (fset 'yes-or-no-p 'y-or-n-p)

it will be possible to put:

  (fset 'yes-or-no-p 'read-y-or-n-p)
  (fset 'y-or-n-p 'read-y-or-n-p)

And when it will prove to work better than the current y-or-n-p,
maybe after the next release the implementation of y-or-n-p
could be switched to read-y-or-n-p later.



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-29 23:45             ` Juri Linkov
@ 2019-10-29 23:58               ` Lars Ingebrigtsen
  2019-10-30  8:22                 ` martin rudalics
  2019-10-30 22:10                 ` Juri Linkov
  0 siblings, 2 replies; 26+ messages in thread
From: Lars Ingebrigtsen @ 2019-10-29 23:58 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

Juri Linkov <juri@linkov.net> writes:

> 'message' can be easily replaced with 'minibuffer-message',
> then it doesn't obscure the minibuffer:
>
> (progn
>   (run-at-time 2 nil (lambda () (minibuffer-message "foo")))
>   (read-from-minibuffer "Yes? "))

Oh, nice.  I guess whenever we're displaying things from an async
context, we should be using minibuffer-message?

> Whereas it obscures the prompt in y-or-n-p:
>
> (progn
>   (run-at-time 2 nil (lambda () (minibuffer-message "foo")))
>   (y-or-n-p "Yes? "))

Then I'm all for your suggestion to rewrite y-or-n-p with
read-from-minibuffer...  but without a history.  (And yes-or-no-p too,
somehow, I guess.)  (Or fix both of those functions to work the same
with respect to minibuffer-message.)

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-29 23:58               ` Lars Ingebrigtsen
@ 2019-10-30  8:22                 ` martin rudalics
  2019-10-30 22:10                 ` Juri Linkov
  1 sibling, 0 replies; 26+ messages in thread
From: martin rudalics @ 2019-10-30  8:22 UTC (permalink / raw)
  To: Lars Ingebrigtsen, Juri Linkov
  Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

 > Then I'm all for your suggestion to rewrite y-or-n-p with
 > read-from-minibuffer...  but without a history.  (And yes-or-no-p too,
 > somehow, I guess.)

Strongly supported for Emacs 27.

martin



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-29 23:58               ` Lars Ingebrigtsen
  2019-10-30  8:22                 ` martin rudalics
@ 2019-10-30 22:10                 ` Juri Linkov
  2019-10-31  2:00                   ` Stefan Monnier
  1 sibling, 1 reply; 26+ messages in thread
From: Juri Linkov @ 2019-10-30 22:10 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Alan Mackenzie, Stefan Monnier, emacs-devel

>> 'message' can be easily replaced with 'minibuffer-message',
>> then it doesn't obscure the minibuffer:
>>
>> (progn
>>   (run-at-time 2 nil (lambda () (minibuffer-message "foo")))
>>   (read-from-minibuffer "Yes? "))
>
> Oh, nice.  I guess whenever we're displaying things from an async
> context, we should be using minibuffer-message?

'minibuffer-message' just calls the 'message' when used outside of
the minibuffer.  Maybe the dependency should be reversed so 'message'
could call 'minibuffer-message' when used in the minibuffer.

>> Whereas it obscures the prompt in y-or-n-p:
>>
>> (progn
>>   (run-at-time 2 nil (lambda () (minibuffer-message "foo")))
>>   (y-or-n-p "Yes? "))
>
> Then I'm all for your suggestion to rewrite y-or-n-p with
> read-from-minibuffer...  but without a history.  (And yes-or-no-p too,
> somehow, I guess.)  (Or fix both of those functions to work the same
> with respect to minibuffer-message.)

Without a history is fine.  There is another problem with
backward-compatibility: currently 'y-or-n-p' uses 'query-replace-map'
to translate input characters to y/n answers via act/skip symbols.

Users customize 'query-replace-map' to map more characters to act/skip
symbols, so we need to use 'query-replace-map', but not directly like

  (let ((prompt "Confirm? "))
    (read-from-minibuffer prompt nil query-replace-map))

that obviously fails with

  call-interactively: (wrong-type-argument commandp act)

query-replace-map needs to be translated to another keymap
where the same characters from 'query-replace-map'
run real commands, not intermediate symbols.



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-30 22:10                 ` Juri Linkov
@ 2019-10-31  2:00                   ` Stefan Monnier
  2019-11-03 20:50                     ` Juri Linkov
  2019-11-05 22:54                     ` bug#38076: Using minibuffer for y-or-n-p Juri Linkov
  0 siblings, 2 replies; 26+ messages in thread
From: Stefan Monnier @ 2019-10-31  2:00 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Alan Mackenzie, Lars Ingebrigtsen, emacs-devel

> 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) ..


-- Stefan




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation
  2019-10-31  2:00                   ` Stefan Monnier
@ 2019-11-03 20:50                     ` Juri Linkov
  2019-11-05 22:54                     ` bug#38076: Using minibuffer for y-or-n-p Juri Linkov
  1 sibling, 0 replies; 26+ messages in thread
From: Juri Linkov @ 2019-11-03 20:50 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Alan Mackenzie, Lars Ingebrigtsen, emacs-devel

>> 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) ..

Thanks, I tried this, and everything works fine.
I'll post the patch after more testing.



^ permalink raw reply	[flat|nested] 26+ messages in thread

* bug#38076: Using minibuffer for y-or-n-p
  2019-10-31  2:00                   ` Stefan Monnier
  2019-11-03 20:50                     ` Juri Linkov
@ 2019-11-05 22:54                     ` Juri Linkov
  2019-11-06 22:25                       ` Juri Linkov
  1 sibling, 1 reply; 26+ messages in thread
From: Juri Linkov @ 2019-11-05 22:54 UTC (permalink / raw)
  To: 38076; +Cc: stefan monnier

[-- Attachment #1: Type: text/plain, Size: 1191 bytes --]

X-Debbugs-CC: Stefan Monnier <monnier@iro.umontreal.ca>
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:


[-- Attachment #2: y-or-n-p-minibuffer.patch --]
[-- Type: text/x-diff, Size: 8881 bytes --]

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-bottom)
+  (define-key map [remap scroll-up-command] 'minibuffer-scroll-up-command)
+  (define-key map [remap scroll-down-command] 'minibuffer-scroll-down-command)
+  (define-key map [remap scroll-other-window] 'minibuffer-scroll-other-window)
+  (define-key map [remap scroll-other-window-down] 'minibuffer-scroll-other-window-down)
+
   (define-key map "\r" 'exit-minibuffer)
   (define-key map "\n" 'exit-minibuffer))
 
@@ -3670,6 +3677,46 @@ minibuffer-beginning-of-buffer
   (when (and arg (not (consp arg)))
     (forward-line 1)))
 
+(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)
 
 ;;; 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 header))
 
+(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.
 
 Under a windowing system a dialog box will be used if `last-nonmenu-event'
 is nil and `use-dialog-box' is non-nil."
-  ;; ¡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)))
@@ -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-actions)
-                                              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_window_for_scrolling, Sother_window_
     {
       /* Nothing specified; look for a neighboring window on the same
 	 frame.  */
-      window = Fnext_window (selected_window, Qnil, Qnil);
+      window = Fnext_window (selected_window, Qlambda, Qnil);
 
       if (EQ (window, selected_window))
 	/* That didn't get us anywhere; look for a window on another
            visible frame on the current terminal.  */
-        window = Fnext_window (window, Qnil, Qvisible);
+        window = Fnext_window (window, Qlambda, Qvisible);
     }
 
   CHECK_LIVE_WINDOW (window);

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* bug#38076: Using minibuffer for y-or-n-p
  2019-11-05 22:54                     ` bug#38076: Using minibuffer for y-or-n-p Juri Linkov
@ 2019-11-06 22:25                       ` Juri Linkov
  0 siblings, 0 replies; 26+ messages in thread
From: Juri Linkov @ 2019-11-06 22:25 UTC (permalink / raw)
  To: 38076; +Cc: Stefan Monnier

[-- Attachment #1: Type: text/plain, Size: 626 bytes --]

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.


[-- Attachment #2: y-or-n-p-minibuffer-read-char-from-minibuffer.patch --]
[-- Type: text/x-diff, Size: 16735 bytes --]

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 (= 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-bottom)
+  (define-key map [remap scroll-up-command] 'minibuffer-scroll-up-command)
+  (define-key map [remap scroll-down-command] 'minibuffer-scroll-down-command)
+  (define-key map [remap scroll-other-window] 'minibuffer-scroll-other-window)
+  (define-key map [remap scroll-other-window-down] 'minibuffer-scroll-other-window-down)
+
   (define-key map "\r" 'exit-minibuffer)
   (define-key map "\n" 'exit-minibuffer))
 
@@ -3670,6 +3682,46 @@ minibuffer-beginning-of-buffer
   (when (and arg (not (consp arg)))
     (forward-line 1)))
 
+(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)
 
 ;;; 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))))
 
-(defvar read-char-from-minibuffer-history nil
+(defvar read-char-history nil
   "The default history for the `read-char-from-minibuffer' function.")
 
 (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.")
 
-(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 minibuffer."
   (interactive)
   (delete-minibuffer-contents)
-  (insert (event-basic-type last-command-event))
+  (insert last-command-event)
   (exit-minibuffer))
 
-(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 one.
-        (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 minibuffer."
+  (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-minibuffer-map)
+                                 (dolist (char chars)
+                                   (define-key map (vector char)
+                                     'read-char-from-minibuffer-insert-char))
+                                 (define-key map [remap self-insert-command]
+                                   'read-char-from-minibuffer-insert-other)
+                                 ;; (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 first 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))
 
 (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 header))
 
+(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.
 
 Under a windowing system a dialog box will be used if `last-nonmenu-event'
 is nil and `use-dialog-box' is non-nil."
-  ;; ¡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)))
@@ -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-actions)
-                                              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_window_for_scrolling, Sother_window_
     {
       /* Nothing specified; look for a neighboring window on the same
 	 frame.  */
-      window = Fnext_window (selected_window, Qnil, Qnil);
+      window = Fnext_window (selected_window, Qlambda, Qnil);
 
       if (EQ (window, selected_window))
 	/* That didn't get us anywhere; look for a window on another
            visible frame on the current terminal.  */
-        window = Fnext_window (window, Qnil, Qvisible);
+        window = Fnext_window (window, Qlambda, Qvisible);
     }
 
   CHECK_LIVE_WINDOW (window);

^ permalink raw reply related	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2019-11-06 22:25 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-10-26 10:14 Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation Alan Mackenzie
2019-10-26 12:41 ` Stefan Monnier
2019-10-26 13:16   ` Lars Ingebrigtsen
2019-10-26 16:38     ` Stefan Monnier
2019-10-27  1:01       ` HaiJun Zhang
2019-10-27 21:57     ` Juri Linkov
2019-10-27 22:29       ` Juri Linkov
2019-10-28  9:41         ` martin rudalics
2019-10-28  2:13       ` Stefan Monnier
2019-10-28 10:45       ` Lars Ingebrigtsen
2019-10-28 22:19         ` Juri Linkov
2019-10-28 23:20           ` Stefan Kangas
2019-10-29 23:39             ` Juri Linkov
2019-10-29 11:38           ` Lars Ingebrigtsen
2019-10-29 23:45             ` Juri Linkov
2019-10-29 23:58               ` Lars Ingebrigtsen
2019-10-30  8:22                 ` martin rudalics
2019-10-30 22:10                 ` Juri Linkov
2019-10-31  2:00                   ` Stefan Monnier
2019-11-03 20:50                     ` Juri Linkov
2019-11-05 22:54                     ` bug#38076: Using minibuffer for y-or-n-p Juri Linkov
2019-11-06 22:25                       ` Juri Linkov
2019-10-26 14:18   ` Strange use of (run-with-timer 0 nil #'foo args) in do-after-load-evaluation Alan Mackenzie
2019-10-26 15:27   ` Juanma Barranquero
2019-10-27 21:51 ` Juri Linkov
2019-10-28  3:34   ` Eli Zaretskii

Code repositories for project(s) associated with this external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.