Subject: bug#77224: [PATCH] New minor mode 'cursor-indicators-mode'

From: Daniel Colascione <[email-masked]>
Date: Fri, 28 Mar 2025 10:28:21 -0400

Eli Zaretskii <[email-masked]> writes:

>> Date: Thu, 27 Mar 2025 16:41:39 -0400
>> From: Daniel Colascione <[email-masked]>
>> CC: [email-masked], [email-masked], [email-masked]
>> 
>> >> Regarding Emacs sluggishness, the `cursor-indicators-mode' is an
>> >> optional mode. If it increases sluggishness unnacceptably (which it
>> >> won't), users can turn it off.
>> >
>> >Experience shows that many users turn on a lot of features which rely
>> >on post-command-hook, and then complain that Emacs is slow to respond.
>> >Telling them to turn off features they like doesn't really work.
>> 
>> Nor does telling external package authors not to use post-command-hook.
>
> Well, I could at least try ;-)  Who knows, some of them might even
> listen...
>
>> I think there are two worthwhile approaches: 1) time the entries in
>> post-command-hook and alert users about slow ones, and 2) provide a
>> "do something when X, Y, or Z changes" API that authors could use
>> when they would otherwise put something on PCH and check X, Y, and Z
>> internally. Internally, we could DTRT and omit running those hooks
>> when the relevant conditions haven't changed.
>
> I'd be okay with developing 1), I think it's a good feature, given the
> proliferation of features that use this hook.

Yeah. SHTDI though, and I'm not going to be able to get to it soon.

> As for 2), I think we already have that: add-variable-watcher.  We
> already use this for a small number of features.  Does that fit the
> bill?

No, because variable watchers are synchronous --- also, they're hacky:
they let you run arbitrary code at nearly arbitrary points.
Like advice, they're a last resort and a debugging aid, and ISTM if
we're using them to implement features, something is wrong.  I think
we're conditioned to understand that named functions can be advised, but
most people aren't thinking of arbitrary variable assignments as
extension points.

The feature I'm proposing is higher-level than that.  For example, for
my personal use, I have something that sits in pre-redisplay-function
and scans all the windows for applying highlights based on current
buffer and window selection.  I should have written this thing using the
appropriate window configuration change hooks, but didn't, because I'm
lazy and my approach is fast enough for me.

    ;; Don't do this.

    (defun window-highlight--pre-redisplay (_window)
      (window-highlight--rescan-windows))

    (add-function :after pre-redisplay-function
       #'window-highlight--pre-redisplay))

What I'd have wanted instead is something like this:

  (run-when-changed
      (list (selected-window) (selected-frame) (frame-focus-state))
    #'window-highlight--rescan-windows)

Here, we'd run window-highlight--rescan-windows only when one of its
dependent conditions (e.g. selected frame) changed.  After any number of
window and frame changes, we'd run window-highlight--rescan-windows
*once*, immediately before its effects would become user-visible
(e.g. just before redisplay, or after commands), and we wouldn't re-run
it until one of its preconditions changed.

It's certainly possible to do the same coalescing and change detection
manually, but it's inconvenient.  It's easier to just throw something in
pre-redisplay or post-command-hook, so that's what people do.
Level-triggered control is easier to reason about than
edge-triggered control.