Subject: bug#69733: [PATCH] Flyspell (flyspell-word): do not force 'save-excursion' on timers

From: Daniel Pettersson <[email-masked]>
Date: Fri, 15 Mar 2024 12:29:46 +0100

Eli Zaretskii <[email-masked]> writes:

> Thanks, but I'm still confused regarding what you are trying to fix
> and why you are trying to fix it with the patch you proposed.

Lets just preface with that for accept-process-output non idle timers
and process filters are the same thing, right?  So any filter or timer
might run inside of accept-process-output if JUST-THIS-ONE is nil.

The issue was noticed with the elpa package dape, with
flyspell-prog-mode.  But I was able to reproduce it in gdb-mi.el
(gud-next) as well.  The common denominator here is moving the point
from filter/timer functions, in both cases source buffers. 

It's definitely a bit more rare for this to happen for gdb-mi.el as it
moves it's point to the beginning of the line where it's highly unlikely
for there to be a word which flyspell-prog-mode sends of to
ispell-process to spell (and waits with accept-process-output),
additionally it depends on the speed of gdb and the ispell program. If
ispell stdouts before gdb, flyspell-word finishes before gud-filter is
evaled. 

> First, AFAIU, save-excursion is there because flyspell-get-word might
> move point.  So this is justified.

No doubt that the save-excursion is justified but it surly does not need
to wrap everything, one could be a bit more exact (wrap those parts)
that actually move the point.

> Next, you seem to be saying that it is for some reason bad to run
> timers under save-excursion, but you haven't explained why.

This has the chance to break both gdb-mi.el, dape and all other packages
that moves a point from filter and timer functions. I say chance here
because it all depends the timing of the process output and If the
buffer that the point is moved in is checked by flyspell think I gave my
explanation to what could break above. 

> Also, code that runs from a timer cannot
> possibly rely on Emacs leaving point where the timer moves it.

I have a feeling that there are a lot of other code, except dape and gud
that do.  As this is not only timers but any code that is called from an
process filter.

> Are there any examples of timers that run like this which _must_ be
> able to move point and make sure point stays where the timer moved it?

See above.

After some thinking I it might be impossible to impose anything on the
caller of accept-process-output.  And the bug is in dape and gdb-mi.el
that gud should call gud-display-line inside of an idle timer to ensure
that the point is moved, if I understand how idle timers are called
which might be false.

Maybe it would be a good idea if "(elisp) Timers" would mention these
things.  Would that be an good idea?  I could be up for writing something
up,  even if don't consider myself to be that good at writing
documentation.

/Daniel