]> code.delx.au - gnu-emacs/blobdiff - src/xdisp.c
*** empty log message ***
[gnu-emacs] / src / xdisp.c
index b40d944b30fa76881b689020090234a7584ca087..f18f5ac5a2f863d865c2e82f8c26c22ff0ec1e3e 100644 (file)
@@ -226,6 +226,7 @@ Lisp_Object Qinhibit_point_motion_hooks;
 Lisp_Object QCeval, Qwhen, QCfile, QCdata;
 Lisp_Object Qfontified;
 Lisp_Object Qgrow_only;
+Lisp_Object Qinhibit_eval_during_redisplay;
 
 /* Functions called to fontify regions of text.  */
 
@@ -254,6 +255,10 @@ int auto_resize_tool_bars_p;
 
 Lisp_Object Vinhibit_redisplay, Qinhibit_redisplay;
 
+/* Non-zero means Lisp evaluation during redisplay is inhibited.  */
+
+int inhibit_eval_during_redisplay;
+
 /* Names of text properties relevant for redisplay.  */
 
 Lisp_Object Qdisplay, Qrelative_width, Qalign_to;
@@ -769,6 +774,8 @@ static int handle_single_display_prop P_ ((struct it *, Lisp_Object,
                                           Lisp_Object, struct text_pos *,
                                           int));
 static int underlying_face_id P_ ((struct it *));
+static int in_ellipses_for_invisible_text_p P_ ((struct display_pos *,
+                                                struct window *));
 
 #define face_before_it_pos(IT) face_before_or_after_it_pos ((IT), 1)
 #define face_after_it_pos(IT)  face_before_or_after_it_pos ((IT), 0)
@@ -1276,15 +1283,24 @@ Lisp_Object
 safe_eval (sexpr)
      Lisp_Object sexpr;
 {
-  int count = BINDING_STACK_SIZE ();
-  struct gcpro gcpro1;
   Lisp_Object val;
+  
+  if (inhibit_eval_during_redisplay)
+    val = Qnil;
+  else
+    {
+      int count = BINDING_STACK_SIZE ();
+      struct gcpro gcpro1;
 
-  GCPRO1 (sexpr);
-  specbind (Qinhibit_redisplay, Qt);
-  val = internal_condition_case_1 (Feval, sexpr, Qerror, safe_eval_handler);
-  UNGCPRO;
-  return unbind_to (count, val);
+      GCPRO1 (sexpr);
+      specbind (Qinhibit_redisplay, Qt);
+      val = internal_condition_case_1 (Feval, sexpr, Qerror,
+                                      safe_eval_handler);
+      UNGCPRO;
+      val = unbind_to (count, val);
+    }
+  
+  return val;
 }
 
 
@@ -1296,17 +1312,25 @@ safe_call (nargs, args)
      int nargs;
      Lisp_Object *args;
 {
-  int count = BINDING_STACK_SIZE ();
   Lisp_Object val;
-  struct gcpro gcpro1;
+  
+  if (inhibit_eval_during_redisplay)
+    val = Qnil;
+  else
+    {
+      int count = BINDING_STACK_SIZE ();
+      struct gcpro gcpro1;
 
-  GCPRO1 (args[0]);
-  gcpro1.nvars = nargs;
-  specbind (Qinhibit_redisplay, Qt);
-  val = internal_condition_case_2 (Ffuncall, nargs, args, Qerror,
-                                  safe_eval_handler);
-  UNGCPRO;
-  return unbind_to (count, val);
+      GCPRO1 (args[0]);
+      gcpro1.nvars = nargs;
+      specbind (Qinhibit_redisplay, Qt);
+      val = internal_condition_case_2 (Ffuncall, nargs, args, Qerror,
+                                      safe_eval_handler);
+      UNGCPRO;
+      val = unbind_to (count, val);
+    }
+
+  return val;
 }
 
 
@@ -1740,18 +1764,17 @@ start_display (it, w, pos)
 }
 
 
-/* Initialize IT for stepping through current_buffer in window W,
-   starting at position POS that includes overlay string and display
-   vector/ control character translation position information.  */
+/* Return 1 if POS is a position in ellipses displayed for invisible
+   text.  W is the window we display, for text property lookup.  */
 
-static void
-init_from_display_pos (it, w, pos)
-     struct it *it;
-     struct window *w;
+static int
+in_ellipses_for_invisible_text_p (pos, w)
      struct display_pos *pos;
+     struct window *w;
 {
-  int charpos = CHARPOS (pos->pos), bytepos = BYTEPOS (pos->pos);
   Lisp_Object prop, window;
+  int ellipses_p = 0;
+  int charpos = CHARPOS (pos->pos);
   
   /* If POS specifies a position in a display vector, this might
      be for an ellipsis displayed for invisible text.  We won't
@@ -1764,16 +1787,39 @@ init_from_display_pos (it, w, pos)
       && (XSETWINDOW (window, w),
          prop = Fget_char_property (make_number (charpos),
                                     Qinvisible, window),
-         NILP (prop)))
+         !TEXT_PROP_MEANS_INVISIBLE (prop)))
     {
       prop = Fget_char_property (make_number (charpos - 1), Qinvisible,
                                 window);
       if (TEXT_PROP_MEANS_INVISIBLE (prop)
          && TEXT_PROP_MEANS_INVISIBLE_WITH_ELLIPSIS (prop))
-       {
-         --charpos;
-         bytepos = 0;
-       }
+       ellipses_p = 1;
+    }
+
+  return ellipses_p;
+}
+
+
+/* Initialize IT for stepping through current_buffer in window W,
+   starting at position POS that includes overlay string and display
+   vector/ control character translation position information.  */
+
+static void
+init_from_display_pos (it, w, pos)
+     struct it *it;
+     struct window *w;
+     struct display_pos *pos;
+{
+  int charpos = CHARPOS (pos->pos), bytepos = BYTEPOS (pos->pos);
+  
+  /* If POS specifies a position in a display vector, this might
+     be for an ellipsis displayed for invisible text.  We won't
+     get the iterator set up for delivering that ellipsis unless
+     we make sure that it gets aware of the invisible text.  */
+  if (in_ellipses_for_invisible_text_p (pos, w))
+    {
+      --charpos;
+      bytepos = 0;
     }
     
   /* Keep in mind: the call to reseat in init_iterator skips invisible
@@ -1786,7 +1832,6 @@ init_from_display_pos (it, w, pos)
      but the call to init_iterator below will move us to the
      after-string.  */
   init_iterator (it, w, charpos, bytepos, NULL, DEFAULT_FACE_ID);
-  xassert (IT_CHARPOS (*it) == CHARPOS (pos->pos));
 
   /* If position is within an overlay string, set up IT to
      the right overlay string.  */
@@ -1848,18 +1893,6 @@ init_from_display_pos (it, w, pos)
       xassert (it->dpvec && it->current.dpvec_index == 0);
       it->current.dpvec_index = pos->dpvec_index;
     }
-  else if (it->current.dpvec_index >= 0)
-    {
-      /* I don't think this can happen, just being paranoid...  */
-      it->dpvec = NULL;
-      it->current.dpvec_index = -1;
-      if (it->s)
-       it->method = next_element_from_c_string;
-      else if (STRINGP (it->string))
-       it->method = next_element_from_string;
-      else
-       it->method = next_element_from_buffer;
-    }
   
   CHECK_IT (it);
 }
@@ -2633,10 +2666,17 @@ handle_display_prop (it)
     return HANDLED_NORMALLY;
 
   if (CONSP (prop)
-      && CONSP (XCAR (prop))
-      && !EQ (Qmargin, XCAR (XCAR (prop))))
+      /* Simple properties.  */
+      && !EQ (XCAR (prop), Qimage)
+      && !EQ (XCAR (prop), Qspace)
+      && !EQ (XCAR (prop), Qwhen)
+      && !EQ (XCAR (prop), Qspace_width)
+      && !EQ (XCAR (prop), Qheight)
+      && !EQ (XCAR (prop), Qraise)
+      /* Marginal area specifications.  */
+      && !(CONSP (XCAR (prop)) && EQ (XCAR (XCAR (prop)), Qmargin))
+      && !NILP (XCAR (prop)))
     {
-      /* A list of sub-properties.  */
       for (; CONSP (prop); prop = XCDR (prop))
        {
          if (handle_single_display_prop (it, XCAR (prop), object,
@@ -3555,8 +3595,9 @@ get_overlay_strings (it)
       /* Set up IT to deliver display elements from the first overlay
         string.  */
       IT_STRING_CHARPOS (*it) = IT_STRING_BYTEPOS (*it) = 0;
-      it->stop_charpos = 0;
       it->string = it->overlay_strings[0];
+      it->stop_charpos = 0;
+      it->end_charpos = XSTRING (it->string)->size;
       it->multibyte_p = STRING_MULTIBYTE (it->string);
       xassert (STRINGP (it->string));
       it->method = next_element_from_string;
@@ -3972,7 +4013,8 @@ reseat_to_string (it, s, string, charpos, precision, field_width, multibyte)
   it->current.dpvec_index = -1;
   xassert (charpos >= 0);
   
-  /* Use the setting of MULTIBYTE if specified.  */
+  /* If STRING is specified, use its multibyteness, otherwise use the
+     setting of MULTIBYTE, if specified.  */
   if (multibyte >= 0)
     it->multibyte_p = multibyte > 0;
   
@@ -4334,6 +4376,8 @@ set_iterator_to_next (it, reseat_p)
              pop_it (it);
              if (!STRINGP (it->string))
                it->method = next_element_from_buffer;
+             else
+               goto consider_string_end;
            }
        }
     }
@@ -6096,6 +6140,11 @@ with_echo_area_buffer (w, which, fn, a1, a2, a3, a4)
 
   buffer = echo_area_buffer[this_one];
 
+  /* Don't get confused by reusing the buffer used for echoing
+     for a different purpose.  */
+  if (!echoing && EQ (buffer, echo_message_buffer))
+    cancel_echoing ();
+
   record_unwind_protect (unwind_with_echo_area_buffer,
                         with_echo_area_buffer_unwind_data (w));
 
@@ -9705,6 +9754,8 @@ redisplay_window (window, just_this_one_p)
            blank_row (w, row, y);
          goto finish_scroll_bars;
        }
+
+      clear_glyph_matrix (w->desired_matrix);
     }
 
   /* Otherwise set up data on this window; select its buffer and point
@@ -9935,36 +9986,10 @@ redisplay_window (window, just_this_one_p)
       goto recenter;
     }
   
-  /* Try scrolling with try_window_id.  */
-  else if (/* Windows and buffers haven't changed.  */
-          !windows_or_buffers_changed
-          /* Window must be either use window-based redisplay or
-             be full width.  */
-          && (FRAME_WINDOW_P (f)
-              || (line_ins_del_ok && WINDOW_FULL_WIDTH_P (w)))
-          && !MINI_WINDOW_P (w)
-          /* Point is not known NOT to appear in window.  */
-          && PT >= CHARPOS (startp)
-          && XFASTINT (w->last_modified)
-          /* Window is not hscrolled.  */
-          && XFASTINT (w->hscroll) == 0
-          /* Selective display has not changed.  */
-          && !current_buffer->clip_changed
-          /* Current matrix is up to date.  */
-          && !NILP (w->window_end_valid)
-          /* Can't use this case if highlighting a region because
-             a cursor movement will do more than just set the cursor.  */
-          && !(!NILP (Vtransient_mark_mode)
-               && !NILP (current_buffer->mark_active))
-          && NILP (w->region_showing)
-          && NILP (Vshow_trailing_whitespace)
-          /* Overlay arrow position and string not changed.  */
-          && EQ (last_arrow_position, COERCE_MARKER (Voverlay_arrow_position))
-          && EQ (last_arrow_string, Voverlay_arrow_string)
-          /* Value is > 0 if update has been done, it is -1 if we
-             know that the same window start will not work.  It is 0
-             if unsuccessful for some other reason.  */
-          && (tem = try_window_id (w)) != 0)
+  /* Try scrolling with try_window_id.  Value is > 0 if update has
+     been done, it is -1 if we know that the same window start will
+     not work.  It is 0 if unsuccessful for some other reason.  */
+  else if ((tem = try_window_id (w)) != 0)
     {
 #if GLYPH_DEBUG
       debug_method_add (w, "try_window_id %d", tem);
@@ -10075,7 +10100,7 @@ redisplay_window (window, just_this_one_p)
   /* Move backward half the height of the window.  */
   init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
   it.current_y = it.last_visible_y;
-  move_it_vertically_backward (&it, it.last_visible_y / 2);
+  move_it_vertically_backward (&it, window_box_height (w) / 2);
   xassert (IT_CHARPOS (it) >= BEGV);
 
   /* The function move_it_vertically_backward may move over more
@@ -10427,6 +10452,13 @@ try_window_reusing_current_matrix (w)
     {
       int first_row_y;
       
+      /* Don't use this method if the display starts with an ellipsis
+        displayed for invisible text.  It's not easy to handle that case
+        below, and it's certainly not worth the effort since this is
+        not a frequent case.  */
+      if (in_ellipses_for_invisible_text_p (&start_row->start, w))
+       return 0;
+
       IF_DEBUG (debug_method_add (w, "twu1"));
       
       /* Display up to a row that can be reused.  The variable
@@ -10460,7 +10492,8 @@ try_window_reusing_current_matrix (w)
              row = MATRIX_FIRST_TEXT_ROW (w->current_matrix);
              while (MATRIX_ROW_DISPLAYS_TEXT_P (row))
                {
-                 if (cursor_row_p (w, row))
+                 if (PT >= MATRIX_ROW_START_CHARPOS (row)
+                     && PT < MATRIX_ROW_END_CHARPOS (row))
                    {
                      set_cursor_from_row (w, row, w->current_matrix, 0, 0,
                                           dy, nrows_scrolled);
@@ -10617,13 +10650,15 @@ try_window_reusing_current_matrix (w)
           MATRIX_ROW_BOTTOM_Y (first_row_to_display) < yb;
           ++first_row_to_display)
        {
-         if (cursor_row_p (w, first_row_to_display))
+         if (PT >= MATRIX_ROW_START_CHARPOS (first_row_to_display)
+             && PT < MATRIX_ROW_END_CHARPOS (first_row_to_display))
            pt_row = first_row_to_display;
        }
 
       /* Start displaying at the start of first_row_to_display.  */
       xassert (first_row_to_display->y < yb);
       init_to_row_start (&it, w, first_row_to_display);
+
       nrows_scrolled = (MATRIX_ROW_VPOS (first_reusable_row, w->current_matrix)
                        - start_vpos);
       it.vpos = (MATRIX_ROW_VPOS (first_row_to_display, w->current_matrix)
@@ -10778,15 +10813,14 @@ find_last_row_displaying_text (matrix, it, start)
 
 
 /* Return the last row in the current matrix of W that is not affected
-   by changes at the start of current_buffer that occurred since the
-   last time W was redisplayed.  Value is null if no such row exists.
+   by changes at the start of current_buffer that occurred since W's
+   current matrix was built.  Value is null if no such row exists.
 
-   The global variable beg_unchanged has to contain the number of
-   bytes unchanged at the start of current_buffer.  BEG +
-   beg_unchanged is the buffer position of the first changed byte in
-   current_buffer.  Characters at positions < BEG + beg_unchanged are
-   at the same buffer positions as they were when the current matrix
-   was built.  */
+   BEG_UNCHANGED us the number of characters unchanged at the start of
+   current_buffer.  BEG + BEG_UNCHANGED is the buffer position of the
+   first changed character in current_buffer.  Characters at positions <
+   BEG + BEG_UNCHANGED are at the same buffer positions as they were
+   when the current matrix was built.  */
 
 static struct glyph_row *
 find_last_unchanged_at_beg_row (w)
@@ -10827,12 +10861,16 @@ find_last_unchanged_at_beg_row (w)
 
 
 /* Find the first glyph row in the current matrix of W that is not
-   affected by changes at the end of current_buffer since the last
-   time the window was redisplayed.  Return in *DELTA the number of
-   chars by which buffer positions in unchanged text at the end of
-   current_buffer must be adjusted.  Return in *DELTA_BYTES the
-   corresponding number of bytes.  Value is null if no such row
-   exists, i.e. all rows are affected by changes.  */
+   affected by changes at the end of current_buffer since the 
+   time W's current matrix was built.
+
+   Return in *DELTA the number of chars by which buffer positions in
+   unchanged text at the end of current_buffer must be adjusted.
+   
+   Return in *DELTA_BYTES the corresponding number of bytes.
+
+   Value is null if no such row exists, i.e. all rows are affected by
+   changes.  */
    
 static struct glyph_row *
 find_first_unchanged_at_end_row (w, delta, delta_bytes)
@@ -10877,8 +10915,8 @@ find_first_unchanged_at_end_row (w, delta, delta_bytes)
 
       /* Set last_unchanged_pos to the buffer position of the last
         character in the buffer that has not been changed.  Z is the
-        index + 1 of the last byte in current_buffer, i.e. by
-        subtracting end_unchanged we get the index of the last
+        index + 1 of the last character in current_buffer, i.e. by
+        subtracting END_UNCHANGED we get the index of the last
         unchanged character, and we have to add BEG to get its buffer
         position.  */
       last_unchanged_pos = Z - END_UNCHANGED + BEG;
@@ -11025,8 +11063,6 @@ row_containing_pos (w, charpos, start, end)
 
    7. Update W's window end information.  */
 
-  /* Check that window end is what we expect it to be.  */
-
 static int
 try_window_id (w)
      struct window *w;
@@ -11046,15 +11082,78 @@ try_window_id (w)
   int first_unchanged_at_end_vpos = 0;
   struct glyph_row *last_text_row, *last_text_row_at_end;
   struct text_pos start;
+  int first_changed_charpos, last_changed_charpos;
 
+  /* This is handy for debugging.  */
+#if 0
+#define GIVE_UP(X)                                             \
+  do {                                                         \
+    fprintf (stderr, "try_window_id give up %d\n", (X));       \
+    return 0;                                                  \
+  } while (0)
+#else
+#define GIVE_UP(X) return 0
+#endif
+  
   SET_TEXT_POS_FROM_MARKER (start, w->start);
 
-  /* Check pre-conditions.  Window end must be valid, otherwise
-     the current matrix would not be up to date.  */
-  xassert (!NILP (w->window_end_valid));
-  xassert (FRAME_WINDOW_P (XFRAME (w->frame))
-          || (line_ins_del_ok && WINDOW_FULL_WIDTH_P (w)));
+  /* Don't use this for mini-windows because these can show
+     messages and mini-buffers, and we don't handle that here.  */
+  if (MINI_WINDOW_P (w))
+    GIVE_UP (1);
+  
+  /* This flag is used to prevent redisplay optimizations.  */
+  if (windows_or_buffers_changed)
+    GIVE_UP (2);
+  
+  /* Narrowing has not changed.  This flag is also set to prevent
+     redisplay optimizations.  It would be nice to further
+     reduce the number of cases where this prevents try_window_id.  */
+  if (current_buffer->clip_changed)
+    GIVE_UP (3);
+
+  /* Window must either use window-based redisplay or be full width.  */
+  if (!FRAME_WINDOW_P (f)
+      && (!line_ins_del_ok
+         || !WINDOW_FULL_WIDTH_P (w)))
+    GIVE_UP (4);
+
+  /* Point is not known NOT to appear in W.  */
+  if (PT < CHARPOS (start))
+    GIVE_UP (5);
+
+  /* Another way to prevent redisplay optimizations.  */
+  if (XFASTINT (w->last_modified) == 0)
+    GIVE_UP (6);
+  
+  /* Window is not hscrolled.  */
+  if (XFASTINT (w->hscroll) != 0)
+    GIVE_UP (7);
+  
+  /* Display wasn't paused.  */
+  if (NILP (w->window_end_valid))
+    GIVE_UP (8);
+  
+  /* Can't use this if highlighting a region because a cursor movement
+     will do more than just set the cursor.  */
+  if (!NILP (Vtransient_mark_mode)
+      && !NILP (current_buffer->mark_active))
+    GIVE_UP (9);
+
+  /* Likewise if highlighting trailing whitespace.  */
+  if (!NILP (Vshow_trailing_whitespace))
+    GIVE_UP (11);
+  
+  /* Likewise if showing a region.  */
+  if (!NILP (w->region_showing))
+    GIVE_UP (10);
+  
+  /* Can use this if overlay arrow position and or string have changed.  */
+  if (!EQ (last_arrow_position, COERCE_MARKER (Voverlay_arrow_position))
+      || !EQ (last_arrow_string, Voverlay_arrow_string))
+    GIVE_UP (12);
 
+  
   /* Make sure beg_unchanged and end_unchanged are up to date.  Do it
      only if buffer has really changed.  The reason is that the gap is
      initially at Z for freshly visited files.  The code below would
@@ -11069,73 +11168,107 @@ try_window_id (w)
        END_UNCHANGED = Z - GPT;
     }
 
+  /* The position of the first and last character that has been changed.  */
+  first_changed_charpos = BEG + BEG_UNCHANGED;
+  last_changed_charpos  = Z - END_UNCHANGED;
+
   /* If window starts after a line end, and the last change is in
      front of that newline, then changes don't affect the display.
      This case happens with stealth-fontification.  Note that although
      the display is unchanged, glyph positions in the matrix have to
      be adjusted, of course.  */
   row = MATRIX_ROW (w->current_matrix, XFASTINT (w->window_end_vpos));
-  if (CHARPOS (start) > BEGV
-      && Z - END_UNCHANGED < CHARPOS (start) - 1
-      && FETCH_BYTE (BYTEPOS (start) - 1) == '\n'
-      && PT < MATRIX_ROW_END_CHARPOS (row))
-    {
-      struct glyph_row *r0 = MATRIX_FIRST_TEXT_ROW (current_matrix);
-      int delta = CHARPOS (start) - MATRIX_ROW_START_CHARPOS (r0);
-
-      if (delta)
-       {
-         struct glyph_row *r1 = MATRIX_BOTTOM_TEXT_ROW (current_matrix, w);
-         int delta_bytes = BYTEPOS (start) - MATRIX_ROW_START_BYTEPOS (r0);
-
-         increment_matrix_positions (w->current_matrix,
-                                     MATRIX_ROW_VPOS (r0, current_matrix),
-                                     MATRIX_ROW_VPOS (r1, current_matrix),
-                                     delta, delta_bytes);
-       }
-      
-#if 0  /* If changes are all in front of the window start, the
-         distance of the last displayed glyph from Z hasn't
-         changed.  */
-      w->window_end_pos
-       = make_number (Z - MATRIX_ROW_END_CHARPOS (row));
-      w->window_end_bytepos
-       = Z_BYTE - MATRIX_ROW_END_BYTEPOS (row);
-#endif
-
-      row = row_containing_pos (w, PT, r0, NULL);
-      if (row == NULL)
-       return 0;
+  if (MATRIX_ROW_DISPLAYS_TEXT_P (row)
+      && ((last_changed_charpos < CHARPOS (start)
+          && CHARPOS (start) == BEGV)
+         || (last_changed_charpos < CHARPOS (start) - 1
+             && FETCH_BYTE (BYTEPOS (start) - 1) == '\n')))
+    {
+      int Z_old, delta, Z_BYTE_old, delta_bytes;
+      struct glyph_row *r0;
+
+      /* Compute how many chars/bytes have been added to or removed
+        from the buffer.  */
+      Z_old = MATRIX_ROW_END_CHARPOS (row) + XFASTINT (w->window_end_pos);
+      Z_BYTE_old = MATRIX_ROW_END_BYTEPOS (row) + w->window_end_bytepos;
+      delta = Z - Z_old;
+      delta_bytes = Z_BYTE - Z_BYTE_old;
+         
+      /* Give up if PT is not in the window.  Note that it already has
+        been checked at the start of try_window_id that PT is not in
+        front of the window start.  */
+      if (PT >= MATRIX_ROW_END_CHARPOS (row) + delta)
+       GIVE_UP (13);
+
+      /* If window start is unchanged, we can reuse the whole matrix
+        as is, after adjusting glyph positions.  No need to compute
+        the window end again, since its offset from Z hasn't changed.  */
+      r0 = MATRIX_FIRST_TEXT_ROW (current_matrix);
+      if (CHARPOS (start) == MATRIX_ROW_START_CHARPOS (r0) + delta
+         && BYTEPOS (start) == MATRIX_ROW_START_BYTEPOS (r0) + delta_bytes)
+       {
+         /* Adjust positions in the glyph matrix.  */
+         if (delta || delta_bytes)
+           {
+             struct glyph_row *r1
+               = MATRIX_BOTTOM_TEXT_ROW (current_matrix, w);
+             increment_matrix_positions (w->current_matrix,
+                                         MATRIX_ROW_VPOS (r0, current_matrix),
+                                         MATRIX_ROW_VPOS (r1, current_matrix),
+                                         delta, delta_bytes);
+           }
       
-      set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
-      return 1;
+         /* Set the cursor.  */
+         row = row_containing_pos (w, PT, r0, NULL);
+         set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
+         return 1;
+       }
     }
 
-  /* Return quickly if changes are all below what is displayed in the
-     window, and if PT is in the window.  */
-  if (BEG_UNCHANGED > MATRIX_ROW_END_CHARPOS (row)
-      && PT < MATRIX_ROW_END_CHARPOS (row))
+  /* Handle the case that changes are all below what is displayed in
+     the window, and that PT is in the window.  */
+  if (first_changed_charpos >= MATRIX_ROW_END_CHARPOS (row))
     {
-      /* We have to update window end positions because the buffer's
-        size has changed.  */
-      w->window_end_pos
-       = make_number (Z - MATRIX_ROW_END_CHARPOS (row));
-      w->window_end_bytepos
-       = Z_BYTE - MATRIX_ROW_END_BYTEPOS (row);
+      struct glyph_row *r0;
 
-      row = MATRIX_FIRST_TEXT_ROW (w->current_matrix);
-      row = row_containing_pos (w, PT, row, NULL);
-      set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
-      return 2;
+      /* Give up if PT is not in the window.  Note that it already has
+        been checked at the start of try_window_id that PT is not in
+        front of the window start.  */
+      if (PT >= MATRIX_ROW_END_CHARPOS (row))
+       GIVE_UP (14);
+
+      /* If window start is unchanged, we can reuse the whole matrix
+        as is, without changing glyph positions since no text has
+        been added/removed in front of the window end.  */
+      r0 = MATRIX_FIRST_TEXT_ROW (current_matrix);
+      if (TEXT_POS_EQUAL_P (start, r0->start.pos))
+       {
+         /* We have to compute the window end anew since text
+            can have been added/removed after it.  */
+         w->window_end_pos
+           = make_number (Z - MATRIX_ROW_END_CHARPOS (row));
+         w->window_end_bytepos
+           = Z_BYTE - MATRIX_ROW_END_BYTEPOS (row);
+
+         /* Set the cursor.  */
+         row = row_containing_pos (w, PT, r0, NULL);
+         set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
+         return 2;
+       }
     }
 
   /* Check that window start agrees with the start of the first glyph
      row in its current matrix.  Check this after we know the window
      start is not in changed text, otherwise positions would not be
      comparable.  */
-  row = MATRIX_FIRST_TEXT_ROW (w->current_matrix);
+  if (BEG_UNCHANGED + END_UNCHANGED != Z - BEG
+      && CHARPOS (start) >= first_changed_charpos
+      && CHARPOS (start) <= last_changed_charpos)
+    GIVE_UP (15);
+  
+  row = MATRIX_FIRST_TEXT_ROW (current_matrix);
   if (!TEXT_POS_EQUAL_P (start, row->start.pos))
-    return 0;
+    GIVE_UP (16);
 
   /* Compute the position at which we have to start displaying new
      lines.  Some of the lines at the top of the window might be
@@ -11155,7 +11288,7 @@ try_window_id (w)
        --last_unchanged_at_beg_row;
 
       if (MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (last_unchanged_at_beg_row))
-       return 0;
+       GIVE_UP (17);
       
       init_to_row_end (&it, w, last_unchanged_at_beg_row);
       start_pos = it.current.pos;
@@ -11224,7 +11357,7 @@ try_window_id (w)
        }
     }
   else if (last_unchanged_at_beg_row == NULL)
-    return 0;
+    GIVE_UP (18);
 
 
 #if GLYPH_DEBUG
@@ -11369,7 +11502,9 @@ try_window_id (w)
          int first_unchanged_at_end_vpos
            = MATRIX_ROW_VPOS (first_unchanged_at_end_row, w->current_matrix);
          int from = XFASTINT (w->top) + first_unchanged_at_end_vpos;
-         int end = XFASTINT (w->top) + window_internal_height (w);
+         int end = (XFASTINT (w->top)
+                    + (WINDOW_WANTS_HEADER_LINE_P (w) ? 1 : 0)
+                    + window_internal_height (w));
          
          /* Perform the operation on the screen.  */
          if (dvpos > 0)
@@ -11520,6 +11655,8 @@ try_window_id (w)
       w->window_end_bytepos = Z_BYTE - MATRIX_ROW_END_BYTEPOS (row);
       w->window_end_vpos
        = make_number (MATRIX_ROW_VPOS (row, w->current_matrix));
+      xassert (w->window_end_bytepos >= 0);
+      IF_DEBUG (debug_method_add (w, "A"));
     }
   else if (last_text_row_at_end)
     {
@@ -11529,6 +11666,8 @@ try_window_id (w)
        = Z_BYTE - MATRIX_ROW_END_BYTEPOS (last_text_row_at_end);
       w->window_end_vpos
        = make_number (MATRIX_ROW_VPOS (last_text_row_at_end, desired_matrix));
+      xassert (w->window_end_bytepos >= 0);
+      IF_DEBUG (debug_method_add (w, "B"));
     }
   else if (last_text_row)
     {
@@ -11541,6 +11680,7 @@ try_window_id (w)
        = Z_BYTE - MATRIX_ROW_END_BYTEPOS (last_text_row);
       w->window_end_vpos
        = make_number (MATRIX_ROW_VPOS (last_text_row, desired_matrix));
+      xassert (w->window_end_bytepos >= 0);
     }
   else if (first_unchanged_at_end_row == NULL
           && last_text_row == NULL
@@ -11548,26 +11688,42 @@ try_window_id (w)
     {
       /* Displayed to end of window, but no line containing text was
         displayed.  Lines were deleted at the end of the window.  */
-      int vpos;
-      int header_line_p = WINDOW_WANTS_HEADER_LINE_P (w) ? 1 : 0;
-
-      for (vpos = XFASTINT (w->window_end_vpos); vpos > 0; --vpos)
-       if ((w->desired_matrix->rows[vpos + header_line_p].enabled_p
-            && w->desired_matrix->rows[vpos + header_line_p].displays_text_p)
-           || (!w->desired_matrix->rows[vpos + header_line_p].enabled_p
-               && w->current_matrix->rows[vpos + header_line_p].displays_text_p))
-         break;
+      int first_vpos = WINDOW_WANTS_HEADER_LINE_P (w) ? 1 : 0;
+      int vpos = XFASTINT (w->window_end_vpos);
+      struct glyph_row *current_row = current_matrix->rows + vpos;
+      struct glyph_row *desired_row = desired_matrix->rows + vpos;
 
-      w->window_end_vpos = make_number (vpos);
+      for (row = NULL;
+          row == NULL && vpos >= first_vpos;
+          --vpos, --current_row, --desired_row)
+       {
+         if (desired_row->enabled_p)
+           {
+             if (desired_row->displays_text_p)
+               row = desired_row;
+           }
+         else if (current_row->displays_text_p)
+           row  = current_row;
+       }
+
+      xassert (row != NULL);
+      w->window_end_vpos = make_number (vpos + 1);
+      w->window_end_pos = make_number (Z - MATRIX_ROW_END_CHARPOS (row));
+      w->window_end_bytepos = Z_BYTE - MATRIX_ROW_END_BYTEPOS (row);
+      xassert (w->window_end_bytepos >= 0);
+      IF_DEBUG (debug_method_add (w, "C"));
     }
   else
     abort ();
 
+#if 0 /* This leads to problems, for instance when the cursor is
+        at ZV, and the cursor line displays no text.  */
   /* Disable rows below what's displayed in the window.  This makes
      debugging easier.  */
   enable_glyph_matrix_rows (current_matrix,
                            XFASTINT (w->window_end_vpos) + 1,
                            bottom_vpos, 0);
+#endif
   
   IF_DEBUG (debug_end_pos = XFASTINT (w->window_end_pos);
            debug_end_vpos = XFASTINT (w->window_end_vpos));
@@ -11576,6 +11732,8 @@ try_window_id (w)
   w->window_end_valid = Qnil;
   w->desired_matrix->no_scrolling_p = 1;
   return 3;
+
+#undef GIVE_UP
 }
 
 
@@ -11838,12 +11996,20 @@ GLYPH > 1 or omitted means dump glyphs in long form.")
 }
 
 
-DEFUN ("trace-redisplay-toggle", Ftrace_redisplay_toggle,
-       Strace_redisplay_toggle, 0, 0, "",
-  "Toggle tracing of redisplay.")
-     ()
+DEFUN ("trace-redisplay", Ftrace_redisplay, Strace_redisplay, 0, 1, "P",
+  "Toggle tracing of redisplay.\n\
+With ARG, turn tracing on if and only if ARG is positive.")
+  (arg)
+     Lisp_Object arg;
 {
-  trace_redisplay_p = !trace_redisplay_p;
+  if (NILP (arg))
+    trace_redisplay_p = !trace_redisplay_p;
+  else
+    {
+      arg = Fprefix_numeric_value (arg);
+      trace_redisplay_p = XINT (arg) > 0;
+    }
+  
   return Qnil;
 }
 
@@ -13919,9 +14085,7 @@ display_string (string, lisp_string, face_string, face_string_pos,
   struct glyph_row *row = it->glyph_row;
 
   /* Initialize the iterator IT for iteration over STRING beginning
-     with index START.  We assume that IT may be modified here (which
-     means that display_line has to do something when displaying a
-     mini-buffer prompt, which it does).  */
+     with index START.  */
   reseat_to_string (it, string, lisp_string, start,
                    precision, field_width, multibyte);
 
@@ -14187,7 +14351,7 @@ syms_of_xdisp ()
   defsubr (&Sdump_glyph_matrix);
   defsubr (&Sdump_glyph_row);
   defsubr (&Sdump_tool_bar_row);
-  defsubr (&Strace_redisplay_toggle);
+  defsubr (&Strace_redisplay);
   defsubr (&Strace_to_stderr);
 #endif
 #ifdef HAVE_WINDOW_SYSTEM
@@ -14258,6 +14422,8 @@ syms_of_xdisp ()
   staticpro (&Qgrow_only);
   Qinhibit_menubar_update = intern ("inhibit-menubar-update");
   staticpro (&Qinhibit_menubar_update);
+  Qinhibit_eval_during_redisplay = intern ("inhibit-eval-during-redisplay");
+  staticpro (&Qinhibit_eval_during_redisplay);
 
   last_arrow_position = Qnil;
   last_arrow_string = Qnil;
@@ -14486,6 +14652,10 @@ Can be used to update submenus whose contents should vary.");
   DEFVAR_BOOL ("inhibit-menubar-update", &inhibit_menubar_update,
     "Non-nil means don't update menu bars.  Internal use only.");
   inhibit_menubar_update = 0;
+
+  DEFVAR_BOOL ("inhibit-eval-during-redisplay", &inhibit_eval_during_redisplay,
+    "Non-nil means don't eval Lisp during redisplay.");
+  inhibit_eval_during_redisplay = 0;
 }