Subject: bug#56682: Fix the long lines font locking related slowdowns

From: Eli Zaretskii <[email-masked]>
Date: Mon, 01 Aug 2022 15:38:43 +0300

> Date: Sun, 31 Jul 2022 22:54:00 +0000
> From: Gregory Heytings <[email-masked]>
> cc: [email-masked], [email-masked], [email-masked], 
>     [email-masked]
> 
> > Whether always to correct narrowed_begv and narrowed_zv if we are 
> > reseating to a position outside the narrowing, is a more complicated 
> > question.  The basic problem here is that we don't have an easy way of 
> > restoring the previous narrowing (except by unwind_protect), and the 
> > display code sometimes calls init_iterator or start_display using the 
> > iterator that already has these members set by previous code, a 
> > situation which we currently cannot easily detect.  However, when this 
> > code runs as part of redisplay, we generally don't expect the original 
> > narrowing to be insufficient, except perhaps in the truncate-line case.
> >
> > So I think we should correct narrowed_begv and narrowed_zv only if 
> > either the 'redisplaying_p' flag is reset (meaning the display code is 
> > being invoked outside of redisplay) or it->line_wrap == TRUNCATE.
> >
> 
> I admit I do not really understand your last two paragraphs, but I tried 
> to do what you suggested, and it doesn't seem to introduce regressions, so 
> I pushed it to the new feature branch.

Thanks.  I will try to explain some more; feel free to ask questions
if something's still unclear.

You can see in the sources that both init_iterator and start_display
are many times called with 'struct it' that was used by the caller, so
it could already have the narrowed_begv and narrowed_zv members
initialized by the caller.  If we discover that we want to recalculate
these values, we'd then need to restore the previous value before we
return, so that the caller will see the same values it used before the
call.  But we have no easy way of doing that, and moreover, cannot
even detect that these members were initialized.  The inability to
detect that they were initialized is due to the fact that we don't
initialize 'struct it' before we call init_iterator, and so these
fields can originally have any arbitrary value.  Which means, for
example, that tests like this:

  if (current_buffer->long_line_optimizations_p)
    {
      if (!it->narrowed_begv  <<<<<<<<<<<<<<<<<<<<<<<<<<
         || ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv)
             && (!redisplaying_p || it->line_wrap == TRUNCATE)))

are not necessarily reliable, because we never initialize
narrowed_begv to zero, AFAICT.  Right?

The other part of my reasoning is that callers of the display code
which are outside redisplay could legitimately move the iterator far
from point: think about pos-visible-in-window-p and its ilk.  So, when
we are not called by redisplay, I think it would be preferable to
update the narrowing due to these considerations.