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.
+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>
* keymap.c (Fdefine_key): Fix Lisp_Object/int mixup; apply some CSE.
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)
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;
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. */
*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;
/* 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;
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;
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);
&& 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;
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
{
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;
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
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 */
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 *);
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
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,
&& 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);
|| !handle_display_spec (NULL, spec, object, Qnil, &tpos, bufpos,
frame_window_p));
- if (!STRINGP (object))
- cached_disp_pos = CHARPOS (tpos);
return CHARPOS (tpos);
}