Commit 55439c61 authored by Eli Zaretskii's avatar Eli Zaretskii

Fix bug #9218 with slow cursor motion and scrolling Org Mode buffers.

 src/dispextern.h (struct bidi_it): New member disp_prop_p.
 src/xdisp.c: Remove one-slot cache of display string positions.
 (compute_display_string_pos): Accept an additional argument
 DISP_PROP_P; callers changed. Scan at most 5K characters forward
 for a display string or property.  If found, set DISP_PROP_P
 non-zero.
 src/bidi.c (bidi_fetch_char): Accept an additional argument
 DISP_PROP_P, and pass it to compute_display_string_pos.  Only
 handle text covered by a display string if DISP_PROP_P is returned
 non-zero.  All callers of bidi_fetch_char changed.
parent 0e6a2bd7
2011-08-02 Eli Zaretskii <eliz@gnu.org>
Fix slow cursor motion and scrolling in large buffers with
selective display, like Org Mode buffers. (Bug#9218)
* dispextern.h (struct bidi_it): New member disp_prop_p.
* xdisp.c: Remove one-slot cache of display string positions.
(compute_display_string_pos): Accept an additional argument
DISP_PROP_P; callers changed. Scan at most 5K characters forward
for a display string or property. If found, set DISP_PROP_P
non-zero.
* bidi.c (bidi_fetch_char): Accept an additional argument
DISP_PROP_P, and pass it to compute_display_string_pos. Only
handle text covered by a display string if DISP_PROP_P is returned
non-zero. All callers of bidi_fetch_char changed.
2011-08-02 Stefan Monnier <monnier@iro.umontreal.ca> 2011-08-02 Stefan Monnier <monnier@iro.umontreal.ca>
* keymap.c (Fdefine_key): Fix Lisp_Object/int mixup; apply some CSE. * keymap.c (Fdefine_key): Fix Lisp_Object/int mixup; apply some CSE.
......
...@@ -792,6 +792,7 @@ bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, int frame_window_p, ...@@ -792,6 +792,7 @@ bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, int frame_window_p,
bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT; bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
bidi_it->sor = L2R; /* FIXME: should it be user-selectable? */ bidi_it->sor = L2R; /* FIXME: should it be user-selectable? */
bidi_it->disp_pos = -1; /* invalid/unknown */ bidi_it->disp_pos = -1; /* invalid/unknown */
bidi_it->disp_prop_p = 0;
/* We can only shrink the cache if we are at the bottom level of its /* We can only shrink the cache if we are at the bottom level of its
"stack". */ "stack". */
if (bidi_cache_start == 0) if (bidi_cache_start == 0)
...@@ -874,14 +875,16 @@ bidi_char_at_pos (EMACS_INT bytepos, const unsigned char *s, int unibyte) ...@@ -874,14 +875,16 @@ bidi_char_at_pos (EMACS_INT bytepos, const unsigned char *s, int unibyte)
covered characters as a single character u+FFFC, and return their covered characters as a single character u+FFFC, and return their
combined length in CH_LEN and NCHARS. DISP_POS specifies the combined length in CH_LEN and NCHARS. DISP_POS specifies the
character position of the next display string, or -1 if not yet character position of the next display string, or -1 if not yet
computed. When the next character is at or beyond that position, computed. DISP_PROP_P non-zero means that there's really a display
the function updates DISP_POS with the position of the next display string at DISP_POS, as opposed to when we searched till DISP_POS
string. STRING->s is the C string to iterate, or NULL if iterating without findingone. When the next character is at or beyond that
over a buffer or a Lisp string; in the latter case, STRING->lstring position, the function updates DISP_POS with the position of the
is the Lisp string. */ next display string. STRING->s is the C string to iterate, or NULL
if iterating over a buffer or a Lisp string; in the latter case,
STRING->lstring is the Lisp string. */
static inline int static inline int
bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos, bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
struct bidi_string_data *string, int *disp_prop_p, struct bidi_string_data *string,
int frame_window_p, EMACS_INT *ch_len, EMACS_INT *nchars) int frame_window_p, EMACS_INT *ch_len, EMACS_INT *nchars)
{ {
int ch; int ch;
...@@ -894,7 +897,8 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos, ...@@ -894,7 +897,8 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
if (charpos < endpos && charpos > *disp_pos) if (charpos < endpos && charpos > *disp_pos)
{ {
SET_TEXT_POS (pos, charpos, bytepos); SET_TEXT_POS (pos, charpos, bytepos);
*disp_pos = compute_display_string_pos (&pos, string, frame_window_p); *disp_pos = compute_display_string_pos (&pos, string, frame_window_p,
disp_prop_p);
} }
/* Fetch the character at BYTEPOS. */ /* Fetch the character at BYTEPOS. */
...@@ -904,8 +908,9 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos, ...@@ -904,8 +908,9 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
*ch_len = 1; *ch_len = 1;
*nchars = 1; *nchars = 1;
*disp_pos = endpos; *disp_pos = endpos;
*disp_prop_p = 0;
} }
else if (charpos >= *disp_pos) else if (charpos >= *disp_pos && *disp_prop_p)
{ {
EMACS_INT disp_end_pos; EMACS_INT disp_end_pos;
...@@ -972,10 +977,12 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos, ...@@ -972,10 +977,12 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
/* If we just entered a run of characters covered by a display /* If we just entered a run of characters covered by a display
string, compute the position of the next display string. */ string, compute the position of the next display string. */
if (charpos + *nchars <= endpos && charpos + *nchars > *disp_pos) if (charpos + *nchars <= endpos && charpos + *nchars > *disp_pos
&& *disp_prop_p)
{ {
SET_TEXT_POS (pos, charpos + *nchars, bytepos + *ch_len); SET_TEXT_POS (pos, charpos + *nchars, bytepos + *ch_len);
*disp_pos = compute_display_string_pos (&pos, string, frame_window_p); *disp_pos = compute_display_string_pos (&pos, string, frame_window_p,
disp_prop_p);
} }
return ch; return ch;
...@@ -1083,6 +1090,7 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p) ...@@ -1083,6 +1090,7 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
int ch; int ch;
EMACS_INT ch_len, nchars; EMACS_INT ch_len, nchars;
EMACS_INT pos, disp_pos = -1; EMACS_INT pos, disp_pos = -1;
int disp_prop_p = 0;
bidi_type_t type; bidi_type_t type;
const unsigned char *s; const unsigned char *s;
...@@ -1130,7 +1138,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p) ...@@ -1130,7 +1138,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
bytepos = pstartbyte; bytepos = pstartbyte;
if (!string_p) if (!string_p)
pos = BYTE_TO_CHAR (bytepos); pos = BYTE_TO_CHAR (bytepos);
ch = bidi_fetch_char (bytepos, pos, &disp_pos, &bidi_it->string, ch = bidi_fetch_char (bytepos, pos, &disp_pos, &disp_prop_p,
&bidi_it->string,
bidi_it->frame_window_p, &ch_len, &nchars); bidi_it->frame_window_p, &ch_len, &nchars);
type = bidi_get_type (ch, NEUTRAL_DIR); type = bidi_get_type (ch, NEUTRAL_DIR);
...@@ -1157,7 +1166,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p) ...@@ -1157,7 +1166,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
&& bidi_at_paragraph_end (pos, bytepos) >= -1) && bidi_at_paragraph_end (pos, bytepos) >= -1)
break; break;
/* Fetch next character and advance to get past it. */ /* Fetch next character and advance to get past it. */
ch = bidi_fetch_char (bytepos, pos, &disp_pos, &bidi_it->string, ch = bidi_fetch_char (bytepos, pos, &disp_pos,
&disp_prop_p, &bidi_it->string,
bidi_it->frame_window_p, &ch_len, &nchars); bidi_it->frame_window_p, &ch_len, &nchars);
pos += nchars; pos += nchars;
bytepos += ch_len; bytepos += ch_len;
...@@ -1290,6 +1300,7 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it) ...@@ -1290,6 +1300,7 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
bidi_it->ch_len = 1; bidi_it->ch_len = 1;
bidi_it->nchars = 1; bidi_it->nchars = 1;
bidi_it->disp_pos = (string_p ? bidi_it->string.schars : ZV); bidi_it->disp_pos = (string_p ? bidi_it->string.schars : ZV);
bidi_it->disp_prop_p = 0;
} }
else else
{ {
...@@ -1297,8 +1308,8 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it) ...@@ -1297,8 +1308,8 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
display string, treat the entire run of covered characters as display string, treat the entire run of covered characters as
a single character u+FFFC. */ a single character u+FFFC. */
curchar = bidi_fetch_char (bidi_it->bytepos, bidi_it->charpos, curchar = bidi_fetch_char (bidi_it->bytepos, bidi_it->charpos,
&bidi_it->disp_pos, &bidi_it->string, &bidi_it->disp_pos, &bidi_it->disp_prop_p,
bidi_it->frame_window_p, &bidi_it->string, bidi_it->frame_window_p,
&bidi_it->ch_len, &bidi_it->nchars); &bidi_it->ch_len, &bidi_it->nchars);
} }
bidi_it->ch = curchar; bidi_it->ch = curchar;
...@@ -2032,12 +2043,13 @@ bidi_level_of_next_char (struct bidi_it *bidi_it) ...@@ -2032,12 +2043,13 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
struct bidi_string_data bs = bidi_it->string; struct bidi_string_data bs = bidi_it->string;
bidi_type_t chtype; bidi_type_t chtype;
int fwp = bidi_it->frame_window_p; int fwp = bidi_it->frame_window_p;
int dpp = bidi_it->disp_prop_p;
if (bidi_it->nchars <= 0) if (bidi_it->nchars <= 0)
abort (); abort ();
do { do {
ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, &bs, fwp, ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, &dpp, &bs,
&clen, &nc); fwp, &clen, &nc);
if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */) if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */)
chtype = NEUTRAL_B; chtype = NEUTRAL_B;
else else
......
...@@ -1868,6 +1868,8 @@ struct bidi_it { ...@@ -1868,6 +1868,8 @@ struct bidi_it {
bidi_dir_t paragraph_dir; /* current paragraph direction */ bidi_dir_t paragraph_dir; /* current paragraph direction */
EMACS_INT separator_limit; /* where paragraph separator should end */ EMACS_INT separator_limit; /* where paragraph separator should end */
EMACS_INT disp_pos; /* position of display string after ch */ EMACS_INT disp_pos; /* position of display string after ch */
int disp_prop_p; /* if non-zero, there really is a
`display' property/string at disp_pos */
unsigned first_elt : 1; /* if non-zero, examine current char first */ unsigned first_elt : 1; /* if non-zero, examine current char first */
unsigned new_paragraph : 1; /* if non-zero, we expect a new paragraph */ unsigned new_paragraph : 1; /* if non-zero, we expect a new paragraph */
unsigned frame_window_p : 1; /* non-zero if displaying on a GUI frame */ unsigned frame_window_p : 1; /* non-zero if displaying on a GUI frame */
...@@ -3035,7 +3037,8 @@ extern Lisp_Object lookup_glyphless_char_display (int, struct it *); ...@@ -3035,7 +3037,8 @@ extern Lisp_Object lookup_glyphless_char_display (int, struct it *);
extern int calc_pixel_width_or_height (double *, struct it *, Lisp_Object, extern int calc_pixel_width_or_height (double *, struct it *, Lisp_Object,
struct font *, int, int *); struct font *, int, int *);
extern EMACS_INT compute_display_string_pos (struct text_pos *, extern EMACS_INT compute_display_string_pos (struct text_pos *,
struct bidi_string_data *, int); struct bidi_string_data *,
int, int *);
extern EMACS_INT compute_display_string_end (EMACS_INT, extern EMACS_INT compute_display_string_end (EMACS_INT,
struct bidi_string_data *); struct bidi_string_data *);
......
...@@ -3134,13 +3134,10 @@ next_overlay_change (EMACS_INT pos) ...@@ -3134,13 +3134,10 @@ next_overlay_change (EMACS_INT pos)
return endpos; return endpos;
} }
/* Record one cached display string position found recently by /* How many characters forward to search for a display property or
compute_display_string_pos. */ display string. Enough for a screenful of 100 lines x 50
static EMACS_INT cached_disp_pos; characters in a line. */
static EMACS_INT cached_prev_pos = -1; #define MAX_DISP_SCAN 5000
static struct buffer *cached_disp_buffer;
static int cached_disp_modiff;
static int cached_disp_overlay_modiff;
/* Return the character position of a display string at or after /* Return the character position of a display string at or after
position specified by POSITION. If no display string exists at or position specified by POSITION. If no display string exists at or
...@@ -3152,57 +3149,33 @@ static int cached_disp_overlay_modiff; ...@@ -3152,57 +3149,33 @@ static int cached_disp_overlay_modiff;
on a GUI frame. */ on a GUI frame. */
EMACS_INT EMACS_INT
compute_display_string_pos (struct text_pos *position, compute_display_string_pos (struct text_pos *position,
struct bidi_string_data *string, int frame_window_p) struct bidi_string_data *string,
int frame_window_p, int *disp_prop_p)
{ {
/* OBJECT = nil means current buffer. */ /* OBJECT = nil means current buffer. */
Lisp_Object object = Lisp_Object object =
(string && STRINGP (string->lstring)) ? string->lstring : Qnil; (string && STRINGP (string->lstring)) ? string->lstring : Qnil;
Lisp_Object pos, spec; Lisp_Object pos, spec, limpos;
int string_p = (string && (STRINGP (string->lstring) || string->s)); int string_p = (string && (STRINGP (string->lstring) || string->s));
EMACS_INT eob = string_p ? string->schars : ZV; EMACS_INT eob = string_p ? string->schars : ZV;
EMACS_INT begb = string_p ? 0 : BEGV; EMACS_INT begb = string_p ? 0 : BEGV;
EMACS_INT bufpos, charpos = CHARPOS (*position); EMACS_INT bufpos, charpos = CHARPOS (*position);
EMACS_INT lim =
(charpos < eob - MAX_DISP_SCAN) ? charpos + MAX_DISP_SCAN : eob;
struct text_pos tpos; struct text_pos tpos;
struct buffer *b; struct buffer *b;
*disp_prop_p = 1;
if (charpos >= eob if (charpos >= eob
/* We don't support display properties whose values are strings /* We don't support display properties whose values are strings
that have display string properties. */ that have display string properties. */
|| string->from_disp_str || string->from_disp_str
/* C strings cannot have display properties. */ /* C strings cannot have display properties. */
|| (string->s && !STRINGP (object))) || (string->s && !STRINGP (object)))
return eob;
/* Check the cached values. */
if (!STRINGP (object))
{ {
if (NILP (object)) *disp_prop_p = 0;
b = current_buffer; return eob;
else
b = XBUFFER (object);
if (b == cached_disp_buffer
&& BUF_MODIFF (b) == cached_disp_modiff
&& BUF_OVERLAY_MODIFF (b) == cached_disp_overlay_modiff
&& !b->clip_changed)
{
if (cached_prev_pos >= 0
&& cached_prev_pos < charpos && charpos <= cached_disp_pos)
return cached_disp_pos;
/* Handle overstepping either end of the known interval. */
if (charpos > cached_disp_pos)
cached_prev_pos = cached_disp_pos;
else /* charpos <= cached_prev_pos */
cached_prev_pos = max (charpos - 1, 0);
}
/* Record new values in the cache. */
if (b != cached_disp_buffer)
{
cached_disp_buffer = b;
cached_prev_pos = max (charpos - 1, 0);
}
cached_disp_modiff = BUF_MODIFF (b);
cached_disp_overlay_modiff = BUF_OVERLAY_MODIFF (b);
} }
/* If the character at CHARPOS is where the display string begins, /* If the character at CHARPOS is where the display string begins,
...@@ -3221,22 +3194,24 @@ compute_display_string_pos (struct text_pos *position, ...@@ -3221,22 +3194,24 @@ compute_display_string_pos (struct text_pos *position,
&& handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos, && handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos,
frame_window_p)) frame_window_p))
{ {
if (!STRINGP (object))
cached_disp_pos = charpos;
return charpos; return charpos;
} }
/* Look forward for the first character with a `display' property /* Look forward for the first character with a `display' property
that will replace the underlying text when displayed. */ that will replace the underlying text when displayed. */
limpos = make_number (lim);
do { do {
pos = Fnext_single_char_property_change (pos, Qdisplay, object, Qnil); pos = Fnext_single_char_property_change (pos, Qdisplay, object, limpos);
CHARPOS (tpos) = XFASTINT (pos); CHARPOS (tpos) = XFASTINT (pos);
if (CHARPOS (tpos) >= lim)
{
*disp_prop_p = 0;
break;
}
if (STRINGP (object)) if (STRINGP (object))
BYTEPOS (tpos) = string_char_to_byte (object, CHARPOS (tpos)); BYTEPOS (tpos) = string_char_to_byte (object, CHARPOS (tpos));
else else
BYTEPOS (tpos) = CHAR_TO_BYTE (CHARPOS (tpos)); BYTEPOS (tpos) = CHAR_TO_BYTE (CHARPOS (tpos));
if (CHARPOS (tpos) >= eob)
break;
spec = Fget_char_property (pos, Qdisplay, object); spec = Fget_char_property (pos, Qdisplay, object);
if (!STRINGP (object)) if (!STRINGP (object))
bufpos = CHARPOS (tpos); bufpos = CHARPOS (tpos);
...@@ -3244,8 +3219,6 @@ compute_display_string_pos (struct text_pos *position, ...@@ -3244,8 +3219,6 @@ compute_display_string_pos (struct text_pos *position,
|| !handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos, || !handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos,
frame_window_p)); frame_window_p));
if (!STRINGP (object))
cached_disp_pos = CHARPOS (tpos);
return CHARPOS (tpos); return CHARPOS (tpos);
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment