Subject: Re: bug#36312: 27.0.50; (message) in display spec condition causes emacs_abort()

From: Eli Zaretskii <[email-masked]>
Date: Fri, 21 Jun 2019 11:39:31 +0300

> Date: Fri, 21 Jun 2019 10:34:26 +0300
> From: Eli Zaretskii <[email-masked]>
> Cc: [email-masked]
> 
> Why not do something like that in a post-command-hook instead?  The
> position of point is already up to date then, and retrieving face
> properties at point is trivial.  What am I missing?
> 
> There's also pre-redisplay-hook.
> 
> > So far, what I'm thinking about is a hook that's run _after_ redisplay
> > to let an overlay know that redisplay just happened and the face used
> > for the text around the overlay changed.
> 
> I don't understand why you think you must run after redisplay.  If
> redisplay changes the faces (probably via JIT font-lock?), and you
> must have the corresponding change in your image without any delays
> (though if you use a timer, you seem to not mind a delay), then why
> not register a function with jit-lock-register, and do that from
> there?  And if you indeed don't mind a short delay, then I think
> post-command-hook should be your friend.
> 
> > But I'd appreciate any suggestions (the immediate application is to
> > define character-like image-based glyphs that "look like text", but
> > there might be others).
> 
> You seem to be thinking about font-lock like use cases, so plugging
> yourself into jit-lock would be my first suggestion.
> 
> In any case, (ab)using 'when' in display specs for running code that
> doesn't affect the value of that same display spec is to be avoided,
> IMO, as that is not what these features is for.

FTR, I wanted to draw your attention to some problematic aspects of
using 'when' in display specs as a kind of "redisplay hook".

First aspect that you need to be aware of is that when the display
spec is being evaluated, there's no 100% guarantee that the value of
point is what will eventually be displayed.  In some, admittedly rare,
situations, when redisplay is finished, it turns out that point is not
in a fully visible screen line.  In such cases, the display engine can
decide to move point to bring it back into the viewport.  It then
performs another redisplay cycle, but that is not guaranteed to
re-evaluate your display spec, because Emacs might find a redisplay
optimization which avoids that (typically, moving point only needs to
consider point's line).

More generally, if a window is redrawn, the display engine might
decide not to redisplay the line where you have your display spec, but
instead either scroll the display (i.e. reuse the results of the
previous redisplay cycle), or even bypass that line altogether,
because it decides that this line is outside of the region affected by
the last command.

Another problematic aspect is that the display specs are evaluated for
purposes other than displaying the results.  There are features in
Emacs that need to perform text layout calculations without displaying
the text whose layout they consider.  One popular example is C-n/C-p,
which calls vertical-motion internally.  vertical-motion then invokes
the display engine in a special mode which performs text layout
calculations and measures the text dimensions without displaying that
text, just because it needs to figure out, for example, what character
or buffer position is on display directly below or above the given
screen coordinates.  The result of this is that your 'when' form will
be called in contexts other than redisplay, and if you fire timers or
other functions inside that form, you will find that your code is
invoked more than once per command, in different contexts you didn't
necessarily expect, and with point whose value may not be up to date.
To continue the same example, vertical-motion invokes the display
routine before it moves point, precisely _because_ it needs to perform
layout calculations to decide where to move point.