From 55439c615b8e04748a66a6d88ec70a9ac5acd672 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 2 Aug 2011 22:16:32 +0300 Subject: [PATCH] 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. --- src/ChangeLog | 18 +++++++++++++++ src/bidi.c | 44 +++++++++++++++++++++++-------------- src/dispextern.h | 5 ++++- src/xdisp.c | 67 +++++++++++++++++--------------------------------------- 4 files changed, 70 insertions(+), 64 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 59f09515829..3717924ff68 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,21 @@ +2011-08-02 Eli Zaretskii + + 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 * keymap.c (Fdefine_key): Fix Lisp_Object/int mixup; apply some CSE. diff --git a/src/bidi.c b/src/bidi.c index 697ebb92856..ae5143b37e0 100644 --- a/src/bidi.c +++ b/src/bidi.c @@ -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->sor = L2R; /* FIXME: should it be user-selectable? */ 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 "stack". */ if (bidi_cache_start == 0) @@ -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 combined length in CH_LEN and NCHARS. DISP_POS specifies the character position of the next display string, or -1 if not yet - computed. When the next character is at or beyond that position, - the function updates DISP_POS with the position of the 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. */ + computed. DISP_PROP_P non-zero means that there's really a display + string at DISP_POS, as opposed to when we searched till DISP_POS + without findingone. When the next character is at or beyond that + position, the function updates DISP_POS with the position of the + 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 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 ch; @@ -894,7 +897,8 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos, if (charpos < endpos && charpos > *disp_pos) { 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. */ @@ -904,8 +908,9 @@ bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos, *ch_len = 1; *nchars = 1; *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; @@ -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 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); - *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; @@ -1083,6 +1090,7 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p) int ch; EMACS_INT ch_len, nchars; EMACS_INT pos, disp_pos = -1; + int disp_prop_p = 0; bidi_type_t type; const unsigned char *s; @@ -1130,7 +1138,8 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p) bytepos = pstartbyte; if (!string_p) 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); 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) && bidi_at_paragraph_end (pos, bytepos) >= -1) break; /* 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); pos += nchars; bytepos += ch_len; @@ -1290,6 +1300,7 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it) bidi_it->ch_len = 1; bidi_it->nchars = 1; bidi_it->disp_pos = (string_p ? bidi_it->string.schars : ZV); + bidi_it->disp_prop_p = 0; } else { @@ -1297,8 +1308,8 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it) display string, treat the entire run of covered characters as a single character u+FFFC. */ curchar = bidi_fetch_char (bidi_it->bytepos, bidi_it->charpos, - &bidi_it->disp_pos, &bidi_it->string, - bidi_it->frame_window_p, + &bidi_it->disp_pos, &bidi_it->disp_prop_p, + &bidi_it->string, bidi_it->frame_window_p, &bidi_it->ch_len, &bidi_it->nchars); } bidi_it->ch = curchar; @@ -2032,12 +2043,13 @@ bidi_level_of_next_char (struct bidi_it *bidi_it) struct bidi_string_data bs = bidi_it->string; bidi_type_t chtype; int fwp = bidi_it->frame_window_p; + int dpp = bidi_it->disp_prop_p; if (bidi_it->nchars <= 0) abort (); do { - ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, &bs, fwp, - &clen, &nc); + ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, &dpp, &bs, + fwp, &clen, &nc); if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */) chtype = NEUTRAL_B; else diff --git a/src/dispextern.h b/src/dispextern.h index dc44c698164..2e245479a81 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -1868,6 +1868,8 @@ struct bidi_it { bidi_dir_t paragraph_dir; /* current paragraph direction */ EMACS_INT separator_limit; /* where paragraph separator should end */ 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 new_paragraph : 1; /* if non-zero, we expect a new paragraph */ 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 *); extern int calc_pixel_width_or_height (double *, struct it *, Lisp_Object, struct font *, int, int *); 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, struct bidi_string_data *); diff --git a/src/xdisp.c b/src/xdisp.c index 9d3e501787b..fa4b3c4f9ab 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3134,13 +3134,10 @@ next_overlay_change (EMACS_INT pos) return endpos; } -/* Record one cached display string position found recently by - compute_display_string_pos. */ -static EMACS_INT cached_disp_pos; -static EMACS_INT cached_prev_pos = -1; -static struct buffer *cached_disp_buffer; -static int cached_disp_modiff; -static int cached_disp_overlay_modiff; +/* How many characters forward to search for a display property or + display string. Enough for a screenful of 100 lines x 50 + characters in a line. */ +#define MAX_DISP_SCAN 5000 /* Return the character position of a display string at or after position specified by POSITION. If no display string exists at or @@ -3152,57 +3149,33 @@ static int cached_disp_overlay_modiff; on a GUI frame. */ EMACS_INT 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. */ Lisp_Object object = (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)); EMACS_INT eob = string_p ? string->schars : ZV; EMACS_INT begb = string_p ? 0 : BEGV; EMACS_INT bufpos, charpos = CHARPOS (*position); + EMACS_INT lim = + (charpos < eob - MAX_DISP_SCAN) ? charpos + MAX_DISP_SCAN : eob; struct text_pos tpos; struct buffer *b; + *disp_prop_p = 1; + if (charpos >= eob /* We don't support display properties whose values are strings that have display string properties. */ || string->from_disp_str /* C strings cannot have display properties. */ || (string->s && !STRINGP (object))) - return eob; - - /* Check the cached values. */ - if (!STRINGP (object)) { - if (NILP (object)) - b = current_buffer; - 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); + *disp_prop_p = 0; + return eob; } /* If the character at CHARPOS is where the display string begins, @@ -3221,22 +3194,24 @@ compute_display_string_pos (struct text_pos *position, && handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos, frame_window_p)) { - if (!STRINGP (object)) - cached_disp_pos = charpos; return charpos; } /* Look forward for the first character with a `display' property that will replace the underlying text when displayed. */ + limpos = make_number (lim); 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); + if (CHARPOS (tpos) >= lim) + { + *disp_prop_p = 0; + break; + } if (STRINGP (object)) BYTEPOS (tpos) = string_char_to_byte (object, CHARPOS (tpos)); else BYTEPOS (tpos) = CHAR_TO_BYTE (CHARPOS (tpos)); - if (CHARPOS (tpos) >= eob) - break; spec = Fget_char_property (pos, Qdisplay, object); if (!STRINGP (object)) bufpos = CHARPOS (tpos); @@ -3244,8 +3219,6 @@ compute_display_string_pos (struct text_pos *position, || !handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos, frame_window_p)); - if (!STRINGP (object)) - cached_disp_pos = CHARPOS (tpos); return CHARPOS (tpos); } -- 2.11.4.GIT