Fix handling of window-specific overlays in buffer iteration for display

* src/xdisp.c (compute_display_string_pos): Pay attention to
'display' properties that come from overlays, in case the overlays
are window-specific.  Call 'handle_display_prop' with the argument
OVERLAY non-nil, if the property is from an overlay.
(compute_display_string_end): Accept an additional argument, a
pointer to the window; all callers changed.  Pay attention to
'display' properties that come from overlays, in case the overlays
are window-specific.  In particular, if the property comes from an
overlay, use that overlay's end position as the next place where
the 'display' property changes, effectively disregarding any
properties on buffer text that is replaced by this overlay's
'display' property.
(handle_single_display_spec): Always use the end position of the
overlay which determined the 'display' property as the end of the
property, to make sure we obey windows-specific overlays.
* src/bidi.c (bidi_fetch_char): Adapt to the above change.
* src/dispextern.h: Adjust prototype of 'compute_display_string_end'.
(Bug#80255)
This commit is contained in:
Eli Zaretskii 2026-02-14 13:36:38 +02:00
parent ed9d341026
commit 69694381a4
3 changed files with 44 additions and 24 deletions

View file

@ -1335,7 +1335,7 @@ bidi_fetch_char (ptrdiff_t charpos, ptrdiff_t bytepos, ptrdiff_t *disp_pos,
Replacement Character. */
ch = OBJECT_REPLACEMENT_CHARACTER;
}
disp_end_pos = compute_display_string_end (*disp_pos, string);
disp_end_pos = compute_display_string_end (*disp_pos, string, w);
if (disp_end_pos < 0)
{
/* Somebody removed the display string from the buffer

View file

@ -3581,7 +3581,8 @@ extern ptrdiff_t compute_display_string_pos (struct text_pos *,
struct bidi_string_data *,
struct window *, bool, int *);
extern ptrdiff_t compute_display_string_end (ptrdiff_t,
struct bidi_string_data *);
struct bidi_string_data *,
struct window *);
extern void produce_stretch_glyph (struct it *);
extern int merge_glyphless_glyph_face (struct it *);
extern void forget_escape_and_glyphless_faces (void);

View file

@ -4497,6 +4497,7 @@ compute_display_string_pos (struct text_pos *position,
(charpos < eob - MAX_DISP_SCAN) ? charpos + MAX_DISP_SCAN : eob;
struct text_pos tpos;
int rv = 0;
Lisp_Object overlay = Qnil, ov1 = Qnil;
if (string && STRINGP (string->lstring))
object1 = object = string->lstring;
@ -4529,13 +4530,15 @@ compute_display_string_pos (struct text_pos *position,
else
bufpos = charpos;
tpos = *position;
if (!NILP (spec = Fget_char_property (pos, Qdisplay, object))
if (!NILP (spec = get_char_property_and_overlay (pos, Qdisplay, object,
&overlay))
&& (charpos <= begb
|| !EQ (Fget_char_property (make_fixnum (charpos - 1), Qdisplay,
object),
spec))
&& (rv = handle_display_spec (NULL, spec, object1, Qnil, &tpos, bufpos,
frame_window_p)))
|| !EQ (get_char_property_and_overlay (make_fixnum (charpos - 1),
Qdisplay, object, &ov1),
spec)
|| !BASE_EQ (overlay, ov1))
&& (rv = handle_display_spec (NULL, spec, object1, overlay, &tpos,
bufpos, frame_window_p)))
{
if (rv == 2)
*disp_prop = 2;
@ -4557,11 +4560,11 @@ compute_display_string_pos (struct text_pos *position,
BYTEPOS (tpos) = string_char_to_byte (object, CHARPOS (tpos));
else
BYTEPOS (tpos) = CHAR_TO_BYTE (CHARPOS (tpos));
spec = Fget_char_property (pos, Qdisplay, object);
spec = get_char_property_and_overlay (pos, Qdisplay, object, &overlay);
if (!STRINGP (object))
bufpos = CHARPOS (tpos);
} while (NILP (spec)
|| !(rv = handle_display_spec (NULL, spec, object1, Qnil, &tpos,
|| !(rv = handle_display_spec (NULL, spec, object1, overlay, &tpos,
bufpos, frame_window_p)));
if (rv == 2)
*disp_prop = 2;
@ -4575,11 +4578,19 @@ compute_display_string_pos (struct text_pos *position,
property whose value is a string or a `display' text property whose
value is a string. */
ptrdiff_t
compute_display_string_end (ptrdiff_t charpos, struct bidi_string_data *string)
compute_display_string_end (ptrdiff_t charpos, struct bidi_string_data *string,
struct window *w)
{
Lisp_Object window = Qnil;
if (w)
XSETWINDOW (window, w);
/* OBJECT = nil means current buffer. */
Lisp_Object object =
(string && STRINGP (string->lstring)) ? string->lstring : Qnil;
Lisp_Object object = (string && STRINGP (string->lstring))
? string->lstring
: WINDOWP (window)
? window
: Qnil;
Lisp_Object pos = make_fixnum (charpos);
ptrdiff_t eob =
(STRINGP (object) || (string && string->s)) ? string->schars : ZV;
@ -4601,11 +4612,18 @@ compute_display_string_end (ptrdiff_t charpos, struct bidi_string_data *string)
it->stop_charpos. But neither compute_display_string_pos nor
bidi_fetch_char that calls it know or care where the next
stop_charpos is. */
if (NILP (Fget_char_property (pos, Qdisplay, object)))
Lisp_Object overlay = Qnil;
if (NILP (get_char_property_and_overlay (pos, Qdisplay, object, &overlay)))
return -1;
/* Look forward for the first character where the `display' property
changes. */
if (OVERLAYP (overlay))
return OVERLAY_END (overlay);
/* next-char-property-change doesn't support window OBJECTs. */
if (WINDOWP (object))
object = w->contents;
pos = Fnext_single_char_property_change (pos, Qdisplay, object, Qnil);
return XFIXNAT (pos);
@ -6286,14 +6304,14 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
if (it)
{
start_pos = *position;
*position = display_prop_end (it, object, start_pos);
/* If the display property comes from an overlay, don't consider
any potential stop_charpos values before the end of that
overlay. Since display_prop_end will happily find another
'display' property coming from some other overlay or text
property on buffer positions before this overlay's end, we
need to ignore them, or else we risk displaying this
overlay's display string/image twice. */
/* If the display property comes from an overlay, always use the
end of that overlay as the end of the property. Since
display_prop_end will happily find another 'display' property
coming from some other overlay or text property on buffer
positions before this overlay's end, or overlays not specific
to this window, we need to ignore them, or else we risk
displaying this overlay's display string/image twice, or fail
to display text that should be. */
if (!NILP (overlay))
{
ptrdiff_t ovendpos = OVERLAY_END (overlay);
@ -6306,9 +6324,10 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
set below. Prevent that. */
ovendpos = clip_to_bounds (BEGV, ovendpos, ZV);
if (ovendpos > CHARPOS (*position))
SET_TEXT_POS (*position, ovendpos, CHAR_TO_BYTE (ovendpos));
}
else
*position = display_prop_end (it, object, start_pos);
}
value = Qnil;